/**
 * 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 org.eclipse.smarthome.core.library.types.OnOffType;
import org.eclipse.smarthome.core.library.types.OpenClosedType;
import org.eclipse.smarthome.core.library.types.StringType;

import com.tinkerforge.BrickletIO4V2;
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 IO-4 Bricklet 2.0.
 * @author Erik Fleckstein - Initial contribution
 */
@NonNullByDefault
public class BrickletIO4V2Wrapper extends BrickletIO4V2 implements CoMCUFlashable, DeviceWrapper {
    public static final DeviceInfo DEVICE_INFO = new DeviceInfo(BrickletIO4V2.DEVICE_DISPLAY_NAME, "brickletio4v2", BrickletIO4V2.DEVICE_IDENTIFIER, BrickletIO4V2Wrapper.class, BrickletIO4V2Actions.class, "2.0.0", true);

    public BrickletIO4V2Wrapper(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(BrickletIO4V2Wrapper.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);
        BrickletIO4V2Config cfg = (BrickletIO4V2Config) config.as(BrickletIO4V2Config.class);
        if(cfg.pinConfiguration0 > 1) {
this.addInputValueListener(this.reg((channel, changed, value) -> {if(channel == 0) {updateStateFn.accept("BrickletIO4V2Input0", transformBrickletIO4V2Input0Callback0(channel, changed, value, cfg));}}, this::removeInputValueListener));}
		if(cfg.pinConfiguration1 > 1) {
this.addInputValueListener(this.reg((channel, changed, value) -> {if(channel == 1) {updateStateFn.accept("BrickletIO4V2Input1", transformBrickletIO4V2Input1Callback0(channel, changed, value, cfg));}}, this::removeInputValueListener));}
		if(cfg.pinConfiguration2 > 1) {
this.addInputValueListener(this.reg((channel, changed, value) -> {if(channel == 2) {updateStateFn.accept("BrickletIO4V2Input2", transformBrickletIO4V2Input2Callback0(channel, changed, value, cfg));}}, this::removeInputValueListener));}
		if(cfg.pinConfiguration3 > 1) {
this.addInputValueListener(this.reg((channel, changed, value) -> {if(channel == 3) {updateStateFn.accept("BrickletIO4V2Input3", transformBrickletIO4V2Input3Callback0(channel, changed, value, cfg));}}, this::removeInputValueListener));}
		if(cfg.pinConfiguration0 <= 1) {
this.addMonoflopDoneListener(this.reg((channel, value) -> {if(channel == 0) {updateStateFn.accept("BrickletIO4V2Output0", transformBrickletIO4V2Output0Callback0(channel, value, cfg));}}, this::removeMonoflopDoneListener));}
		if(cfg.pinConfiguration1 <= 1) {
this.addMonoflopDoneListener(this.reg((channel, value) -> {if(channel == 1) {updateStateFn.accept("BrickletIO4V2Output1", transformBrickletIO4V2Output1Callback0(channel, value, cfg));}}, this::removeMonoflopDoneListener));}
		if(cfg.pinConfiguration2 <= 1) {
this.addMonoflopDoneListener(this.reg((channel, value) -> {if(channel == 2) {updateStateFn.accept("BrickletIO4V2Output2", transformBrickletIO4V2Output2Callback0(channel, value, cfg));}}, this::removeMonoflopDoneListener));}
		if(cfg.pinConfiguration3 <= 1) {
this.addMonoflopDoneListener(this.reg((channel, value) -> {if(channel == 3) {updateStateFn.accept("BrickletIO4V2Output3", transformBrickletIO4V2Output3Callback0(channel, value, cfg));}}, this::removeMonoflopDoneListener));}
        
		this.setStatusLEDConfig(cfg.statusLEDConfig);
		if (cfg.pinConfiguration0 > 1) {
		BrickletIO4V2InputValueConfig channelCfg = getChannelConfigFn.apply("BrickletIO4V2Input0").as(BrickletIO4V2InputValueConfig.class);
		this.setConfiguration(0, 'i', cfg.pinConfiguration0 % 2 == 1);
		            this.setInputValueCallbackConfiguration(0, channelCfg.updateInterval, true);
		}
		if (cfg.pinConfiguration1 > 1) {
		BrickletIO4V2InputValueConfig channelCfg = getChannelConfigFn.apply("BrickletIO4V2Input1").as(BrickletIO4V2InputValueConfig.class);
		this.setConfiguration(1, 'i', cfg.pinConfiguration1 % 2 == 1);
		            this.setInputValueCallbackConfiguration(1, channelCfg.updateInterval, true);
		}
		if (cfg.pinConfiguration2 > 1) {
		BrickletIO4V2InputValueConfig channelCfg = getChannelConfigFn.apply("BrickletIO4V2Input2").as(BrickletIO4V2InputValueConfig.class);
		this.setConfiguration(2, 'i', cfg.pinConfiguration2 % 2 == 1);
		            this.setInputValueCallbackConfiguration(2, channelCfg.updateInterval, true);
		}
		if (cfg.pinConfiguration3 > 1) {
		BrickletIO4V2InputValueConfig channelCfg = getChannelConfigFn.apply("BrickletIO4V2Input3").as(BrickletIO4V2InputValueConfig.class);
		this.setConfiguration(3, 'i', cfg.pinConfiguration3 % 2 == 1);
		            this.setInputValueCallbackConfiguration(3, channelCfg.updateInterval, true);
		}
		if (cfg.pinConfiguration0 <= 1) {
		this.setConfiguration(0, 'o', cfg.pinConfiguration0 % 2 == 1);
		}
		if (cfg.pinConfiguration1 <= 1) {
		this.setConfiguration(1, 'o', cfg.pinConfiguration1 % 2 == 1);
		}
		if (cfg.pinConfiguration2 <= 1) {
		this.setConfiguration(2, 'o', cfg.pinConfiguration2 % 2 == 1);
		}
		if (cfg.pinConfiguration3 <= 1) {
		this.setConfiguration(3, 'o', cfg.pinConfiguration3 % 2 == 1);
		}
		if (cfg.pinConfiguration0 > 1) {
		BrickletIO4V2EdgeCountConfig channelCfg = getChannelConfigFn.apply("BrickletIO4V2EdgeCount0").as(BrickletIO4V2EdgeCountConfig.class);
		this.setEdgeCountConfiguration(0, channelCfg.edgeType, channelCfg.debounce);
		if(channelCfg.updateInterval > 0) {
    this.manualChannelUpdates.add(scheduler.scheduleWithFixedDelay(() -> {if(handler.getThing().getStatus() == ThingStatus.ONLINE) {handler.handleCommand(new ChannelUID(handler.getThing().getUID(), "BrickletIO4V2EdgeCount0"), RefreshType.REFRESH);}}, channelCfg.updateInterval, channelCfg.updateInterval, TimeUnit.MILLISECONDS));
}
		}
		if (cfg.pinConfiguration1 > 1) {
		BrickletIO4V2EdgeCountConfig channelCfg = getChannelConfigFn.apply("BrickletIO4V2EdgeCount1").as(BrickletIO4V2EdgeCountConfig.class);
		this.setEdgeCountConfiguration(1, channelCfg.edgeType, channelCfg.debounce);
		if(channelCfg.updateInterval > 0) {
    this.manualChannelUpdates.add(scheduler.scheduleWithFixedDelay(() -> {if(handler.getThing().getStatus() == ThingStatus.ONLINE) {handler.handleCommand(new ChannelUID(handler.getThing().getUID(), "BrickletIO4V2EdgeCount1"), RefreshType.REFRESH);}}, channelCfg.updateInterval, channelCfg.updateInterval, TimeUnit.MILLISECONDS));
}
		}
		if (cfg.pinConfiguration2 > 1) {
		BrickletIO4V2EdgeCountConfig channelCfg = getChannelConfigFn.apply("BrickletIO4V2EdgeCount2").as(BrickletIO4V2EdgeCountConfig.class);
		this.setEdgeCountConfiguration(2, channelCfg.edgeType, channelCfg.debounce);
		if(channelCfg.updateInterval > 0) {
    this.manualChannelUpdates.add(scheduler.scheduleWithFixedDelay(() -> {if(handler.getThing().getStatus() == ThingStatus.ONLINE) {handler.handleCommand(new ChannelUID(handler.getThing().getUID(), "BrickletIO4V2EdgeCount2"), RefreshType.REFRESH);}}, channelCfg.updateInterval, channelCfg.updateInterval, TimeUnit.MILLISECONDS));
}
		}
		if (cfg.pinConfiguration3 > 1) {
		BrickletIO4V2EdgeCountConfig channelCfg = getChannelConfigFn.apply("BrickletIO4V2EdgeCount3").as(BrickletIO4V2EdgeCountConfig.class);
		this.setEdgeCountConfiguration(3, channelCfg.edgeType, channelCfg.debounce);
		if(channelCfg.updateInterval > 0) {
    this.manualChannelUpdates.add(scheduler.scheduleWithFixedDelay(() -> {if(handler.getThing().getStatus() == ThingStatus.ONLINE) {handler.handleCommand(new ChannelUID(handler.getThing().getUID(), "BrickletIO4V2EdgeCount3"), RefreshType.REFRESH);}}, channelCfg.updateInterval, channelCfg.updateInterval, TimeUnit.MILLISECONDS));
}
		}
    }

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

