/**
 * 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 com.tinkerforge.BrickletPiezoSpeakerV2;
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 Piezo Speaker Bricklet 2.0.
 * @author Erik Fleckstein - Initial contribution
 */
@NonNullByDefault
public class BrickletPiezoSpeakerV2Wrapper extends BrickletPiezoSpeakerV2 implements CoMCUFlashable, DeviceWrapper {
    public static final DeviceInfo DEVICE_INFO = new DeviceInfo(BrickletPiezoSpeakerV2.DEVICE_DISPLAY_NAME, "brickletpiezospeakerv2", BrickletPiezoSpeakerV2.DEVICE_IDENTIFIER, BrickletPiezoSpeakerV2Wrapper.class, BrickletPiezoSpeakerV2Actions.class, "2.0.0", true);

    public BrickletPiezoSpeakerV2Wrapper(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(BrickletPiezoSpeakerV2Wrapper.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);
        BrickletPiezoSpeakerV2Config cfg = (BrickletPiezoSpeakerV2Config) config.as(BrickletPiezoSpeakerV2Config.class);
        this.addBeepFinishedListener(this.reg(() -> {if(true) {updateStateFn.accept("BrickletPiezoSpeakerV2Beep", transformBrickletPiezoSpeakerV2BeepCallback0(cfg));}}, this::removeBeepFinishedListener));
		this.addAlarmFinishedListener(this.reg(() -> {if(true) {updateStateFn.accept("BrickletPiezoSpeakerV2Alarm", transformBrickletPiezoSpeakerV2AlarmCallback0(cfg));}}, this::removeAlarmFinishedListener));
        
		this.setStatusLEDConfig(cfg.statusLEDConfig);
    }

