/**
 * Copyright (c) 2010-2020 Contributors to the openHAB project
 *
 * See the NOTICE file(s) distributed with this work for additional
 * information.
 *
 * This program and the accompanying materials are made available under the
 * terms of the Eclipse Public License 2.0 which is available at
 * http://www.eclipse.org/legal/epl-2.0
 *
 * SPDX-License-Identifier: EPL-2.0
 */

 /* ***********************************************************
 * This file was automatically generated on 2022-01-03.      *
 *                                                           *
 * openHAB Bindings Version 2.0.0                            *
 *                                                           *
 * If you have a bugfix for this file and want to commit it, *
 * please fix the bug in the generator. You can find a link  *
 * to the generators git repository on tinkerforge.com       *
 *************************************************************/

package org.openhab.binding.tinkerforge.internal.device;

import java.net.URI;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.function.Function;
import java.util.function.BiConsumer;
import java.util.function.Consumer;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;
import org.eclipse.smarthome.config.core.Configuration;
import org.eclipse.smarthome.config.core.ConfigDescription;
import org.eclipse.smarthome.config.core.ConfigDescriptionBuilder;
import org.eclipse.smarthome.config.core.ConfigDescriptionParameter.Type;
import org.eclipse.smarthome.config.core.ConfigDescriptionParameterBuilder;
import org.eclipse.smarthome.config.core.ConfigDescriptionParameterGroup;
import org.eclipse.smarthome.config.core.ParameterOption;
import org.eclipse.smarthome.core.types.State;
import org.eclipse.smarthome.core.types.StateOption;
import org.eclipse.smarthome.core.types.Command;
import org.eclipse.smarthome.core.types.CommandDescriptionBuilder;
import org.eclipse.smarthome.core.types.CommandOption;
import org.eclipse.smarthome.core.thing.ChannelUID;
import org.eclipse.smarthome.core.thing.ThingStatus;
import org.eclipse.smarthome.core.thing.ThingTypeUID;
import org.eclipse.smarthome.core.thing.binding.BaseThingHandler;
import org.eclipse.smarthome.core.thing.type.ChannelDefinitionBuilder;
import org.eclipse.smarthome.core.thing.type.ChannelType;
import org.eclipse.smarthome.core.thing.type.ChannelTypeBuilder;
import org.eclipse.smarthome.core.thing.type.ChannelTypeUID;
import org.eclipse.smarthome.core.types.RefreshType;
import org.eclipse.smarthome.core.thing.type.ThingType;
import org.eclipse.smarthome.core.thing.type.ThingTypeBuilder;
import org.eclipse.smarthome.core.types.StateDescriptionFragmentBuilder;
import org.openhab.binding.tinkerforge.internal.TinkerforgeBindingConstants;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.eclipse.smarthome.core.library.types.DecimalType;
import org.eclipse.smarthome.core.library.types.QuantityType;
import org.eclipse.smarthome.core.library.unit.MetricPrefix;
import org.eclipse.smarthome.core.library.unit.SIUnits;
import org.eclipse.smarthome.core.library.unit.SmartHomeUnits;

import com.tinkerforge.BrickletAirQuality;
import com.tinkerforge.IPConnection;
import com.tinkerforge.TinkerforgeException;

import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;

/**
 * Wraps the Java Bindings of the Air Quality Bricklet.
 * @author Erik Fleckstein - Initial contribution
 */
@NonNullByDefault
public class BrickletAirQualityWrapper extends BrickletAirQuality implements CoMCUFlashable, DeviceWrapper {
    public static final DeviceInfo DEVICE_INFO = new DeviceInfo(BrickletAirQuality.DEVICE_DISPLAY_NAME, "brickletairquality", BrickletAirQuality.DEVICE_IDENTIFIER, BrickletAirQualityWrapper.class, BrickletAirQualityActions.class, "2.0.3", true);

    public BrickletAirQualityWrapper(String uid, IPConnection ipcon) {
        super(uid, ipcon);
    }

    private List<ScheduledFuture<?>> manualChannelUpdates = new ArrayList<>();
    private List<ListenerReg<?>> listenerRegs = new ArrayList<>();