        BrickletIO4V2Config cfg = (BrickletIO4V2Config) config.as(BrickletIO4V2Config.class);
        if(cfg.pinConfiguration0 > 1) {
this.setInputValueCallbackConfiguration(0, 0, false);}
		if(cfg.pinConfiguration1 > 1) {
this.setInputValueCallbackConfiguration(1, 0, false);}
		if(cfg.pinConfiguration2 > 1) {
this.setInputValueCallbackConfiguration(2, 0, false);}
		if(cfg.pinConfiguration3 > 1) {
this.setInputValueCallbackConfiguration(3, 0, false);}
		
    }

    @Override
    public List<String> getEnabledChannels(org.eclipse.smarthome.config.core.Configuration config) throws TinkerforgeException{
        BrickletIO4V2Config cfg = (BrickletIO4V2Config) config.as(BrickletIO4V2Config.class);
List<String> result = new ArrayList<String>();
        if (cfg.pinConfiguration0 > 1) {
                result.add("BrickletIO4V2Input0");
            }
		if (cfg.pinConfiguration1 > 1) {
                result.add("BrickletIO4V2Input1");
            }
		if (cfg.pinConfiguration2 > 1) {
                result.add("BrickletIO4V2Input2");
            }
		if (cfg.pinConfiguration3 > 1) {
                result.add("BrickletIO4V2Input3");
            }
		if (cfg.pinConfiguration0 <= 1) {
                result.add("BrickletIO4V2Output0");
            }
		if (cfg.pinConfiguration1 <= 1) {
                result.add("BrickletIO4V2Output1");
            }
		if (cfg.pinConfiguration2 <= 1) {
                result.add("BrickletIO4V2Output2");
            }
		if (cfg.pinConfiguration3 <= 1) {
                result.add("BrickletIO4V2Output3");
            }
		if (cfg.pinConfiguration0 <= 1) {
                result.add("BrickletIO4V2Monoflop0");
            }
		if (cfg.pinConfiguration1 <= 1) {
                result.add("BrickletIO4V2Monoflop1");
            }
		if (cfg.pinConfiguration2 <= 1) {
                result.add("BrickletIO4V2Monoflop2");
            }
		if (cfg.pinConfiguration3 <= 1) {
                result.add("BrickletIO4V2Monoflop3");
            }
		if (cfg.pinConfiguration0 > 1) {
                result.add("BrickletIO4V2EdgeCount0");
            }
		if (cfg.pinConfiguration1 > 1) {
                result.add("BrickletIO4V2EdgeCount1");
            }
		if (cfg.pinConfiguration2 > 1) {
                result.add("BrickletIO4V2EdgeCount2");
            }
		if (cfg.pinConfiguration3 > 1) {
                result.add("BrickletIO4V2EdgeCount3");
            }
        return result;
    }

    public static @Nullable ChannelType getChannelType(ChannelTypeUID channelTypeUID) {
        switch(channelTypeUID.getId()) {
            case "BrickletIO4V2InputValue":
                return ChannelTypeBuilder.state(new ChannelTypeUID("tinkerforge", "BrickletIO4V2InputValue"), "NOT USED", "Contact").withConfigDescriptionURI(URI.create("channel-type:tinkerforge:BrickletIO4V2InputValue")).withDescription("The logic level that is currently measured on the pin.").build();
            case "BrickletIO4V2Output":
                return ChannelTypeBuilder.state(new ChannelTypeUID("tinkerforge", "BrickletIO4V2Output"), "NOT USED", "Switch").withConfigDescriptionURI(URI.create("channel-type:tinkerforge:BrickletIO4V2Output")).withDescription("The logic level that is currently set on the pin.").build();
            case "BrickletIO4V2Monoflop":
                return ChannelTypeBuilder.state(new ChannelTypeUID("tinkerforge", "BrickletIO4V2Monoflop"), "NOT USED", "String").withConfigDescriptionURI(URI.create("channel-type:tinkerforge:BrickletIO4V2Monoflop")).withDescription("Triggers a monoflop as configured.")
.withCommandDescription(CommandDescriptionBuilder.create().withCommandOption(new CommandOption("TRIGGER", "Trigger")).build()).build();
            case "BrickletIO4V2EdgeCount":
                return ChannelTypeBuilder.state(new ChannelTypeUID("tinkerforge", "BrickletIO4V2EdgeCount"), "Edge Count", "Number:Dimensionless").withConfigDescriptionURI(URI.create("channel-type:tinkerforge:BrickletIO4V2EdgeCount")).withDescription("The current value of the edge counter of the pin.")
.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 IO-4 Bricklet 2.0").isListed(false).withSupportedBridgeTypeUIDs(Arrays.asList(TinkerforgeBindingConstants.THING_TYPE_BRICK_DAEMON.toString())).withConfigDescriptionURI(URI.create("thing-type:tinkerforge:" + thingTypeUID.getId())).withDescription("4-channel digital input/output").withChannelDefinitions(Arrays.asList(new ChannelDefinitionBuilder("BrickletIO4V2Input0", new ChannelTypeUID("tinkerforge", "BrickletIO4V2InputValue")).withLabel("Input Value 0").build(), new ChannelDefinitionBuilder("BrickletIO4V2Input1", new ChannelTypeUID("tinkerforge", "BrickletIO4V2InputValue")).withLabel("Input Value 1").build(), new ChannelDefinitionBuilder("BrickletIO4V2Input2", new ChannelTypeUID("tinkerforge", "BrickletIO4V2InputValue")).withLabel("Input Value 2").build(), new ChannelDefinitionBuilder("BrickletIO4V2Input3", new ChannelTypeUID("tinkerforge", "BrickletIO4V2InputValue")).withLabel("Input Value 3").build(), new ChannelDefinitionBuilder("BrickletIO4V2Output0", new ChannelTypeUID("tinkerforge", "BrickletIO4V2Output")).withLabel("Output Value 0").build(), new ChannelDefinitionBuilder("BrickletIO4V2Output1", new ChannelTypeUID("tinkerforge", "BrickletIO4V2Output")).withLabel("Output Value 1").build(), new ChannelDefinitionBuilder("BrickletIO4V2Output2", new ChannelTypeUID("tinkerforge", "BrickletIO4V2Output")).withLabel("Output Value 2").build(), new ChannelDefinitionBuilder("BrickletIO4V2Output3", new ChannelTypeUID("tinkerforge", "BrickletIO4V2Output")).withLabel("Output Value 3").build(), new ChannelDefinitionBuilder("BrickletIO4V2Monoflop0", new ChannelTypeUID("tinkerforge", "BrickletIO4V2Monoflop")).withLabel("Monoflop 0").build(), new ChannelDefinitionBuilder("BrickletIO4V2Monoflop1", new ChannelTypeUID("tinkerforge", "BrickletIO4V2Monoflop")).withLabel("Monoflop 1").build(), new ChannelDefinitionBuilder("BrickletIO4V2Monoflop2", new ChannelTypeUID("tinkerforge", "BrickletIO4V2Monoflop")).withLabel("Monoflop 2").build(), new ChannelDefinitionBuilder("BrickletIO4V2Monoflop3", new ChannelTypeUID("tinkerforge", "BrickletIO4V2Monoflop")).withLabel("Monoflop 3").build(), new ChannelDefinitionBuilder("BrickletIO4V2EdgeCount0", new ChannelTypeUID("tinkerforge", "BrickletIO4V2EdgeCount")).withLabel("Edge Count 0").build(), new ChannelDefinitionBuilder("BrickletIO4V2EdgeCount1", new ChannelTypeUID("tinkerforge", "BrickletIO4V2EdgeCount")).withLabel("Edge Count 1").build(), new ChannelDefinitionBuilder("BrickletIO4V2EdgeCount2", new ChannelTypeUID("tinkerforge", "BrickletIO4V2EdgeCount")).withLabel("Edge Count 2").build(), new ChannelDefinitionBuilder("BrickletIO4V2EdgeCount3", new ChannelTypeUID("tinkerforge", "BrickletIO4V2EdgeCount")).withLabel("Edge Count 3").build())).withProperties(props).build();
    }

    public static @Nullable ConfigDescription getConfigDescription(URI uri) {
        switch(uri.toASCIIString()) {
            case "thing-type:tinkerforge:brickletio4v2":
                return ConfigDescriptionBuilder.create(uri).withParameters(Arrays.asList(ConfigDescriptionParameterBuilder.create("pinConfiguration0", Type.INTEGER).withDefault("3").withDescription("Configures pin 0 as input or output. Inputs without pull-up will be floating if nothing is connected. Outputs can have an initial state of low or high.").withLabel("Pin Configuration 0").withLimitToOptions(true).withOptions(Arrays.asList(new ParameterOption("3", "Input with pull-up"), new ParameterOption("2", "Input without pull-up"), new ParameterOption("1", "Output (Initial high)"), new ParameterOption("0", "Output (Initial low)"))).build(), ConfigDescriptionParameterBuilder.create("pinConfiguration1", Type.INTEGER).withDefault("3").withDescription("Configures pin 1 as input or output. Inputs without pull-up will be floating if nothing is connected. Outputs can have an initial state of low or high.").withLabel("Pin Configuration 1").withLimitToOptions(true).withOptions(Arrays.asList(new ParameterOption("3", "Input with pull-up"), new ParameterOption("2", "Input without pull-up"), new ParameterOption("1", "Output (Initial high)"), new ParameterOption("0", "Output (Initial low)"))).build(), ConfigDescriptionParameterBuilder.create("pinConfiguration2", Type.INTEGER).withDefault("3").withDescription("Configures pin 2 as input or output. Inputs without pull-up will be floating if nothing is connected. Outputs can have an initial state of low or high.").withLabel("Pin Configuration 2").withLimitToOptions(true).withOptions(Arrays.asList(new ParameterOption("3", "Input with pull-up"), new ParameterOption("2", "Input without pull-up"), new ParameterOption("1", "Output (Initial high)"), new ParameterOption("0", "Output (Initial low)"))).build(), ConfigDescriptionParameterBuilder.create("pinConfiguration3", Type.INTEGER).withDefault("3").withDescription("Configures pin 3 as input or output. Inputs without pull-up will be floating if nothing is connected. Outputs can have an initial state of low or high.").withLabel("Pin Configuration 3").withLimitToOptions(true).withOptions(Arrays.asList(new ParameterOption("3", "Input with pull-up"), new ParameterOption("2", "Input without pull-up"), new ParameterOption("1", "Output (Initial high)"), new ParameterOption("0", "Output (Initial low)"))).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()).build();
            case "channel-type:tinkerforge:BrickletIO4V2InputValue":
                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:BrickletIO4V2Output":
                return ConfigDescriptionBuilder.create(uri).withParameters(Arrays.asList()).withParameterGroups(Arrays.asList()).build();
            case "channel-type:tinkerforge:BrickletIO4V2Monoflop":
                return ConfigDescriptionBuilder.create(uri).withParameters(Arrays.asList(ConfigDescriptionParameterBuilder.create("monoflopDuration", Type.INTEGER).withDefault("1000").withDescription("The time that the pin should hold the configured value.").withLabel("Monoflop Duration").withUnit("ms").withAdvanced(true).withMinimum(BigDecimal.valueOf(0)).withMaximum(BigDecimal.valueOf(4294967295L)).build(), ConfigDescriptionParameterBuilder.create("monoflopValue", Type.BOOLEAN).withDefault("true").withDescription("The desired value of the pin.").withLabel("Monoflop Value").withAdvanced(true).build())).withParameterGroups(Arrays.asList()).build();
            case "channel-type:tinkerforge:BrickletIO4V2EdgeCount":
                return ConfigDescriptionBuilder.create(uri).withParameters(Arrays.asList(ConfigDescriptionParameterBuilder.create("edgeType", Type.INTEGER).withDefault("0").withDescription("Configures if rising edges, falling edges or both are counted.").withLabel("Edge Type").withAdvanced(true).withLimitToOptions(true).withOptions(Arrays.asList(new ParameterOption("0", "Rising"), new ParameterOption("1", "Falling"), new ParameterOption("2", "Both"))).build(), ConfigDescriptionParameterBuilder.create("debounce", Type.INTEGER).withDefault("100").withDescription("The debounce time is the minimum time between two count increments.").withLabel("Debounce Time").withAdvanced(true).withMinimum(BigDecimal.valueOf(0)).withMaximum(BigDecimal.valueOf(255)).build(), ConfigDescriptionParameterBuilder.create("resetOnRead", Type.BOOLEAN).withDefault("false").withDescription("Enabling this will reset the edge counter after openHAB reads its value. Use this if you want relative counts per update.").withLabel("Reset Edge Counter On Update").withAdvanced(true).build(), ConfigDescriptionParameterBuilder.create("updateInterval", Type.INTEGER).withDefault("1000").withDescription("Specifies the update interval in milliseconds. A value of 0 disables automatic updates.").withLabel("Update Interval").withUnit("ms").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 {
        BrickletIO4V2Config cfg = (BrickletIO4V2Config) config.as(BrickletIO4V2Config.class);
switch(channel) {
            case "BrickletIO4V2Input0":
                   {
            boolean[] value = this.getValue();
            if(true)
                updateStateFn.accept(channel, transformBrickletIO4V2Input0Getter0(value, cfg));
        }
                   return;
            case "BrickletIO4V2Input1":
                   {
            boolean[] value = this.getValue();
            if(true)
                updateStateFn.accept(channel, transformBrickletIO4V2Input1Getter0(value, cfg));
        }
                   return;
            case "BrickletIO4V2Input2":
                   {
            boolean[] value = this.getValue();
            if(true)
                updateStateFn.accept(channel, transformBrickletIO4V2Input2Getter0(value, cfg));
        }
                   return;
            case "BrickletIO4V2Input3":
                   {
            boolean[] value = this.getValue();
            if(true)
                updateStateFn.accept(channel, transformBrickletIO4V2Input3Getter0(value, cfg));
        }
                   return;
            case "BrickletIO4V2Output0":
                   {
            boolean[] value = this.getValue();
            if(true)
                updateStateFn.accept(channel, transformBrickletIO4V2Output0Getter0(value, cfg));
        }
                   return;
            case "BrickletIO4V2Output1":
                   {
            boolean[] value = this.getValue();
            if(true)
                updateStateFn.accept(channel, transformBrickletIO4V2Output1Getter0(value, cfg));
        }
                   return;
            case "BrickletIO4V2Output2":
                   {
            boolean[] value = this.getValue();
            if(true)
                updateStateFn.accept(channel, transformBrickletIO4V2Output2Getter0(value, cfg));
        }
                   return;
            case "BrickletIO4V2Output3":
                   {
            boolean[] value = this.getValue();
            if(true)
                updateStateFn.accept(channel, transformBrickletIO4V2Output3Getter0(value, cfg));
        }
                   return;
            case "BrickletIO4V2Monoflop0":
                   {
            BrickletIO4V2.Monoflop value = this.getMonoflop(0);
            if(true)
                updateStateFn.accept(channel, transformBrickletIO4V2Monoflop0Getter0(value, cfg));
        }
                   return;
            case "BrickletIO4V2Monoflop1":
                   {
            BrickletIO4V2.Monoflop value = this.getMonoflop(1);
            if(true)
                updateStateFn.accept(channel, transformBrickletIO4V2Monoflop1Getter0(value, cfg));
        }
                   return;
            case "BrickletIO4V2Monoflop2":
                   {
            BrickletIO4V2.Monoflop value = this.getMonoflop(2);
            if(true)
                updateStateFn.accept(channel, transformBrickletIO4V2Monoflop2Getter0(value, cfg));
        }
                   return;
            case "BrickletIO4V2Monoflop3":
                   {
            BrickletIO4V2.Monoflop value = this.getMonoflop(3);
            if(true)
                updateStateFn.accept(channel, transformBrickletIO4V2Monoflop3Getter0(value, cfg));
        }
                   return;
            case "BrickletIO4V2EdgeCount0": {
                   BrickletIO4V2EdgeCountConfig channelCfg = channelConfig.as(BrickletIO4V2EdgeCountConfig.class);
                   {
            long value = this.getEdgeCount(0, channelCfg.resetOnRead);
            if(true)
                updateStateFn.accept(channel, transformBrickletIO4V2EdgeCount0Getter0(value, cfg));
        }
                   return;
               }
            case "BrickletIO4V2EdgeCount1": {
                   BrickletIO4V2EdgeCountConfig channelCfg = channelConfig.as(BrickletIO4V2EdgeCountConfig.class);
                   {
            long value = this.getEdgeCount(1, channelCfg.resetOnRead);
            if(true)
                updateStateFn.accept(channel, transformBrickletIO4V2EdgeCount1Getter0(value, cfg));
        }
                   return;
               }
            case "BrickletIO4V2EdgeCount2": {
                   BrickletIO4V2EdgeCountConfig channelCfg = channelConfig.as(BrickletIO4V2EdgeCountConfig.class);
                   {
            long value = this.getEdgeCount(2, channelCfg.resetOnRead);
            if(true)
                updateStateFn.accept(channel, transformBrickletIO4V2EdgeCount2Getter0(value, cfg));
        }
                   return;
               }
            case "BrickletIO4V2EdgeCount3": {
                   BrickletIO4V2EdgeCountConfig channelCfg = channelConfig.as(BrickletIO4V2EdgeCountConfig.class);
                   {
            long value = this.getEdgeCount(3, channelCfg.resetOnRead);
            if(true)
                updateStateFn.accept(channel, transformBrickletIO4V2EdgeCount3Getter0(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 = new ArrayList<SetterRefresh>();
        
        switch(channel) {
            case "BrickletIO4V2Output0":
                if (command instanceof OnOffType) {
                    OnOffType cmd = (OnOffType) command;
this.setSelectedValue(0, cmd == OnOffType.ON);
                }
        
                else {
                    logger.warn("Command type {} not supported for channel {}. Please use one of OnOffType.", command.getClass().getName(), channel);
                }
                
                break;
            case "BrickletIO4V2Output1":
                if (command instanceof OnOffType) {
                    OnOffType cmd = (OnOffType) command;
this.setSelectedValue(1, cmd == OnOffType.ON);
                }
        
                else {
                    logger.warn("Command type {} not supported for channel {}. Please use one of OnOffType.", command.getClass().getName(), channel);
                }
                
                break;
            case "BrickletIO4V2Output2":
                if (command instanceof OnOffType) {
                    OnOffType cmd = (OnOffType) command;
this.setSelectedValue(2, cmd == OnOffType.ON);
                }
        
                else {
                    logger.warn("Command type {} not supported for channel {}. Please use one of OnOffType.", command.getClass().getName(), channel);
                }
                
                break;
            case "BrickletIO4V2Output3":
                if (command instanceof OnOffType) {
                    OnOffType cmd = (OnOffType) command;
this.setSelectedValue(3, cmd == OnOffType.ON);
                }
        
                else {
                    logger.warn("Command type {} not supported for channel {}. Please use one of OnOffType.", command.getClass().getName(), channel);
                }
                
                break;
            case "BrickletIO4V2Monoflop0":
                if (command instanceof StringType) {
                    BrickletIO4V2MonoflopConfig channelCfg = channelConfig.as(BrickletIO4V2MonoflopConfig.class);
this.setMonoflop(0, channelCfg.monoflopValue.booleanValue(), channelCfg.monoflopDuration);
                }
        
                else {
                    logger.warn("Command type {} not supported for channel {}. Please use one of StringType.", command.getClass().getName(), channel);
                }
                result.add(new SetterRefresh("BrickletIO4V2Output0", 0));
                break;
            case "BrickletIO4V2Monoflop1":
                if (command instanceof StringType) {
                    BrickletIO4V2MonoflopConfig channelCfg = channelConfig.as(BrickletIO4V2MonoflopConfig.class);
this.setMonoflop(1, channelCfg.monoflopValue.booleanValue(), channelCfg.monoflopDuration);
                }
        
                else {
                    logger.warn("Command type {} not supported for channel {}. Please use one of StringType.", command.getClass().getName(), channel);
                }
                result.add(new SetterRefresh("BrickletIO4V2Output1", 0));
                break;
            case "BrickletIO4V2Monoflop2":
                if (command instanceof StringType) {
                    BrickletIO4V2MonoflopConfig channelCfg = channelConfig.as(BrickletIO4V2MonoflopConfig.class);
this.setMonoflop(2, channelCfg.monoflopValue.booleanValue(), channelCfg.monoflopDuration);
                }
        
                else {
                    logger.warn("Command type {} not supported for channel {}. Please use one of StringType.", command.getClass().getName(), channel);
                }
                result.add(new SetterRefresh("BrickletIO4V2Output2", 0));
                break;
            case "BrickletIO4V2Monoflop3":
                if (command instanceof StringType) {
                    BrickletIO4V2MonoflopConfig channelCfg = channelConfig.as(BrickletIO4V2MonoflopConfig.class);
this.setMonoflop(3, channelCfg.monoflopValue.booleanValue(), channelCfg.monoflopDuration);
                }
        
                else {
                    logger.warn("Command type {} not supported for channel {}. Please use one of StringType.", command.getClass().getName(), channel);
                }
                result.add(new SetterRefresh("BrickletIO4V2Output3", 0));
                break;
default:
                logger.warn("Command for unknown channel {}", channel);
        }
        return result;
    }

        private org.eclipse.smarthome.core.types.State transformBrickletIO4V2Input0Callback0(int channel, boolean changed, boolean value, BrickletIO4V2Config cfg) {
        return value ? OpenClosedType.CLOSED : OpenClosedType.OPEN;
    }
	    private org.eclipse.smarthome.core.types.State transformBrickletIO4V2Input1Callback0(int channel, boolean changed, boolean value, BrickletIO4V2Config cfg) {
        return value ? OpenClosedType.CLOSED : OpenClosedType.OPEN;
    }
	    private org.eclipse.smarthome.core.types.State transformBrickletIO4V2Input2Callback0(int channel, boolean changed, boolean value, BrickletIO4V2Config cfg) {
        return value ? OpenClosedType.CLOSED : OpenClosedType.OPEN;
    }
	    private org.eclipse.smarthome.core.types.State transformBrickletIO4V2Input3Callback0(int channel, boolean changed, boolean value, BrickletIO4V2Config cfg) {
        return value ? OpenClosedType.CLOSED : OpenClosedType.OPEN;
    }
	    private org.eclipse.smarthome.core.types.State transformBrickletIO4V2Output0Callback0(int channel, boolean value, BrickletIO4V2Config cfg) {
        return value ? OnOffType.ON : OnOffType.OFF;
    }
	    private org.eclipse.smarthome.core.types.State transformBrickletIO4V2Output1Callback0(int channel, boolean value, BrickletIO4V2Config cfg) {
        return value ? OnOffType.ON : OnOffType.OFF;
    }
	    private org.eclipse.smarthome.core.types.State transformBrickletIO4V2Output2Callback0(int channel, boolean value, BrickletIO4V2Config cfg) {
        return value ? OnOffType.ON : OnOffType.OFF;
    }
	    private org.eclipse.smarthome.core.types.State transformBrickletIO4V2Output3Callback0(int channel, boolean value, BrickletIO4V2Config cfg) {
        return value ? OnOffType.ON : OnOffType.OFF;
    }
	    private org.eclipse.smarthome.core.types.State transformBrickletIO4V2Input0Getter0(boolean[] value, BrickletIO4V2Config cfg) {
        return value[0] ? OpenClosedType.CLOSED : OpenClosedType.OPEN;
    }
	    private org.eclipse.smarthome.core.types.State transformBrickletIO4V2Input1Getter0(boolean[] value, BrickletIO4V2Config cfg) {
        return value[1] ? OpenClosedType.CLOSED : OpenClosedType.OPEN;
    }
	    private org.eclipse.smarthome.core.types.State transformBrickletIO4V2Input2Getter0(boolean[] value, BrickletIO4V2Config cfg) {
        return value[2] ? OpenClosedType.CLOSED : OpenClosedType.OPEN;
    }
	    private org.eclipse.smarthome.core.types.State transformBrickletIO4V2Input3Getter0(boolean[] value, BrickletIO4V2Config cfg) {
        return value[3] ? OpenClosedType.CLOSED : OpenClosedType.OPEN;
    }
	    private org.eclipse.smarthome.core.types.State transformBrickletIO4V2Output0Getter0(boolean[] value, BrickletIO4V2Config cfg) {
        return value[0] ? OnOffType.ON : OnOffType.OFF;
    }
	    private org.eclipse.smarthome.core.types.State transformBrickletIO4V2Output1Getter0(boolean[] value, BrickletIO4V2Config cfg) {
        return value[1] ? OnOffType.ON : OnOffType.OFF;
    }
	    private org.eclipse.smarthome.core.types.State transformBrickletIO4V2Output2Getter0(boolean[] value, BrickletIO4V2Config cfg) {
        return value[2] ? OnOffType.ON : OnOffType.OFF;
    }
	    private org.eclipse.smarthome.core.types.State transformBrickletIO4V2Output3Getter0(boolean[] value, BrickletIO4V2Config cfg) {
        return value[3] ? OnOffType.ON : OnOffType.OFF;
    }
	    private org.eclipse.smarthome.core.types.State transformBrickletIO4V2Monoflop0Getter0(BrickletIO4V2.Monoflop value, BrickletIO4V2Config cfg) {
        return value.value ? OnOffType.ON : OnOffType.OFF;
    }
	    private org.eclipse.smarthome.core.types.State transformBrickletIO4V2Monoflop1Getter0(BrickletIO4V2.Monoflop value, BrickletIO4V2Config cfg) {
        return value.value ? OnOffType.ON : OnOffType.OFF;
    }
	    private org.eclipse.smarthome.core.types.State transformBrickletIO4V2Monoflop2Getter0(BrickletIO4V2.Monoflop value, BrickletIO4V2Config cfg) {
        return value.value ? OnOffType.ON : OnOffType.OFF;
    }
	    private org.eclipse.smarthome.core.types.State transformBrickletIO4V2Monoflop3Getter0(BrickletIO4V2.Monoflop value, BrickletIO4V2Config cfg) {
        return value.value ? OnOffType.ON : OnOffType.OFF;
    }
	    private org.eclipse.smarthome.core.types.State transformBrickletIO4V2EdgeCount0Getter0(long value, BrickletIO4V2Config cfg) {
        return new DecimalType(value);
    }
	    private org.eclipse.smarthome.core.types.State transformBrickletIO4V2EdgeCount1Getter0(long value, BrickletIO4V2Config cfg) {
        return new DecimalType(value);
    }
	    private org.eclipse.smarthome.core.types.State transformBrickletIO4V2EdgeCount2Getter0(long value, BrickletIO4V2Config cfg) {
        return new DecimalType(value);
    }
	    private org.eclipse.smarthome.core.types.State transformBrickletIO4V2EdgeCount3Getter0(long value, BrickletIO4V2Config cfg) {
        return new DecimalType(value);
    }

}