package io.openems.edge.evse.chargepoint.hardybarth.common;

import static io.openems.common.channel.Level.WARNING;
import static io.openems.common.channel.Unit.AMPERE;
import static io.openems.common.channel.Unit.CUMULATED_WATT_HOURS;
import static io.openems.common.types.OpenemsType.BOOLEAN;
import static io.openems.common.types.OpenemsType.DOUBLE;
import static io.openems.common.types.OpenemsType.INTEGER;
import static io.openems.common.types.OpenemsType.LONG;
import static io.openems.common.types.OpenemsType.STRING;
import static io.openems.edge.common.channel.ChannelUtils.setValue;

import java.util.function.Function;

import io.openems.edge.common.channel.BooleanDoc;
import io.openems.edge.common.channel.Doc;
import io.openems.edge.common.channel.StringReadChannel;
import io.openems.edge.common.component.OpenemsComponent;
import io.openems.edge.meter.api.ElectricityMeter;

public interface HardyBarth extends OpenemsComponent, ElectricityMeter {

	public enum ChannelId implements io.openems.edge.common.channel.ChannelId, PathProvider {
		// EVSE
		RAW_EVSE_GRID_CURRENT_LIMIT(Doc.of(INTEGER)//
				.unit(AMPERE), //
				"secc", "port0", "ci", "evse", "basic", "grid_current_limit", "actual"), //
		RAW_PHASE_COUNT(Doc.of(INTEGER), //
				"secc", "port0", "ci", "evse", "basic", "phase_count"), //
		RAW_PHYSICAL_CURRENT_LIMIT(Doc.of(STRING),
				"secc", "port0", "ci", "evse", "basic", "physical_current_limit"), //

		// CHARGE
		RAW_CHARGE_STATUS_PLUG(Doc.of(STRING), //
				"secc", "port0", "ci", "charge", "plug", "status"), //
		RAW_CHARGE_STATUS_CONTACTOR(Doc.of(STRING), //
				"secc", "port0", "ci", "charge", "contactor", "status"), //
		RAW_CHARGE_STATUS_PWM(Doc.of(STRING), //
				"secc", "port0", "ci", "charge", "pwm", "status"), //

		/**
		 * States of the Hardy Barth.
		 *
		 * <ul>
		 * <li>A = Free (no EV connected)
		 * <li>B = EV connected, no charging (pause state)
		 * <li>C = Charging
		 * <li>D = Charging (With ventilation)
		 * <li>E = Deactivated Socket
		 * <li>F = Failure
		 * </ul>
		 */
		RAW_CHARGE_STATUS_CHARGEPOINT(Doc.of(STRING), //
				"secc", "port0", "ci", "charge", "cp", "status"), //

		// SALIA
		RAW_SALIA_CHARGE_MODE(Doc.of(STRING), //
				"secc", "port0", "salia", "chargemode"), //
		RAW_SALIA_CHARGE_PAUSE(Doc.of(INTEGER), //
				"secc", "port0", "salia", "pausecharging"), //
		RAW_SALIA_CHANGE_METER(Doc.of(STRING), //
				"secc", "port0", "salia", "changemeter"), //
		RAW_SALIA_AUTHMODE(Doc.of(STRING), //
				"secc", "port0", "salia", "authmode"), //
		RAW_SALIA_FIRMWARESTATE(Doc.of(STRING), //
				"secc", "port0", "salia", "firmwarestate"), //
		RAW_SALIA_FIRMWAREPROGRESS(Doc.of(STRING), //
				"secc", "port0", "salia", "firmwareprogress"), //
		RAW_SALIA_PUBLISH(Doc.of(STRING), //
				"secc", "port0", "salia", "publish"), //
		RAW_SALIA_SOCKET_MAX_AMP(Doc.of(STRING),
				"secc", "port0", "salia", "socketmaxamp"), //
		RAW_SALIA_INTCTRL_LIMIT(Doc.of(STRING),
				"secc", "port0", "salia", "intctrl_limit"), //

		// SESSION
		RAW_SESSION_STATUS_AUTHORIZATION(Doc.of(STRING), //
				"secc", "port0", "session", "authorization_status"), //
		RAW_SESSION_SLAC_STARTED(Doc.of(STRING), //
				"secc", "port0", "session", "slac_started"), //
		RAW_SESSION_AUTHORIZATION_METHOD(Doc.of(STRING), //
				"secc", "port0", "session", "authorization_method"), //

		// CONTACTOR
		RAW_CONTACTOR_HLC_TARGET(Doc.of(STRING), //
				"secc", "port0", "contactor", "state", "hlc_target"), //
		RAW_CONTACTOR_ACTUAL(Doc.of(STRING), //
				"secc", "port0", "contactor", "state", "actual"), //
		RAW_CONTACTOR_TARGET(Doc.of(STRING), //
				"secc", "port0", "contactor", "state", "target"), //
		RAW_CONTACTOR_ERROR(Doc.of(STRING), //
				"secc", "port0", "contactor", "error"), //

		// METERING - METER
		RAW_METER_SERIALNUMBER(Doc.of(STRING), //
				"secc", "port0", "metering", "meter", "serialnumber"), //
		RAW_METER_TYPE(Doc.of(STRING), //
				"secc", "port0", "metering", "meter", "type"), //
		METER_NOT_AVAILABLE(Doc.of(WARNING)//
				.translationKey(HardyBarth.class, "noMeterAvailable")), //
		TARGET_WRITE_FAILED(Doc.of(WARNING)//
				.translationKey(HardyBarth.class, "targetWriteFailed")), //
		RAW_METER_AVAILABLE(new BooleanDoc()//
				.onChannelSetNextValue((hb, value) -> {
					var notAvailable = value.get() == null ? null : !value.get();
					setValue(hb, HardyBarth.ChannelId.METER_NOT_AVAILABLE, notAvailable);
				}), //
				"secc", "port0", "metering", "meter", "available"), //