    public void cancelManualUpdates() {
        manualChannelUpdates.forEach(f -> f.cancel(true));
    }

    public <T> T reg(T listener, Consumer<T> toRemove) {
        listenerRegs.add(new ListenerReg<T>(listener, toRemove));
        return listener;
    }

    
    private final Logger logger = LoggerFactory.getLogger(BrickletAirQualityWrapper.class);

    @Override
    public void initialize(org.eclipse.smarthome.config.core.Configuration config, Function<String, org.eclipse.smarthome.config.core.Configuration> getChannelConfigFn, BiConsumer<String, org.eclipse.smarthome.core.types.State> updateStateFn, BiConsumer<String, String> triggerChannelFn, ScheduledExecutorService scheduler, BaseThingHandler handler) throws TinkerforgeException {
        this.setResponseExpectedAll(true);
        BrickletAirQualityConfig cfg = (BrickletAirQualityConfig) config.as(BrickletAirQualityConfig.class);
        this.addHumidityListener(this.reg((humidity) -> {if(true) {updateStateFn.accept("BrickletAirQualityHumidity", transformBrickletAirQualityHumidityCallback0(humidity, cfg));}}, this::removeHumidityListener));
		this.addTemperatureListener(this.reg((temperature) -> {if(true) {updateStateFn.accept("BrickletAirQualityTemperature", transformBrickletAirQualityTemperatureCallback0(temperature, cfg));}}, this::removeTemperatureListener));
		this.addAirPressureListener(this.reg((airPressure) -> {if(true) {updateStateFn.accept("BrickletAirQualityAirPressure", transformBrickletAirQualityAirPressureCallback0(airPressure, cfg));}}, this::removeAirPressureListener));
		this.addIAQIndexListener(this.reg((iaqIndex, iaqIndexAccuracy) -> {if(true) {updateStateFn.accept("BrickletAirQualityIAQIndex", transformBrickletAirQualityIAQIndexCallback0(iaqIndex, iaqIndexAccuracy, cfg));}}, this::removeIAQIndexListener));
		this.addIAQIndexListener(this.reg((iaqIndex, iaqIndexAccuracy) -> {if(true) {updateStateFn.accept("BrickletAirQualityIAQIndexAccuracy", transformBrickletAirQualityIAQIndexAccuracyCallback0(iaqIndex, iaqIndexAccuracy, cfg));}}, this::removeIAQIndexListener));
        this.setTemperatureOffset((int)(cfg.temperatureOffset.doubleValue() * 100.0f));
		        this.setIAQIndexCallbackConfiguration(cfg.iaqIndexUpdateInterval, true);
		this.setStatusLEDConfig(cfg.statusLEDConfig);
		{
		BrickletAirQualityHumidityConfig channelCfg = getChannelConfigFn.apply("BrickletAirQualityHumidity").as(BrickletAirQualityHumidityConfig.class);
		this.setHumidityCallbackConfiguration(channelCfg.updateInterval, true, 'x', 0, 0);
		}
		{
		BrickletAirQualityTemperatureConfig channelCfg = getChannelConfigFn.apply("BrickletAirQualityTemperature").as(BrickletAirQualityTemperatureConfig.class);
		this.setTemperatureCallbackConfiguration(channelCfg.updateInterval, true, 'x', 0, 0);
		}
		{
		BrickletAirQualityAirPressureConfig channelCfg = getChannelConfigFn.apply("BrickletAirQualityAirPressure").as(BrickletAirQualityAirPressureConfig.class);
		this.setAirPressureCallbackConfiguration(channelCfg.updateInterval, true, 'x', 0, 0);
		}
    }

    @Override
    public void dispose(org.eclipse.smarthome.config.core.Configuration config) throws TinkerforgeException {
        listenerRegs.forEach(ListenerReg::deregister);

        
        this.setHumidityCallbackConfiguration(0, true, 'x', 0, 0);
		this.setTemperatureCallbackConfiguration(0, true, 'x', 0, 0);
		this.setAirPressureCallbackConfiguration(0, true, 'x', 0, 0);
		this.setIAQIndexCallbackConfiguration(0, true);
    }