    @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("BrickletPiezoSpeakerV2Beep");
		result.add("BrickletPiezoSpeakerV2Alarm");
		result.add("BrickletPiezoSpeakerV2Volume");
		result.add("BrickletPiezoSpeakerV2BeepFrequency");
        return result;
    }

    public static @Nullable ChannelType getChannelType(ChannelTypeUID channelTypeUID) {
        switch(channelTypeUID.getId()) {
            case "BrickletPiezoSpeakerV2Beep":
                return ChannelTypeBuilder.state(new ChannelTypeUID("tinkerforge", "BrickletPiezoSpeakerV2Beep"), "Beep", "Switch").withConfigDescriptionURI(URI.create("channel-type:tinkerforge:BrickletPiezoSpeakerV2Beep")).withDescription("Beeps with the configured default frequency and volume for the configured duration. Frequency and Volume can be updated using the corresponding channels.").build();
            case "BrickletPiezoSpeakerV2Alarm":
                return ChannelTypeBuilder.state(new ChannelTypeUID("tinkerforge", "BrickletPiezoSpeakerV2Alarm"), "Alarm", "Switch").withConfigDescriptionURI(URI.create("channel-type:tinkerforge:BrickletPiezoSpeakerV2Alarm")).withDescription("Creates an alarm (a tone that goes back and force between two specified frequencies). The following parameters can be set:<br/><br/><ul><li>Start Frequency: Start frequency of the alarm in Hz.</li><li>End Frequency: End frequency of the alarm in Hz.</li><li>Step Size: Size of one step of the sweep between the start/end frequencies in Hz.</li><li>Step Delay: Delay between two steps (duration of time that one tone is used in a sweep) in ms.</li><li>Duration: Duration of the alarm in ms.</li></ul><br/><br/>A duration of 0 stops the current alarm if any is ongoing. A duration of 4294967295 results in an infinite alarm.").build();
            case "BrickletPiezoSpeakerV2Volume":
                return ChannelTypeBuilder.state(new ChannelTypeUID("tinkerforge", "BrickletPiezoSpeakerV2Volume"), "Volume", "Number:Dimensionless").withConfigDescriptionURI(URI.create("channel-type:tinkerforge:BrickletPiezoSpeakerV2Volume")).withDescription("Volume of an ongoing beep or alarm. The range of the volume is 0 to 10.")
.withStateDescription(StateDescriptionFragmentBuilder.create().withMinimum(BigDecimal.valueOf(0)).withMaximum(BigDecimal.valueOf(10)).withStep(BigDecimal.valueOf(1)).withPattern("%d").withReadOnly(false).build().toStateDescription()).build();
            case "BrickletPiezoSpeakerV2BeepFrequency":
                return ChannelTypeBuilder.state(new ChannelTypeUID("tinkerforge", "BrickletPiezoSpeakerV2BeepFrequency"), "Beep Frequency", "Number:Frequency").withConfigDescriptionURI(URI.create("channel-type:tinkerforge:BrickletPiezoSpeakerV2BeepFrequency")).withDescription("Frequency of an ongoing beep. The range of the frequency is 50Hz to 15000Hz.")
.withStateDescription(StateDescriptionFragmentBuilder.create().withMinimum(BigDecimal.valueOf(50)).withMaximum(BigDecimal.valueOf(15000)).withStep(BigDecimal.valueOf(1)).withPattern("%d %unit%").withReadOnly(false).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 Piezo Speaker Bricklet 2.0").isListed(false).withSupportedBridgeTypeUIDs(Arrays.asList(TinkerforgeBindingConstants.THING_TYPE_BRICK_DAEMON.toString())).withConfigDescriptionURI(URI.create("thing-type:tinkerforge:" + thingTypeUID.getId())).withDescription("Creates beep and alarm with configurable volume and frequency").withChannelDefinitions(Arrays.asList(new ChannelDefinitionBuilder("BrickletPiezoSpeakerV2Beep", new ChannelTypeUID("tinkerforge", "BrickletPiezoSpeakerV2Beep")).withLabel("Beep").build(), new ChannelDefinitionBuilder("BrickletPiezoSpeakerV2Alarm", new ChannelTypeUID("tinkerforge", "BrickletPiezoSpeakerV2Alarm")).withLabel("Alarm").build(), new ChannelDefinitionBuilder("BrickletPiezoSpeakerV2Volume", new ChannelTypeUID("tinkerforge", "BrickletPiezoSpeakerV2Volume")).withLabel("Volume").build(), new ChannelDefinitionBuilder("BrickletPiezoSpeakerV2BeepFrequency", new ChannelTypeUID("tinkerforge", "BrickletPiezoSpeakerV2BeepFrequency")).withLabel("Beep Frequency").build())).withProperties(props).build();
    }

    public static @Nullable ConfigDescription getConfigDescription(URI uri) {
        switch(uri.toASCIIString()) {
            case "thing-type:tinkerforge:brickletpiezospeakerv2":
                return ConfigDescriptionBuilder.create(uri).withParameters(Arrays.asList(ConfigDescriptionParameterBuilder.create("statusLEDConfig", Type.INTEGER).withDefault("3").withDescription("The status LED configuration. By default the LED shows communication traffic between Brick and Bricklet, it flickers once for every 10 received data packets.<br/><br/>You can also turn the LED permanently on/off or show a heartbeat.<br/><br/>If the Bricklet is in bootloader mode, the LED is will show heartbeat by default.").withLabel("Status LED Configuration").withAdvanced(true).withLimitToOptions(true).withOptions(Arrays.asList(new ParameterOption("0", "Off"), new ParameterOption("1", "On"), new ParameterOption("2", "Show Heartbeat"), new ParameterOption("3", "Show Status"))).build())).withParameterGroups(Arrays.asList(new ConfigDescriptionParameterGroup("update_intervals", "null", true, "Update Intervals", "Update Intervals"))).build();
            case "channel-type:tinkerforge:BrickletPiezoSpeakerV2Beep":
                return ConfigDescriptionBuilder.create(uri).withParameters(Arrays.asList(ConfigDescriptionParameterBuilder.create("defaultFrequency", Type.INTEGER).withDefault("50").withDescription("The frequency in Hz to start the beep with. The range of the frequency is 50Hz to 15000Hz.").withLabel("Default Frequency").withMinimum(BigDecimal.valueOf(50)).withMaximum(BigDecimal.valueOf(15000)).build(), ConfigDescriptionParameterBuilder.create("defaultVolume", Type.INTEGER).withDefault("0").withDescription("The volume to start the beep with. The range of the volume is 0 to 10.").withLabel("Default Volume").withMinimum(BigDecimal.valueOf(0)).withMaximum(BigDecimal.valueOf(10)).build(), ConfigDescriptionParameterBuilder.create("duration", Type.DECIMAL).withDefault("0").withDescription("The duration in ms to beep for. A duration of 0 stops the current beep if any is ongoing. A duration of 4294967295 results in an infinite beep.").withLabel("Duration").withStepSize(BigDecimal.valueOf(1)).build())).withParameterGroups(Arrays.asList()).build();
            case "channel-type:tinkerforge:BrickletPiezoSpeakerV2Alarm":
                return ConfigDescriptionBuilder.create(uri).withParameters(Arrays.asList(ConfigDescriptionParameterBuilder.create("startFrequency", Type.INTEGER).withDefault("250").withDescription("Start frequency of the alarm in Hz. The range of the start frequency is 50Hz to 14999Hz. (has to be smaller than end frequency)").withLabel("Start Frequency").withMinimum(BigDecimal.valueOf(50)).withMaximum(BigDecimal.valueOf(14999)).build(), ConfigDescriptionParameterBuilder.create("endFrequency", Type.INTEGER).withDefault("750").withDescription("End frequency of the alarm in Hz. The range of the end frequency is 51Hz to 15000Hz. (has to be bigger than start frequency)").withLabel("End Frequency").withMinimum(BigDecimal.valueOf(51)).withMaximum(BigDecimal.valueOf(15000)).build(), ConfigDescriptionParameterBuilder.create("stepSize", Type.INTEGER).withDefault("1").withDescription("Size of one step of the sweep between the start/end frequencies in Hz. 1Hz - 65536Hz (has to be small enough to fit into the frequency range)").withLabel("Step Size").withMinimum(BigDecimal.valueOf(0)).withMaximum(BigDecimal.valueOf(14950)).build(), ConfigDescriptionParameterBuilder.create("stepDelay", Type.INTEGER).withDefault("1").withDescription("Delay between two steps (duration of time that one tone is used in a sweep) in ms. 1ms - 65535ms (has to be small enough to fit into the duration)").withLabel("Step Delay").withMinimum(BigDecimal.valueOf(0)).withMaximum(BigDecimal.valueOf(65535)).build(), ConfigDescriptionParameterBuilder.create("defaultVolume", Type.INTEGER).withDefault("0").withDescription("The volume to start the alarm with. The range of the volume is 0 to 10.").withLabel("Default Volume").withMinimum(BigDecimal.valueOf(0)).withMaximum(BigDecimal.valueOf(10)).build(), ConfigDescriptionParameterBuilder.create("duration", Type.INTEGER).withDefault("0").withDescription("The duration in ms to sound the alarm for. A duration of 0 stops the current alarm if any is ongoing. A duration of 4294967295 results in an infinite alarm.").withLabel("Duration").build())).withParameterGroups(Arrays.asList()).build();
            case "channel-type:tinkerforge:BrickletPiezoSpeakerV2Volume":
                return ConfigDescriptionBuilder.create(uri).withParameters(Arrays.asList()).withParameterGroups(Arrays.asList()).build();
            case "channel-type:tinkerforge:BrickletPiezoSpeakerV2BeepFrequency":
                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 {
        BrickletPiezoSpeakerV2Config cfg = (BrickletPiezoSpeakerV2Config) config.as(BrickletPiezoSpeakerV2Config.class);
switch(channel) {
            case "BrickletPiezoSpeakerV2Beep":
                   {
            BrickletPiezoSpeakerV2.Beep value = this.getBeep();
            if(true)
                updateStateFn.accept(channel, transformBrickletPiezoSpeakerV2BeepGetter0(value, cfg));
        }
                   return;
            case "BrickletPiezoSpeakerV2Alarm":
                   {
            BrickletPiezoSpeakerV2.Alarm value = this.getAlarm();
            if(true)
                updateStateFn.accept(channel, transformBrickletPiezoSpeakerV2AlarmGetter0(value, cfg));
        }
                   return;
            case "BrickletPiezoSpeakerV2Volume":
                   {
            BrickletPiezoSpeakerV2.Beep value = this.getBeep();
            if(value.durationRemaining > 0)
                updateStateFn.accept(channel, transformBrickletPiezoSpeakerV2VolumeGetter0(value, cfg));
        }
                   {
            BrickletPiezoSpeakerV2.Alarm value = this.getAlarm();
            if(value.durationRemaining > 0)
                updateStateFn.accept(channel, transformBrickletPiezoSpeakerV2VolumeGetter1(value, cfg));
        }
                   return;
            case "BrickletPiezoSpeakerV2BeepFrequency":
                   {
            BrickletPiezoSpeakerV2.Beep value = this.getBeep();
            if(value.durationRemaining > 0)
                updateStateFn.accept(channel, transformBrickletPiezoSpeakerV2BeepFrequencyGetter0(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 "BrickletPiezoSpeakerV2Beep":
                if (command instanceof OnOffType) {
                    BrickletPiezoSpeakerV2BeepConfig channelCfg = channelConfig.as(BrickletPiezoSpeakerV2BeepConfig.class);
OnOffType cmd = (OnOffType) command;
this.setBeep(channelCfg.defaultFrequency, channelCfg.defaultVolume, cmd == OnOffType.ON ? channelCfg.duration.longValue() : 0);
                }
        
                else {
                    logger.warn("Command type {} not supported for channel {}. Please use one of OnOffType.", command.getClass().getName(), channel);
                }
                result.add(new SetterRefresh("BrickletPiezoSpeakerV2Volume", 0));
				result.add(new SetterRefresh("BrickletPiezoSpeakerV2BeepFrequency", 0));
                break;
            case "BrickletPiezoSpeakerV2Alarm":
                if (command instanceof OnOffType) {
                    BrickletPiezoSpeakerV2AlarmConfig channelCfg = channelConfig.as(BrickletPiezoSpeakerV2AlarmConfig.class);
OnOffType cmd = (OnOffType) command;
this.setAlarm(channelCfg.startFrequency, channelCfg.endFrequency, channelCfg.stepSize, channelCfg.stepDelay, channelCfg.defaultVolume, cmd == OnOffType.ON ? channelCfg.duration : 0);
                }
        
                else {
                    logger.warn("Command type {} not supported for channel {}. Please use one of OnOffType.", command.getClass().getName(), channel);
                }
                result.add(new SetterRefresh("BrickletPiezoSpeakerV2Volume", 0));
                break;
            case "BrickletPiezoSpeakerV2Volume":
                if (command instanceof Number) {
                    Number cmd = (Number) command;
this.updateVolume(cmd.intValue());
                }
        
                else {
                    logger.warn("Command type {} not supported for channel {}. Please use one of Number.", command.getClass().getName(), channel);
                }
                
                break;
            case "BrickletPiezoSpeakerV2BeepFrequency":
                if (command instanceof Number) {
                    Number cmd = (Number) command;
this.updateFrequency(cmd.intValue());
                }
        
                else {
                    logger.warn("Command type {} not supported for channel {}. Please use one of Number.", command.getClass().getName(), channel);
                }
                
                break;
default:
                logger.warn("Command for unknown channel {}", channel);
        }
        return result;
    }

        private org.eclipse.smarthome.core.types.State transformBrickletPiezoSpeakerV2BeepCallback0(BrickletPiezoSpeakerV2Config cfg) {
        return OnOffType.OFF;
    }
	    private org.eclipse.smarthome.core.types.State transformBrickletPiezoSpeakerV2AlarmCallback0(BrickletPiezoSpeakerV2Config cfg) {
        return OnOffType.OFF;
    }
	    private org.eclipse.smarthome.core.types.State transformBrickletPiezoSpeakerV2BeepGetter0(BrickletPiezoSpeakerV2.Beep value, BrickletPiezoSpeakerV2Config cfg) {
        return value.durationRemaining > 0 ? OnOffType.ON : OnOffType.OFF;
    }
	    private org.eclipse.smarthome.core.types.State transformBrickletPiezoSpeakerV2AlarmGetter0(BrickletPiezoSpeakerV2.Alarm value, BrickletPiezoSpeakerV2Config cfg) {
        return value.durationRemaining > 0 ? OnOffType.ON : OnOffType.OFF;
    }
	    private org.eclipse.smarthome.core.types.State transformBrickletPiezoSpeakerV2VolumeGetter0(BrickletPiezoSpeakerV2.Beep value, BrickletPiezoSpeakerV2Config cfg) {
        return new DecimalType(value.volume);
    }
	    private org.eclipse.smarthome.core.types.State transformBrickletPiezoSpeakerV2VolumeGetter1(BrickletPiezoSpeakerV2.Alarm value, BrickletPiezoSpeakerV2Config cfg) {
        return new DecimalType(value.volume);
    }
	    private org.eclipse.smarthome.core.types.State transformBrickletPiezoSpeakerV2BeepFrequencyGetter0(BrickletPiezoSpeakerV2.Beep value, BrickletPiezoSpeakerV2Config cfg) {
        return new QuantityType<>(value.frequency, SmartHomeUnits.HERTZ);
    }

}