/**
 * 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 com.tinkerforge.BrickletRS232;
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 RS232 Bricklet.
 * @author Erik Fleckstein - Initial contribution
 */
@NonNullByDefault
public class BrickletRS232Wrapper extends BrickletRS232 implements StandardFlashable, DeviceWrapper {
    public static final DeviceInfo DEVICE_INFO = new DeviceInfo(BrickletRS232.DEVICE_DISPLAY_NAME, "brickletrs232", BrickletRS232.DEVICE_IDENTIFIER, BrickletRS232Wrapper.class, BrickletRS232Actions.class, "2.0.4", false);

    public BrickletRS232Wrapper(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(BrickletRS232Wrapper.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);
        BrickletRS232Config cfg = (BrickletRS232Config) config.as(BrickletRS232Config.class);
        this.addFrameReadableListener(this.reg((frameCount) -> {if(true) {triggerChannelFn.accept("BrickletRS232FrameReadable", transformBrickletRS232FrameReadableCallback0(frameCount, cfg));}}, this::removeFrameReadableListener));
		this.addErrorListener(this.reg((error) -> {if((error & 1 << 0) == 0) {triggerChannelFn.accept("BrickletRS232OverrunError", transformBrickletRS232OverrunErrorCallback0(error, cfg));}}, this::removeErrorListener));
		this.addErrorListener(this.reg((error) -> {if((error & 1 << 1) == 0) {triggerChannelFn.accept("BrickletRS232ParityError", transformBrickletRS232ParityErrorCallback0(error, cfg));}}, this::removeErrorListener));
		this.addErrorListener(this.reg((error) -> {if((error & 1 << 2) == 0) {triggerChannelFn.accept("BrickletRS232FramingError", transformBrickletRS232FramingErrorCallback0(error, cfg));}}, this::removeErrorListener));
        this.setConfiguration(cfg.baudRate.shortValue(), cfg.parity.shortValue(), cfg.stopBits.shortValue(), cfg.wordLength.shortValue(), (short)(cfg.flowControl == 1 ? 1 : 0), (short)(cfg.flowControl == 2 ? 1 : 0));
		    this.setFrameReadableCallbackConfiguration(cfg.frameSize.shortValue());
    }