    @Override
    public List<String> getEnabledChannels(org.eclipse.smarthome.config.core.Configuration config) throws TinkerforgeException{
        List<String> result = new ArrayList<String>();
        result.add("BrickletAirQualityHumidity");
		result.add("BrickletAirQualityTemperature");
		result.add("BrickletAirQualityAirPressure");
		result.add("BrickletAirQualityIAQIndex");
		result.add("BrickletAirQualityIAQIndexAccuracy");
        return result;
    }

    public static @Nullable ChannelType getChannelType(ChannelTypeUID channelTypeUID) {
        switch(channelTypeUID.getId()) {
            case "BrickletAirQualityHumidity":
                return ChannelTypeBuilder.state(new ChannelTypeUID("tinkerforge", "BrickletAirQualityHumidity"), "Humidity", "Number:Dimensionless").withConfigDescriptionURI(URI.create("channel-type:tinkerforge:BrickletAirQualityHumidity")).withDescription("The measured relative humidity")
.withStateDescription(StateDescriptionFragmentBuilder.create().withPattern("%.2f %unit%").withReadOnly(true).build().toStateDescription()).build();
            case "BrickletAirQualityTemperature":
                return ChannelTypeBuilder.state(new ChannelTypeUID("tinkerforge", "BrickletAirQualityTemperature"), "Temperature", "Number:Temperature").withConfigDescriptionURI(URI.create("channel-type:tinkerforge:BrickletAirQualityTemperature")).withDescription("The measured temperature")
.withStateDescription(StateDescriptionFragmentBuilder.create().withPattern("%.2f %unit%").withReadOnly(true).build().toStateDescription()).build();
            case "BrickletAirQualityAirPressure":
                return ChannelTypeBuilder.state(new ChannelTypeUID("tinkerforge", "BrickletAirQualityAirPressure"), "Air Pressure", "Number:Pressure").withConfigDescriptionURI(URI.create("channel-type:tinkerforge:BrickletAirQualityAirPressure")).withDescription("Messured air pressure")
.withStateDescription(StateDescriptionFragmentBuilder.create().withPattern("%.5f %unit%").withReadOnly(true).build().toStateDescription()).build();
            case "BrickletAirQualityIAQIndex":
                return ChannelTypeBuilder.state(new ChannelTypeUID("tinkerforge", "BrickletAirQualityIAQIndex"), "IAQ Index", "Number:Dimensionless").withConfigDescriptionURI(URI.create("channel-type:tinkerforge:BrickletAirQualityIAQIndex")).withDescription("The IAQ index goes from 0 to 500. The higher the IAQ index, the greater the level of air pollution.")
.withStateDescription(StateDescriptionFragmentBuilder.create().withMinimum(BigDecimal.valueOf(0)).withMaximum(BigDecimal.valueOf(500)).withStep(BigDecimal.valueOf(1)).withPattern("%d").withReadOnly(true).build().toStateDescription()).build();
            case "BrickletAirQualityIAQIndexAccuracy":
                return ChannelTypeBuilder.state(new ChannelTypeUID("tinkerforge", "BrickletAirQualityIAQIndexAccuracy"), "IAQ Index Accuracy", "Number:Dimensionless").withConfigDescriptionURI(URI.create("channel-type:tinkerforge:BrickletAirQualityIAQIndexAccuracy")).withDescription("The Bricklet is building a database of measurements and uses this data to do an automatic background calibration and calculate an accurate IAQ index over time. It will take a few days until the IAQ index has a high reliability.<br/><br/>The IAQ accuracy is reflects the current state of the background calibration process.<br/><br/>    Accuracy 0: The Bricklet was just started and the sensor is stabilizing.<br/>    Accuracy 1: The background history is uncertain. This typically means the gas sensor data was too stable for the calibration algorithm to clearly define its references.<br/>    Accuracy 2: The Bricklet found new calibration data and is currently calibrating.<br/>    Accuracy 3: The Bricklets is calibrated successfully.<br/><br/>The effective duration of the calibration process depends on the stimuli observed by the sensor. An IAQ accuracy going back and fourth between 2 and 3 is expected, the Bricklet is recalibrating itself.<br/><br/>The Bricklet saves the current database of values and calculated coefficients every 12 hours in its internal flash memory. If the Bricklet loses power it will not take as long to receive reliable data again.<br/><br/>Starting with firmware version 2.0.3 the automatic background calibration considers 28 days of data. We recommend that you run the Bricklet for 28 full days before you consider the data as completely reliable. It is possible to reduce the calibration window to 4 days with the API.<br/><br/><br/>Note<br/><br/>The currently released firmware (2.0.4) does not save the calibration every 12 hours. We had to disable this because of a bug in the proprietary load/save mechanism that we can't fix. We will release a new firmware that has this enabled again as soon as this bug is fixed.")
.withStateDescription(StateDescriptionFragmentBuilder.create().withPattern("%d").withReadOnly(true).build().toStateDescription()).build();
default:
                break;
        }

        return null;
    }

