/**
 * 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.BrickletBarometerV2;
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 Barometer Bricklet 2.0.
 * @author Erik Fleckstein - Initial contribution
 */
@NonNullByDefault
public class BrickletBarometerV2Wrapper extends BrickletBarometerV2 implements CoMCUFlashable, DeviceWrapper {
    public static final DeviceInfo DEVICE_INFO = new DeviceInfo(BrickletBarometerV2.DEVICE_DISPLAY_NAME, "brickletbarometerv2", BrickletBarometerV2.DEVICE_IDENTIFIER, BrickletBarometerV2Wrapper.class, BrickletBarometerV2Actions.class, "2.0.0", true);

    public BrickletBarometerV2Wrapper(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(BrickletBarometerV2Wrapper.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);
        BrickletBarometerV2Config cfg = (BrickletBarometerV2Config) config.as(BrickletBarometerV2Config.class);
        this.addAirPressureListener(this.reg((airPressure) -> {if(true) {updateStateFn.accept("BrickletBarometerV2AirPressure", transformBrickletBarometerV2AirPressureCallback0(airPressure, cfg));}}, this::removeAirPressureListener));
		this.addAltitudeListener(this.reg((altitude) -> {if(true) {updateStateFn.accept("BrickletBarometerV2Altitude", transformBrickletBarometerV2AltitudeCallback0(altitude, cfg));}}, this::removeAltitudeListener));
		this.addTemperatureListener(this.reg((temperature) -> {if(true) {updateStateFn.accept("BrickletBarometerV2Temperature", transformBrickletBarometerV2TemperatureCallback0(temperature, cfg));}}, this::removeTemperatureListener));
        this.setReferenceAirPressure(cfg.referenceAirPressure.multiply(new BigDecimal(1000)).intValue());
		this.setMovingAverageConfiguration(cfg.airPressureMovingAverageLength, cfg.temperatureMovingAverageLength);
		this.setSensorConfiguration(cfg.dataRate, cfg.airPressureLowPassFilter);
		this.setStatusLEDConfig(cfg.statusLEDConfig);
		{
		BrickletBarometerV2AirPressureConfig channelCfg = getChannelConfigFn.apply("BrickletBarometerV2AirPressure").as(BrickletBarometerV2AirPressureConfig.class);
		this.setAirPressureCallbackConfiguration(channelCfg.updateInterval, true, 'x', 0, 0);
		}
		{
		BrickletBarometerV2AltitudeConfig channelCfg = getChannelConfigFn.apply("BrickletBarometerV2Altitude").as(BrickletBarometerV2AltitudeConfig.class);
		this.setAltitudeCallbackConfiguration(channelCfg.updateInterval, true, 'x', 0, 0);
		}
		{
		BrickletBarometerV2TemperatureConfig channelCfg = getChannelConfigFn.apply("BrickletBarometerV2Temperature").as(BrickletBarometerV2TemperatureConfig.class);
		this.setTemperatureCallbackConfiguration(channelCfg.updateInterval, true, 'x', 0, 0);
		}
    }

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

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

    @Override
    public List<String> getEnabledChannels(org.eclipse.smarthome.config.core.Configuration config) throws TinkerforgeException{
        List<String> result = new ArrayList<String>();
        result.add("BrickletBarometerV2AirPressure");
		result.add("BrickletBarometerV2Altitude");
		result.add("BrickletBarometerV2Temperature");
        return result;
    }