    @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("BrickletRS232FrameReadable");
		result.add("BrickletRS232OverrunError");
		result.add("BrickletRS232ParityError");
		result.add("BrickletRS232FramingError");
        return result;
    }

    public static @Nullable ChannelType getChannelType(ChannelTypeUID channelTypeUID) {
        switch(channelTypeUID.getId()) {
            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 RS232 Bricklet").isListed(false).withSupportedBridgeTypeUIDs(Arrays.asList(TinkerforgeBindingConstants.THING_TYPE_BRICK_DAEMON.toString())).withConfigDescriptionURI(URI.create("thing-type:tinkerforge:" + thingTypeUID.getId())).withDescription("Communicates with RS232 devices").withChannelDefinitions(Arrays.asList(new ChannelDefinitionBuilder("BrickletRS232FrameReadable", new ChannelTypeUID("system", "trigger")).withLabel("Frame Readable").withDescription("This channel is triggered in when a new frame was received and can be read out. The channel will only trigger again if the frame was read.").build(), new ChannelDefinitionBuilder("BrickletRS232OverrunError", new ChannelTypeUID("system", "trigger")).withLabel("Overrun Error").withDescription("Triggers if an overrun error occures.").build(), new ChannelDefinitionBuilder("BrickletRS232ParityError", new ChannelTypeUID("system", "trigger")).withLabel("Parity Error").withDescription("Triggers if a parity error occures.").build(), new ChannelDefinitionBuilder("BrickletRS232FramingError", new ChannelTypeUID("system", "trigger")).withLabel("Framing Error").withDescription("Triggers if a framing error occures.").build())).withProperties(props).build();
    }

    public static @Nullable ConfigDescription getConfigDescription(URI uri) {
        switch(uri.toASCIIString()) {
            case "thing-type:tinkerforge:brickletrs232":
                return ConfigDescriptionBuilder.create(uri).withParameters(Arrays.asList(ConfigDescriptionParameterBuilder.create("baudRate", Type.INTEGER).withDefault("11").withDescription("The baud rate to send/receive with.").withLabel("Baud Rate").withLimitToOptions(true).withOptions(Arrays.asList(new ParameterOption("0", "300"), new ParameterOption("1", "600"), new ParameterOption("2", "1200"), new ParameterOption("3", "2400"), new ParameterOption("4", "4800"), new ParameterOption("5", "9600"), new ParameterOption("6", "14400"), new ParameterOption("7", "19200"), new ParameterOption("8", "28800"), new ParameterOption("9", "38400"), new ParameterOption("10", "57600"), new ParameterOption("11", "115200"), new ParameterOption("12", "230400"))).build(), ConfigDescriptionParameterBuilder.create("parity", Type.INTEGER).withDefault("0").withDescription("The parity mode to use. See here: https://en.wikipedia.org/wiki/Serial_port#Parity.").withLabel("Parity").withLimitToOptions(true).withOptions(Arrays.asList(new ParameterOption("0", "None"), new ParameterOption("1", "Odd"), new ParameterOption("2", "Even"), new ParameterOption("3", "Forced Parity 1"), new ParameterOption("4", "Forced Parity 0"))).build(), ConfigDescriptionParameterBuilder.create("stopBits", Type.INTEGER).withDefault("1").withDescription("The number of stop bits to send/expect.").withLabel("Stop Bits").withLimitToOptions(true).withOptions(Arrays.asList(new ParameterOption("1", "1"), new ParameterOption("2", "2"))).build(), ConfigDescriptionParameterBuilder.create("wordLength", Type.INTEGER).withDefault("8").withDescription("The length of a serial word. Typically one byte.").withLabel("Word Length").withLimitToOptions(true).withOptions(Arrays.asList(new ParameterOption("5", "5"), new ParameterOption("6", "6"), new ParameterOption("7", "7"), new ParameterOption("8", "8"))).build(), ConfigDescriptionParameterBuilder.create("flowControl", Type.INTEGER).withDefault("0").withDescription("The flow control mechanism to use. Software uses control characters in-band. Hardware uses the RTS and CTS lines.").withLabel("Flow Control").withLimitToOptions(true).withOptions(Arrays.asList(new ParameterOption("0", "Off"), new ParameterOption("1", "Software"), new ParameterOption("2", "Hardware"))).build(), ConfigDescriptionParameterBuilder.create("frameSize", Type.INTEGER).withDefault("1").withDescription("The size of receivable data frames in bytes. Set this to something other than 1, if you want to receive data with a fix frame size. The frame readable channel will trigger every time a frame of this size is ready to be read, but will wait until this frame is read before triggering again. This means, you can use this channel as a trigger in a rule, that will read exactly one frame.").withLabel("Frame Size").withMinimum(BigDecimal.valueOf(0)).withMaximum(BigDecimal.valueOf(100)).build())).withParameterGroups(Arrays.asList(new ConfigDescriptionParameterGroup("update_intervals", "null", true, "Update Intervals", "Update Intervals"))).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 {
        switch(channel) {
            case "BrickletRS232FrameReadable":
               return;
            case "BrickletRS232OverrunError":
               return;
            case "BrickletRS232ParityError":
               return;
            case "BrickletRS232FramingError":
               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 String transformBrickletRS232FrameReadableCallback0(short frameCount, BrickletRS232Config cfg) {
        return "";
    }
	    private String transformBrickletRS232OverrunErrorCallback0(short error, BrickletRS232Config cfg) {
        return "";
    }
	    private String transformBrickletRS232ParityErrorCallback0(short error, BrickletRS232Config cfg) {
        return "";
    }
	    private String transformBrickletRS232FramingErrorCallback0(short error, BrickletRS232Config cfg) {
        return "";
    }

}