    public static ThingType getThingType(ThingTypeUID thingTypeUID) {
             Map<String, String> thingTypeProperties = ThingTypeBuilder.instance(thingTypeUID, "unused").build().getProperties();
             Map<String, String> props = new HashMap<String, String>(thingTypeProperties);
             props.putIfAbsent(TinkerforgeBindingConstants.PROPERTY_MINIMUM_FIRMWARE_VERSION, DEVICE_INFO.minimumFWVersion);
        return ThingTypeBuilder.instance(thingTypeUID, "Tinkerforge Air Quality Bricklet").isListed(false).withSupportedBridgeTypeUIDs(Arrays.asList(TinkerforgeBindingConstants.THING_TYPE_BRICK_DAEMON.toString())).withConfigDescriptionURI(URI.create("thing-type:tinkerforge:" + thingTypeUID.getId())).withDescription("Measures IAQ index, temperature, humidity and air pressure").withChannelDefinitions(Arrays.asList(new ChannelDefinitionBuilder("BrickletAirQualityHumidity", new ChannelTypeUID("tinkerforge", "BrickletAirQualityHumidity")).withLabel("Humidity").build(), new ChannelDefinitionBuilder("BrickletAirQualityTemperature", new ChannelTypeUID("tinkerforge", "BrickletAirQualityTemperature")).withLabel("Temperature").build(), new ChannelDefinitionBuilder("BrickletAirQualityAirPressure", new ChannelTypeUID("tinkerforge", "BrickletAirQualityAirPressure")).withLabel("Air Pressure").build(), new ChannelDefinitionBuilder("BrickletAirQualityIAQIndex", new ChannelTypeUID("tinkerforge", "BrickletAirQualityIAQIndex")).withLabel("IAQ Index").build(), new ChannelDefinitionBuilder("BrickletAirQualityIAQIndexAccuracy", new ChannelTypeUID("tinkerforge", "BrickletAirQualityIAQIndexAccuracy")).withLabel("IAQ Index Accuracy").build())).withProperties(props).build();
    }