    public static @Nullable ChannelType getChannelType(ChannelTypeUID channelTypeUID) {
        switch(channelTypeUID.getId()) {
            case "BrickletBarometerV2AirPressure":
                return ChannelTypeBuilder.state(new ChannelTypeUID("tinkerforge", "BrickletBarometerV2AirPressure"), "Air Pressure", "Number:Pressure").withConfigDescriptionURI(URI.create("channel-type:tinkerforge:BrickletBarometerV2AirPressure")).withDescription("The measured air pressure")
.withStateDescription(StateDescriptionFragmentBuilder.create().withMinimum(BigDecimal.valueOf(0.26)).withMaximum(BigDecimal.valueOf(1.26)).withStep(BigDecimal.valueOf(1e-06)).withPattern("%.6f %unit%").withReadOnly(true).build().toStateDescription()).build();
            case "BrickletBarometerV2Altitude":
                return ChannelTypeBuilder.state(new ChannelTypeUID("tinkerforge", "BrickletBarometerV2Altitude"), "Altitude", "Number:Length").withConfigDescriptionURI(URI.create("channel-type:tinkerforge:BrickletBarometerV2Altitude")).withDescription("The relative altitude. The value is calculated based on the difference between the current air pressure and the reference air pressure.")
.withStateDescription(StateDescriptionFragmentBuilder.create().withPattern("%.3f %unit%").withReadOnly(true).build().toStateDescription()).build();
            case "BrickletBarometerV2Temperature":
                return ChannelTypeBuilder.state(new ChannelTypeUID("tinkerforge", "BrickletBarometerV2Temperature"), "Temperature", "Number:Temperature").withConfigDescriptionURI(URI.create("channel-type:tinkerforge:BrickletBarometerV2Temperature")).withDescription("The measured temperature")
.withStateDescription(StateDescriptionFragmentBuilder.create().withMinimum(BigDecimal.valueOf(-40)).withMaximum(BigDecimal.valueOf(85)).withStep(BigDecimal.valueOf(0.01)).withPattern("%.2f %unit%").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 Barometer Bricklet 2.0").isListed(false).withSupportedBridgeTypeUIDs(Arrays.asList(TinkerforgeBindingConstants.THING_TYPE_BRICK_DAEMON.toString())).withConfigDescriptionURI(URI.create("thing-type:tinkerforge:" + thingTypeUID.getId())).withDescription("Measures air pressure and altitude changes").withChannelDefinitions(Arrays.asList(new ChannelDefinitionBuilder("BrickletBarometerV2AirPressure", new ChannelTypeUID("tinkerforge", "BrickletBarometerV2AirPressure")).withLabel("Air Pressure").build(), new ChannelDefinitionBuilder("BrickletBarometerV2Altitude", new ChannelTypeUID("tinkerforge", "BrickletBarometerV2Altitude")).withLabel("Altitude").build(), new ChannelDefinitionBuilder("BrickletBarometerV2Temperature", new ChannelTypeUID("tinkerforge", "BrickletBarometerV2Temperature")).withLabel("Temperature").build())).withProperties(props).build();
    }

    public static @Nullable ConfigDescription getConfigDescription(URI uri) {
        switch(uri.toASCIIString()) {
            case "thing-type:tinkerforge:brickletbarometerv2":
                return ConfigDescriptionBuilder.create(uri).withParameters(Arrays.asList(ConfigDescriptionParameterBuilder.create("airPressureMovingAverageLength", Type.INTEGER).withDefault("100").withDescription("The length of a moving averaging for the air pressure.").withGroupName("average").withLabel("Air Pressure Moving Average Length").withMinimum(BigDecimal.valueOf(1)).withMaximum(BigDecimal.valueOf(1000)).build(), ConfigDescriptionParameterBuilder.create("temperatureMovingAverageLength", Type.INTEGER).withDefault("100").withDescription("The length of a moving averaging for the temperature.").withGroupName("average").withLabel("Temperature Moving Average Length").withMinimum(BigDecimal.valueOf(1)).withMaximum(BigDecimal.valueOf(1000)).build(), ConfigDescriptionParameterBuilder.create("referenceAirPressure", Type.DECIMAL).withDefault("1013.25").withDescription("The reference air pressure in hPa for the altitude calculation. Setting the reference to the current air pressure results in a calculated altitude of 0 m.").withLabel("Reference Air Pressure").withMinimum(BigDecimal.valueOf(260)).withMaximum(BigDecimal.valueOf(1260)).build(), ConfigDescriptionParameterBuilder.create("dataRate", Type.INTEGER).withDefault("4").withDescription("Configures the data rate. A higher data rate will result in a less precise temperature because of self-heating of the sensor. If the accuracy of the temperature reading is important to you, we would recommend the 1Hz data rate.").withLabel("Data Rate").withAdvanced(true).withLimitToOptions(true).withOptions(Arrays.asList(new ParameterOption("0", "Off"), new ParameterOption("1", "1Hz"), new ParameterOption("2", "10Hz"), new ParameterOption("3", "25Hz"), new ParameterOption("4", "50Hz"), new ParameterOption("5", "75Hz"))).build(), ConfigDescriptionParameterBuilder.create("airPressureLowPassFilter", Type.INTEGER).withDefault("1").withDescription("Configures the air pressure low pass filter. The low pass filter cut-off frequency (if enabled) can be set to 1/9th or 1/20th of the configure data rate to decrease the noise on the air pressure data.").withLabel("Air Pressure Low Pass Filter").withAdvanced(true).withLimitToOptions(true).withOptions(Arrays.asList(new ParameterOption("0", "Off"), new ParameterOption("1", "1/9th"), new ParameterOption("2", "1/20th"))).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"), new ConfigDescriptionParameterGroup("average", "null", true, "Averaging", "The length of a moving averaging: https://en.wikipedia.org/wiki/Moving_average for the air pressure and temperature measurements.<br/><br/>Setting the length to 1 will turn the averaging off. With less averaging, there is more noise on the data.<br/><br/>If you want to do long term measurements the longest moving average will give the cleanest results."))).build();
            case "channel-type:tinkerforge:BrickletBarometerV2AirPressure":
                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:BrickletBarometerV2Altitude":
                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:BrickletBarometerV2Temperature":
                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();
            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 {
        BrickletBarometerV2Config cfg = (BrickletBarometerV2Config) config.as(BrickletBarometerV2Config.class);
switch(channel) {
            case "BrickletBarometerV2AirPressure":
                   {
            int value = this.getAirPressure();
            if(true)
                updateStateFn.accept(channel, transformBrickletBarometerV2AirPressureGetter0(value, cfg));
        }
                   return;
            case "BrickletBarometerV2Altitude":
                   {
            int value = this.getAltitude();
            if(true)
                updateStateFn.accept(channel, transformBrickletBarometerV2AltitudeGetter0(value, cfg));
        }
                   return;
            case "BrickletBarometerV2Temperature":
                   {
            int value = this.getTemperature();
            if(true)
                updateStateFn.accept(channel, transformBrickletBarometerV2TemperatureGetter0(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 transformBrickletBarometerV2AirPressureCallback0(int airPressure, BrickletBarometerV2Config cfg) {
        return new QuantityType<>(airPressure / 1000000.0, SmartHomeUnits.BAR);
    }
	    private org.eclipse.smarthome.core.types.State transformBrickletBarometerV2AltitudeCallback0(int altitude, BrickletBarometerV2Config cfg) {
        return new QuantityType<>(altitude / 1000.0, SIUnits.METRE);
    }
	    private org.eclipse.smarthome.core.types.State transformBrickletBarometerV2TemperatureCallback0(int temperature, BrickletBarometerV2Config cfg) {
        return new QuantityType<>(temperature / 100.0, SIUnits.CELSIUS);
    }
	    private org.eclipse.smarthome.core.types.State transformBrickletBarometerV2AirPressureGetter0(int value, BrickletBarometerV2Config cfg) {
        return new QuantityType<>(value / 1000000.0, SmartHomeUnits.BAR);
    }
	    private org.eclipse.smarthome.core.types.State transformBrickletBarometerV2AltitudeGetter0(int value, BrickletBarometerV2Config cfg) {
        return new QuantityType<>(value / 1000.0, SIUnits.METRE);
    }
	    private org.eclipse.smarthome.core.types.State transformBrickletBarometerV2TemperatureGetter0(int value, BrickletBarometerV2Config cfg) {
        return new QuantityType<>(value / 100.0, SIUnits.CELSIUS);
    }

}