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

import com.tinkerforge.BrickletLCD20x4;
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 LCD 20x4 Bricklet.
 * @author Erik Fleckstein - Initial contribution
 */
@NonNullByDefault
public class BrickletLCD20x4Wrapper extends BrickletLCD20x4 implements StandardFlashable, DeviceWrapper {
    public static final DeviceInfo DEVICE_INFO = new DeviceInfo(BrickletLCD20x4.DEVICE_DISPLAY_NAME, "brickletlcd20x4", BrickletLCD20x4.DEVICE_IDENTIFIER, BrickletLCD20x4Wrapper.class, BrickletLCD20x4Actions.class, "2.0.2", false);

    public BrickletLCD20x4Wrapper(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(BrickletLCD20x4Wrapper.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);
        BrickletLCD20x4Config cfg = (BrickletLCD20x4Config) config.as(BrickletLCD20x4Config.class);
        this.addButtonPressedListener(this.reg((button) -> {if(button == 0) {triggerChannelFn.accept("BrickletLCD20x4Button0", transformBrickletLCD20x4Button0Callback0(button, cfg));}}, this::removeButtonPressedListener));
		this.addButtonReleasedListener(this.reg((button) -> {if(button == 0) {triggerChannelFn.accept("BrickletLCD20x4Button0", transformBrickletLCD20x4Button0Callback1(button, cfg));}}, this::removeButtonReleasedListener));
		this.addButtonPressedListener(this.reg((button) -> {if(button == 1) {triggerChannelFn.accept("BrickletLCD20x4Button1", transformBrickletLCD20x4Button1Callback0(button, cfg));}}, this::removeButtonPressedListener));
		this.addButtonReleasedListener(this.reg((button) -> {if(button == 1) {triggerChannelFn.accept("BrickletLCD20x4Button1", transformBrickletLCD20x4Button1Callback1(button, cfg));}}, this::removeButtonReleasedListener));
		this.addButtonPressedListener(this.reg((button) -> {if(button == 2) {triggerChannelFn.accept("BrickletLCD20x4Button2", transformBrickletLCD20x4Button2Callback0(button, cfg));}}, this::removeButtonPressedListener));
		this.addButtonReleasedListener(this.reg((button) -> {if(button == 2) {triggerChannelFn.accept("BrickletLCD20x4Button2", transformBrickletLCD20x4Button2Callback1(button, cfg));}}, this::removeButtonReleasedListener));
		if(this.getIdentity().hardwareVersion[1] >= 2) {
this.addButtonPressedListener(this.reg((button) -> {if(button == 3) {triggerChannelFn.accept("BrickletLCD20x4Button3", transformBrickletLCD20x4Button3Callback0(button, cfg));}}, this::removeButtonPressedListener));}
		if(this.getIdentity().hardwareVersion[1] >= 2) {
this.addButtonReleasedListener(this.reg((button) -> {if(button == 3) {triggerChannelFn.accept("BrickletLCD20x4Button3", transformBrickletLCD20x4Button3Callback1(button, cfg));}}, this::removeButtonReleasedListener));}
        this.setConfig(cfg.showCursor, cfg.showBlinkingCursor);
    }

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

        
        
    }

    @Override
    public List<String> getEnabledChannels(org.eclipse.smarthome.config.core.Configuration config) throws TinkerforgeException{
        List<String> result = new ArrayList<String>();
        result.add("BrickletLCD20x4Text");
		result.add("BrickletLCD20x4ClearDisplay");
		result.add("BrickletLCD20x4Backlight");
		result.add("BrickletLCD20x4Button0");
		result.add("BrickletLCD20x4Button1");
		result.add("BrickletLCD20x4Button2");
		if (this.getIdentity().hardwareVersion[1] >= 2) {
                result.add("BrickletLCD20x4Button3");
            }
        return result;
    }

    public static @Nullable ChannelType getChannelType(ChannelTypeUID channelTypeUID) {
        switch(channelTypeUID.getId()) {
            case "BrickletLCD20x4Text":
                return ChannelTypeBuilder.state(new ChannelTypeUID("tinkerforge", "BrickletLCD20x4Text"), "Text", "String").withConfigDescriptionURI(URI.create("channel-type:tinkerforge:BrickletLCD20x4Text")).withDescription("Text to display on the LCD. Command format is [line],[position],[text].<br/><br/>Additional ',' are handled as part of the text. Unicode characters are converted to the LCD character set if possible. Additionally you can use \\x[two hex digits] to use a character of the LCD character set directly.").build();
            case "BrickletLCD20x4ClearDisplay":
                return ChannelTypeBuilder.state(new ChannelTypeUID("tinkerforge", "BrickletLCD20x4ClearDisplay"), "Clear Display", "String").withConfigDescriptionURI(URI.create("channel-type:tinkerforge:BrickletLCD20x4ClearDisplay")).withDescription("Deletes all characters from the display.")
.withCommandDescription(CommandDescriptionBuilder.create().withCommandOption(new CommandOption("CLEAR", "Clear")).build()).build();
            case "BrickletLCD20x4Backlight":
                return ChannelTypeBuilder.state(new ChannelTypeUID("tinkerforge", "BrickletLCD20x4Backlight"), "Backlight", "Switch").withConfigDescriptionURI(URI.create("channel-type:tinkerforge:BrickletLCD20x4Backlight")).withDescription("Toggles the LCD's backlight.").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 LCD 20x4 Bricklet").isListed(false).withSupportedBridgeTypeUIDs(Arrays.asList(TinkerforgeBindingConstants.THING_TYPE_BRICK_DAEMON.toString())).withConfigDescriptionURI(URI.create("thing-type:tinkerforge:" + thingTypeUID.getId())).withDescription("20x4 character alphanumeric display with blue backlight").withChannelDefinitions(Arrays.asList(new ChannelDefinitionBuilder("BrickletLCD20x4Text", new ChannelTypeUID("tinkerforge", "BrickletLCD20x4Text")).withLabel("Text").build(), new ChannelDefinitionBuilder("BrickletLCD20x4ClearDisplay", new ChannelTypeUID("tinkerforge", "BrickletLCD20x4ClearDisplay")).withLabel("Clear Display").build(), new ChannelDefinitionBuilder("BrickletLCD20x4Backlight", new ChannelTypeUID("tinkerforge", "BrickletLCD20x4Backlight")).withLabel("Backlight").build(), new ChannelDefinitionBuilder("BrickletLCD20x4Button0", new ChannelTypeUID("system", "rawbutton")).withLabel("Button 0").withDescription("This channel triggers if button 0 is pressed or released").build(), new ChannelDefinitionBuilder("BrickletLCD20x4Button1", new ChannelTypeUID("system", "rawbutton")).withLabel("Button 1").withDescription("This channel triggers if button 1 is pressed or released").build(), new ChannelDefinitionBuilder("BrickletLCD20x4Button2", new ChannelTypeUID("system", "rawbutton")).withLabel("Button 2").withDescription("This channel triggers if button 2 is pressed or released").build(), new ChannelDefinitionBuilder("BrickletLCD20x4Button3", new ChannelTypeUID("system", "rawbutton")).withLabel("Button 3").withDescription("This channel triggers if button 3 is pressed or released").build())).withProperties(props).build();
    }

    public static @Nullable ConfigDescription getConfigDescription(URI uri) {
        switch(uri.toASCIIString()) {
            case "thing-type:tinkerforge:brickletlcd20x4":
                return ConfigDescriptionBuilder.create(uri).withParameters(Arrays.asList(ConfigDescriptionParameterBuilder.create("showCursor", Type.BOOLEAN).withDefault("false").withDescription("Configures if the cursor (shown as '_') should be visible. The cursor position is one character behind the the last text written.").withLabel("Show Cursor").withAdvanced(true).build(), ConfigDescriptionParameterBuilder.create("showBlinkingCursor", Type.BOOLEAN).withDefault("false").withDescription("Configures if the blinking cursor (shown as a blinking block) should be visible. The cursor position is one character behind the the last text written.").withLabel("Show Blinking Cursor").withAdvanced(true).build())).withParameterGroups(Arrays.asList(new ConfigDescriptionParameterGroup("update_intervals", "null", true, "Update Intervals", "Update Intervals"))).build();
            case "channel-type:tinkerforge:BrickletLCD20x4Text":
                return ConfigDescriptionBuilder.create(uri).withParameters(Arrays.asList()).withParameterGroups(Arrays.asList()).build();
            case "channel-type:tinkerforge:BrickletLCD20x4ClearDisplay":
                return ConfigDescriptionBuilder.create(uri).withParameters(Arrays.asList()).withParameterGroups(Arrays.asList()).build();
            case "channel-type:tinkerforge:BrickletLCD20x4Backlight":
                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 {
        BrickletLCD20x4Config cfg = (BrickletLCD20x4Config) config.as(BrickletLCD20x4Config.class);
switch(channel) {
            case "BrickletLCD20x4Text":
               return;
            case "BrickletLCD20x4ClearDisplay":
               return;
            case "BrickletLCD20x4Backlight":
                   {
            boolean value = this.isBacklightOn();
            if(true)
                updateStateFn.accept(channel, transformBrickletLCD20x4BacklightGetter0(value, cfg));
        }
                   return;
            case "BrickletLCD20x4Button0":
                   {
            boolean value = this.isButtonPressed((short)(0));
            if(true)
                triggerChannelFn.accept(channel, transformBrickletLCD20x4Button0Getter0(value, cfg));
        }
                   return;
            case "BrickletLCD20x4Button1":
                   {
            boolean value = this.isButtonPressed((short)(1));
            if(true)
                triggerChannelFn.accept(channel, transformBrickletLCD20x4Button1Getter0(value, cfg));
        }
                   return;
            case "BrickletLCD20x4Button2":
                   {
            boolean value = this.isButtonPressed((short)(2));
            if(true)
                triggerChannelFn.accept(channel, transformBrickletLCD20x4Button2Getter0(value, cfg));
        }
                   return;
            case "BrickletLCD20x4Button3":
                   {
            boolean value = this.isButtonPressed((short)(3));
            if(true)
                triggerChannelFn.accept(channel, transformBrickletLCD20x4Button3Getter0(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) {
            case "BrickletLCD20x4Text":
                if (command instanceof StringType) {
                    StringType cmd = (StringType) command;
this.writeLine(Helper.parseDisplayCommandLine(cmd.toString(), logger), Helper.parseDisplayCommandPosition(cmd.toString(), logger), Helper.parseDisplayCommandText(cmd.toString(), logger, true));
                }
        
                else {
                    logger.warn("Command type {} not supported for channel {}. Please use one of StringType.", command.getClass().getName(), channel);
                }
                
                break;
            case "BrickletLCD20x4ClearDisplay":
                if (command instanceof StringType) {
                    this.clearDisplay();
                }
        
                else {
                    logger.warn("Command type {} not supported for channel {}. Please use one of StringType.", command.getClass().getName(), channel);
                }
                
                break;
            case "BrickletLCD20x4Backlight":
                if (command instanceof OnOffType) {
                    OnOffType cmd = (OnOffType) command;
if(cmd == OnOffType.ON) {
    this.backlightOn();
}
                }
        
if (command instanceof OnOffType) {
                    OnOffType cmd = (OnOffType) command;
if(cmd == OnOffType.OFF) {
    this.backlightOff();
}
                }
        
                else {
                    logger.warn("Command type {} not supported for channel {}. Please use one of OnOffType, OnOffType.", command.getClass().getName(), channel);
                }
                
                break;
default:
                logger.warn("Command for unknown channel {}", channel);
        }
        return result;
    }

        private String transformBrickletLCD20x4Button0Callback0(short button, BrickletLCD20x4Config cfg) {
        return CommonTriggerEvents.PRESSED;
    }
	    private String transformBrickletLCD20x4Button0Callback1(short button, BrickletLCD20x4Config cfg) {
        return CommonTriggerEvents.RELEASED;
    }
	    private String transformBrickletLCD20x4Button1Callback0(short button, BrickletLCD20x4Config cfg) {
        return CommonTriggerEvents.PRESSED;
    }
	    private String transformBrickletLCD20x4Button1Callback1(short button, BrickletLCD20x4Config cfg) {
        return CommonTriggerEvents.RELEASED;
    }
	    private String transformBrickletLCD20x4Button2Callback0(short button, BrickletLCD20x4Config cfg) {
        return CommonTriggerEvents.PRESSED;
    }
	    private String transformBrickletLCD20x4Button2Callback1(short button, BrickletLCD20x4Config cfg) {
        return CommonTriggerEvents.RELEASED;
    }
	    private String transformBrickletLCD20x4Button3Callback0(short button, BrickletLCD20x4Config cfg) {
        return CommonTriggerEvents.PRESSED;
    }
	    private String transformBrickletLCD20x4Button3Callback1(short button, BrickletLCD20x4Config cfg) {
        return CommonTriggerEvents.RELEASED;
    }
	    private org.eclipse.smarthome.core.types.State transformBrickletLCD20x4BacklightGetter0(boolean value, BrickletLCD20x4Config cfg) {
        return value ? OnOffType.ON : OnOffType.OFF;
    }
	    private String transformBrickletLCD20x4Button0Getter0(boolean value, BrickletLCD20x4Config cfg) {
        return value ? CommonTriggerEvents.PRESSED : CommonTriggerEvents.RELEASED;
    }
	    private String transformBrickletLCD20x4Button1Getter0(boolean value, BrickletLCD20x4Config cfg) {
        return value ? CommonTriggerEvents.PRESSED : CommonTriggerEvents.RELEASED;
    }
	    private String transformBrickletLCD20x4Button2Getter0(boolean value, BrickletLCD20x4Config cfg) {
        return value ? CommonTriggerEvents.PRESSED : CommonTriggerEvents.RELEASED;
    }
	    private String transformBrickletLCD20x4Button3Getter0(boolean value, BrickletLCD20x4Config cfg) {
        return value ? CommonTriggerEvents.PRESSED : CommonTriggerEvents.RELEASED;
    }

}