    public static @Nullable ConfigDescription getConfigDescription(URI uri) {
        switch(uri.toASCIIString()) {
            case "thing-type:tinkerforge:brickletairquality":
                return ConfigDescriptionBuilder.create(uri).withParameters(Arrays.asList(ConfigDescriptionParameterBuilder.create("temperatureOffset", Type.DECIMAL).withDefault("0").withDescription("If you install this Bricklet into an enclosure and you want to measure the ambient temperature, you may have to decrease the measured temperature by some value to compensate for the error because of the heating inside of the enclosure.<br/><br/>We recommend that you leave the parts in the enclosure running for at least 24 hours such that a temperature equilibrium can be reached. After that you can measure the temperature directly outside of enclosure and set the difference as offset.<br/><br/>This temperature offset is used to calculate the relative humidity and IAQ index measurements. In case the Bricklet is installed in an enclosure, we recommend to measure and set the temperature offset to imporve the accuracy of the measurements.").withLabel("Temperature Offset").withMinimum(BigDecimal.valueOf(-21474836.48)).withMaximum(BigDecimal.valueOf(21474836.47)).build(), ConfigDescriptionParameterBuilder.create("iaqIndexUpdateInterval", Type.INTEGER).withDefault("1000").withDescription("Specifies the update interval for the IAQ index and accuracy in milliseconds. A value of 0 disables automatic updates.").withGroupName("update_intervals").withLabel("IAQ Index Update Interval").withUnit("ms").withMinimum(BigDecimal.valueOf(0)).withMaximum(BigDecimal.valueOf(4294967295L)).build(), ConfigDescriptionParameterBuilder.create("statusLEDConfig", Type.INTEGER).withDefault("3").withDescription("The status LED configuration. By default the LED shows communication traffic between Brick and Bricklet, it flickers once for every 10 received data packets.<br/><br/>You can also turn the LED permanently on/off or show a heartbeat.<br/><br/>If the Bricklet is in bootloader mode, the LED is will show heartbeat by default.").withLabel("Status LED Configuration").withAdvanced(true).withLimitToOptions(true).withOptions(Arrays.asList(new ParameterOption("0", "Off"), new ParameterOption("1", "On"), new ParameterOption("2", "Show Heartbeat"), new ParameterOption("3", "Show Status"))).build())).withParameterGroups(Arrays.asList(new ConfigDescriptionParameterGroup("update_intervals", "null", true, "Update Intervals", "Update Intervals"))).build();
            case "channel-type:tinkerforge:BrickletAirQualityHumidity":
                return ConfigDescriptionBuilder.create(uri).withParameters(Arrays.asList(ConfigDescriptionParameterBuilder.create("updateInterval", Type.INTEGER).withDefault("1000").withDescription("Specifies the update interval in milliseconds. A value of 0 disables automatic updates.").withGroupName("update_intervals").withLabel("Update Interval").withUnit("ms").withMinimum(BigDecimal.valueOf(0)).withMaximum(BigDecimal.valueOf(4294967295L)).build())).withParameterGroups(Arrays.asList()).build();
            case "channel-type:tinkerforge:BrickletAirQualityTemperature":
                return ConfigDescriptionBuilder.create(uri).withParameters(Arrays.asList(ConfigDescriptionParameterBuilder.create("updateInterval", Type.INTEGER).withDefault("1000").withDescription("Specifies the update interval in milliseconds. A value of 0 disables automatic updates.").withGroupName("update_intervals").withLabel("Update Interval").withUnit("ms").withMinimum(BigDecimal.valueOf(0)).withMaximum(BigDecimal.valueOf(4294967295L)).build())).withParameterGroups(Arrays.asList()).build();
            case "channel-type:tinkerforge:BrickletAirQualityAirPressure":
                return ConfigDescriptionBuilder.create(uri).withParameters(Arrays.asList(ConfigDescriptionParameterBuilder.create("updateInterval", Type.INTEGER).withDefault("1000").withDescription("Specifies the update interval in milliseconds. A value of 0 disables automatic updates.").withGroupName("update_intervals").withLabel("Update Interval").withUnit("ms").withMinimum(BigDecimal.valueOf(0)).withMaximum(BigDecimal.valueOf(4294967295L)).build())).withParameterGroups(Arrays.asList()).build();
            case "channel-type:tinkerforge:BrickletAirQualityIAQIndex":
                return ConfigDescriptionBuilder.create(uri).withParameters(Arrays.asList()).withParameterGroups(Arrays.asList()).build();
            case "channel-type:tinkerforge:BrickletAirQualityIAQIndexAccuracy":
                return ConfigDescriptionBuilder.create(uri).withParameters(Arrays.asList()).withParameterGroups(Arrays.asList()).build();
            default:
                break;
        }
        return null;
    }