		// METERING - ENERGY
		RAW_ACTIVE_ENERGY_TOTAL(Doc.of(DOUBLE)//
				.unit(CUMULATED_WATT_HOURS), //
				"secc", "port0", "metering", "energy", "active_total", "actual"), //
		RAW_ACTIVE_ENERGY_EXPORT(Doc.of(DOUBLE)//
				.unit(CUMULATED_WATT_HOURS), //
				"secc", "port0", "metering", "energy", "active_export", "actual"), //

		// EMERGENCY SHUTDOWN
		RAW_EMERGENCY_SHUTDOWN(Doc.of(STRING), //
				"secc", "port0", "emergency_shutdown"), //

		// RCD
		RAW_RCD_AVAILABLE(Doc.of(BOOLEAN), //
				"secc", "port0", "rcd", "recloser", "available"), //

		// PLUG LOCK
		RAW_PLUG_LOCK_STATE_ACTUAL(Doc.of(STRING), //
				"secc", "port0", "plug_lock", "state", "actual"), //
		RAW_PLUG_LOCK_STATE_TARGET(Doc.of(STRING), //
				"secc", "port0", "plug_lock", "state", "target"), //
		RAW_PLUG_LOCK_ERROR(Doc.of(STRING), //
				"secc", "port0", "plug_lock", "error"), //

		// CHARGE POINT
		RAW_CP_STATE(Doc.of(STRING), //
				"secc", "port0", "cp", "state"), //

		// DIODE PRESENT
		RAW_DIODE_PRESENT(Doc.of(STRING), //
				"secc", "port0", "diode_present"), //

		// CABLE CURRENT LIMIT
		RAW_CABLE_CURRENT_LIMIT(Doc.of(STRING), //
				"secc", "port0", "cable_current_limit"), //

		RAW_MAX_AMP(Doc.of(STRING), //
				"secc", "port0", "max_amp"), //

		// VENTILATION
		RAW_VENTILATION_STATE_ACTUAL(Doc.of(STRING), //
				"secc", "port0", "ventilation", "state", "actual"), //
		RAW_VENTILATION_STATE_TARGET(Doc.of(STRING), //
				"secc", "port0", "ventilation", "state", "target"), //
		RAW_VENTILATION_AVAILABLE(Doc.of(BOOLEAN), //
				"secc", "port0", "ventilation", "available"), //

		// EV - PRESENT
		RAW_EV_PRESENT(Doc.of(STRING), //
				"secc", "port0", "ev_present"), //

		// CHARGING
		RAW_CHARGING(Doc.of(STRING), //
				"secc", "port0", "charging"), //

		// RFID
		RAW_RFID_AUTHORIZEREQ(Doc.of(STRING), //
				"secc", "port0", "rfid", "authorizereq"), //
		RAW_RFID_AVAILABLE(Doc.of(BOOLEAN), //
				"secc", "port0", "rfid", "available"), //

		// GRID CURRENT LIMIT
		RAW_GRID_CURRENT_LIMIT(Doc.of(STRING), //
				"secc", "port0", "grid_current_limit"), //

		// SLAC ERROR
		RAW_SLAC_ERROR(Doc.of(STRING), //
				"secc", "port0", "slac_error"), //

		// DEVICE
		RAW_DEVICE_PRODUCT(Doc.of(STRING), //
				"device", "product"), //
		RAW_DEVICE_MODELNAME(Doc.of(STRING), //
				"device", "modelname"), //
		RAW_DEVICE_HARDWARE_VERSION(Doc.of(STRING), //
				"device", "hardware_version"), //
		RAW_DEVICE_SOFTWARE_VERSION(Doc.of(STRING), //
				"device", "software_version"), //
		RAW_DEVICE_VCS_VERSION(Doc.of(STRING), //
				"device", "vcs_version"), //
		RAW_DEVICE_HOSTNAME(Doc.of(STRING), //
				"device", "hostname"), //
		RAW_DEVICE_MAC_ADDRESS(Doc.of(STRING), //
				"device", "mac_address"), //
		RAW_DEVICE_SERIAL(Doc.of(LONG), //
				"device", "serial"), //
		RAW_DEVICE_UUID(Doc.of(STRING), //
				"device", "uuid"), //
		;

		private final Doc doc;
		private final Path path;

		private ChannelId(Doc doc, String... jsonPaths) {
			this(doc, value -> value, jsonPaths);
		}

		private ChannelId(Doc doc, Function<Object, Object> converter, String... jsonPaths) {
			this.doc = doc;
			this.path = new Path(converter, jsonPaths);
		}

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

		@Override
		public Path getPath() {
			return this.path;
		}
	}

	/**
	 * Gets the Channel for {@link ChannelId#RAW_DEVICE_SOFTWARE_VERSION}.
	 *
	 * @return the Channel
	 */
	public default StringReadChannel getSoftwareVersionChannel() {
		return this.channel(ChannelId.RAW_DEVICE_SOFTWARE_VERSION);
	}

	public default String getSoftwareVersion() {
		return this.getSoftwareVersionChannel().value().get();
	}

	public interface PathProvider {

		/**
		 * Gets the {@link Path}.
		 * 
		 * @return Path
		 */
		public Path getPath();
	}

	public static record Path(Function<Object, Object> converter, String... jsonPaths) {
		public Path(String... jsonPaths) {
			this(value -> value, jsonPaths);
		}
	}

	/**
	 * Defines if the instance is read only.
	 *
	 * @return true if the instance is read-only
	 */
	public boolean isReadOnly();
}
