package io.openems.edge.batteryinverter.kaco.blueplanetgridsave;

import io.openems.common.channel.Level;
import io.openems.common.exceptions.OpenemsError.OpenemsNamedException;
import io.openems.common.exceptions.OpenemsException;
import io.openems.edge.batteryinverter.api.BatteryInverterErrorAcknowledge;
import io.openems.edge.batteryinverter.api.ManagedSymmetricBatteryInverter;
import io.openems.edge.batteryinverter.api.SymmetricBatteryInverter;
import io.openems.edge.batteryinverter.kaco.blueplanetgridsave.KacoSunSpecModel.S64201.S64201CurrentState;
import io.openems.edge.batteryinverter.kaco.blueplanetgridsave.KacoSunSpecModel.S64201.S64201RequestedState;
import io.openems.edge.batteryinverter.kaco.blueplanetgridsave.statemachine.StateMachine.State;
import io.openems.edge.bridge.modbus.api.ModbusComponent;
import io.openems.edge.bridge.modbus.sunspec.SunSpecPoint;
import io.openems.edge.common.channel.Channel;
import io.openems.edge.common.channel.Doc;
import io.openems.edge.common.channel.StateChannel;
import io.openems.edge.common.channel.WriteChannel;
import io.openems.edge.common.channel.value.Value;
import io.openems.edge.common.component.OpenemsComponent;
import io.openems.edge.common.startstop.StartStop;
import io.openems.edge.common.startstop.StartStoppable;

public interface BatteryInverterKacoBlueplanetGridsave extends ManagedSymmetricBatteryInverter,
		SymmetricBatteryInverter, ModbusComponent, OpenemsComponent, StartStoppable, BatteryInverterErrorAcknowledge {

	/**
	 * Sets the KACO watchdog timeout to 60 seconds.
	 */
	public static final int WATCHDOG_TIMEOUT_SECONDS = 60;
	/**
	 * The watchdog gets triggered every WATCHDOG_TRIGGER_CYCLES seconds. This must
	 * be less than WATCHDOG_TIMEOUT_SECONDS.
	 */
	public static final int WATCHDOG_TRIGGER_SECONDS = 10;

	public enum ChannelId implements io.openems.edge.common.channel.ChannelId {

		/*
		 * Whenever one of these states would be Level.FAULT, the EssGeneric will stop
		 * the battery and the inverter. If this is necessary, it must be specifically
		 * mentioned and the state should have a proper description of the fault.
		 */

		STATE_MACHINE(Doc.of(State.values())//
				.text("Current State of State-Machine")), //
		RUN_FAILED(Doc.of(Level.WARNING)//
				.text("Running the Logic failed")), //
		MAX_START_TIMEOUT(Doc.of(Level.WARNING)//
				.text("Max start time is exceeded")), //
		MAX_STOP_TIMEOUT(Doc.of(Level.WARNING)//
				.text("Max stop time is exceeded")), //
		INVERTER_RESTART_STOPPING(Doc.of(Level.WARNING)//
				.text("Inverter Restart (Stopping). The inverter detected a non critical state e.g. NA-protection signal. This required a restart of the inverter.")), //
		INVERTER_RESTART_STARTING(Doc.of(Level.WARNING)//
				.text("Inverter Restart (Starting). The inverter detected a non critical state e.g. NA-protection signal. This required a restart of the inverter.")), //
		;

		private final Doc doc;

		private ChannelId(Doc doc) {
			this.doc = doc;
		}

		@Override
		public Doc doc() {
			return this.doc;
		}
	}

	/**
	 * Gets the target Start/Stop mode from config or StartStop-Channel.
	 *
	 * @return {@link StartStop}
	 */
	public StartStop getStartStopTarget();

	/**
	 * Get the Channel for the given Point or throw an error if it is not available.
	 *
	 * @param <T>   the Channel type
	 * @param point the SunSpec Point
	 * @return the optional Channel
	 * @throws OpenemsException if Channel is not available
	 */
	public <T extends Channel<?>> T getSunSpecChannelOrError(SunSpecPoint point) throws OpenemsException;

	/**
	 * Gets the Current State.
	 *
	 * @return the {@link S64201CurrentState}
	 */
	public S64201CurrentState getCurrentState();

	/**
	 * Gets the Channel for {@link ChannelId#MAX_START_TIMEOUT}.
	 *
	 * @return the Channel
	 */
	public default StateChannel getMaxStartTimeoutChannel() {
		return this.channel(ChannelId.MAX_START_TIMEOUT);
	}

	/**
	 * Gets the {@link StateChannel} for {@link ChannelId#MAX_START_TIMEOUT}.
	 *
	 * @return the Channel {@link Value}
	 */
	public default Value<Boolean> getMaxStartTimeout() {
		return this.getMaxStartTimeoutChannel().value();
	}

	/**
	 * Internal method to set the 'nextValue' on {@link ChannelId#MAX_START_TIMEOUT}
	 * Channel.
	 *
	 * @param value the next value
	 */
	public default void _setMaxStartTimeout(boolean value) {
		this.getMaxStartTimeoutChannel().setNextValue(value);
	}

	/**
	 * Gets the Channel for {@link ChannelId#MAX_STOP_TIMEOUT}.
	 *
	 * @return the Channel
	 */
	public default StateChannel getMaxStopTimeoutChannel() {
		return this.channel(ChannelId.MAX_STOP_TIMEOUT);
	}

	/**
	 * Gets the {@link StateChannel} for {@link ChannelId#MAX_STOP_TIMEOUT}.
	 *
	 * @return the Channel {@link Value}
	 */
	public default Value<Boolean> getMaxStopTimeout() {
		return this.getMaxStopTimeoutChannel().value();
	}

	/**
	 * Internal method to set the 'nextValue' on {@link ChannelId#MAX_STOP_TIMEOUT}
	 * Channel.
	 *
	 * @param value the next value
	 */
	public default void _setMaxStopTimeout(boolean value) {
		this.getMaxStopTimeoutChannel().setNextValue(value);
	}

	/**
	 * Gets the Channel for KacoSunSpecModel.S64201.REQUESTED_STATE.
	 *
	 * @return the Channel
	 * @throws OpenemsException on error
	 */
	public default WriteChannel<S64201RequestedState> getRequestedStateChannel() throws OpenemsException {
		return this.getSunSpecChannelOrError(KacoSunSpecModel.S64201.REQUESTED_STATE);
	}

	/**
	 * Writes the value to the KacoSunSpecModel.S64201.REQUESTED_STATE Register.
	 *
	 * @param value the next value
	 * @throws OpenemsNamedException on error
	 */
	public default void setRequestedState(S64201RequestedState value) throws OpenemsNamedException {
		this.getRequestedStateChannel().setNextWriteValue(value);
	}

	/**
	 * Gets the Channel for ChannelId.RUN_FAILED.
	 *
	 * @return the Channel
	 */
	public default Channel<Boolean> getRunFailedChannel() {
		return this.channel(ChannelId.RUN_FAILED);
	}

	/**
	 * Writes the value to the ChannelId.RUN_FAILED.
	 *
	 * @param value the next value
	 */
	public default void _setRunFailed(boolean value) {
		this.getRunFailedChannel().setNextValue(value);
	}

}