        @Override
    public void refreshValue(String channel, org.eclipse.smarthome.config.core.Configuration config, org.eclipse.smarthome.config.core.Configuration channelConfig, BiConsumer<String, org.eclipse.smarthome.core.types.State> updateStateFn, BiConsumer<String, String> triggerChannelFn) throws TinkerforgeException {
        BrickletAirQualityConfig cfg = (BrickletAirQualityConfig) config.as(BrickletAirQualityConfig.class);
switch(channel) {
            case "BrickletAirQualityHumidity":
                   {
            int value = this.getHumidity();
            if(true)
                updateStateFn.accept(channel, transformBrickletAirQualityHumidityGetter0(value, cfg));
        }
                   return;
            case "BrickletAirQualityTemperature":
                   {
            int value = this.getTemperature();
            if(true)
                updateStateFn.accept(channel, transformBrickletAirQualityTemperatureGetter0(value, cfg));
        }
                   return;
            case "BrickletAirQualityAirPressure":
                   {
            int value = this.getAirPressure();
            if(true)
                updateStateFn.accept(channel, transformBrickletAirQualityAirPressureGetter0(value, cfg));
        }
                   return;
            case "BrickletAirQualityIAQIndex":
                   {
            BrickletAirQuality.IAQIndex value = this.getIAQIndex();
            if(true)
                updateStateFn.accept(channel, transformBrickletAirQualityIAQIndexGetter0(value, cfg));
        }
                   return;
            case "BrickletAirQualityIAQIndexAccuracy":
                   {
            BrickletAirQuality.IAQIndex value = this.getIAQIndex();
            if(true)
                updateStateFn.accept(channel, transformBrickletAirQualityIAQIndexAccuracyGetter0(value, cfg));
        }
                   return;
default:
                logger.warn("Refresh for unknown channel {}", channel);
                break;
        }
    }
    

        @Override
    public List<SetterRefresh> handleCommand(org.eclipse.smarthome.config.core.Configuration config, org.eclipse.smarthome.config.core.Configuration channelConfig, String channel, Command command) throws TinkerforgeException {
        List<SetterRefresh> result = Collections.emptyList();
        
        switch(channel) {
            default:
                logger.warn("Command for unknown channel {}", channel);
        }
        return result;
    }

        private org.eclipse.smarthome.core.types.State transformBrickletAirQualityHumidityCallback0(int humidity, BrickletAirQualityConfig cfg) {
        return new QuantityType<>(humidity / 100.0, SmartHomeUnits.PERCENT);
    }
	    private org.eclipse.smarthome.core.types.State transformBrickletAirQualityTemperatureCallback0(int temperature, BrickletAirQualityConfig cfg) {
        return new QuantityType<>(temperature / 100.0, SIUnits.CELSIUS);
    }
	    private org.eclipse.smarthome.core.types.State transformBrickletAirQualityAirPressureCallback0(int airPressure, BrickletAirQualityConfig cfg) {
        return new QuantityType<>(airPressure / 100000.0, SmartHomeUnits.BAR);
    }
	    private org.eclipse.smarthome.core.types.State transformBrickletAirQualityIAQIndexCallback0(int iaqIndex, int iaqIndexAccuracy, BrickletAirQualityConfig cfg) {
        return new DecimalType(iaqIndex);
    }
	    private org.eclipse.smarthome.core.types.State transformBrickletAirQualityIAQIndexAccuracyCallback0(int iaqIndex, int iaqIndexAccuracy, BrickletAirQualityConfig cfg) {
        return new DecimalType(iaqIndexAccuracy);
    }
	    private org.eclipse.smarthome.core.types.State transformBrickletAirQualityHumidityGetter0(int value, BrickletAirQualityConfig cfg) {
        return new QuantityType<>(value / 100.0, SmartHomeUnits.PERCENT);
    }
	    private org.eclipse.smarthome.core.types.State transformBrickletAirQualityTemperatureGetter0(int value, BrickletAirQualityConfig cfg) {
        return new QuantityType<>(value / 100.0, SIUnits.CELSIUS);
    }
	    private org.eclipse.smarthome.core.types.State transformBrickletAirQualityAirPressureGetter0(int value, BrickletAirQualityConfig cfg) {
        return new QuantityType<>(value / 100000.0, SmartHomeUnits.BAR);
    }
	    private org.eclipse.smarthome.core.types.State transformBrickletAirQualityIAQIndexGetter0(BrickletAirQuality.IAQIndex value, BrickletAirQualityConfig cfg) {
        return new DecimalType(value.iaqIndex);
    }
	    private org.eclipse.smarthome.core.types.State transformBrickletAirQualityIAQIndexAccuracyGetter0(BrickletAirQuality.IAQIndex value, BrickletAirQualityConfig cfg) {
        return new DecimalType(value.iaqIndexAccuracy);
    }

}