package de.ctdo.bunti.model;

import de.ctdo.bunti.dmx.DMX;
import de.ctdo.bunti.dmx.DMXChannel;
import de.ctdo.bunti.dmx.DMXChannels;
import org.codehaus.jackson.annotate.JsonIgnore;

import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;

public abstract class BuntiDMXDevice extends BuntiDevice {
    private int startAddress;
    private final DMXChannels dmxChannels = new DMXChannels();

    public BuntiDMXDevice(int deviceId, int startAddress, String name) {
        super(deviceId, name);
        setStartAddress(startAddress);
    }

    /**
     * Gets the DMX start address for this device
     * @return The address value from 1 to max 512
     */
    public final int getStartAddress() {
        return startAddress;
    }

    /**
     * Gets the DMX start address for this device
     * @param startAddress The address value from 1 to max 512
     * @return True on success, false if start address is wrong
     */
    public final boolean setStartAddress(int startAddress) {
        if(startAddress < DMX.DMX_CHANNEL_INDEX_MIN ||
            startAddress - 1 + dmxChannels.getCount() > DMX.DMX_CHANNEL_INDEX_MAX) {
            return false;
        }
        this.startAddress = startAddress;
        return true;
    }

    /**
     * Set channel value by given channel name.
     *
     * @param name  The channel name to change the value.
     * @param value The channel value to set.
     * @return True on success, otherwise false
     */
    protected final boolean setChannelValueByName(String name, int value) {
        DMXChannel dx = dmxChannels.getChannelByName(name);
        if (dx != null) {
            dx.setValue(DMX.sanitizeDMXValue(value));
            return true;
        }
        return false;
    }

    /**
     * Returns the channel value identified by channel name.
     *
     * @param name The channel name to get the value from.
     * @return The desired channel value.
     */
    protected final int getChannelValueByName(String name) {
        DMXChannel dx = dmxChannels.getChannelByName(name);
        if (dx != null) {
            return dx.getValue();
        }
        return 0;
    }

    /**
     * Collect the DMX Channel Data with correct DMX512 calculated offsets
     * @return The channel data with startaddress+offset of every channel
     */
    @JsonIgnore
    public Map<Integer, Integer> getChannelData() {
        Map<Integer, Integer> map = new HashMap<Integer, Integer>();

        for (DMXChannel channel : dmxChannels.getAllChannels()) {
            int index = channel.getOffset() + startAddress;

//            if (index >= DMX.DMX_CHANNEL_INDEX_MIN && index <= DMX.DMX_CHANNEL_INDEX_MAX) {
                map.put(index, channel.getValue());
//            }
        }

        return map;
    }

    @Override
    public final boolean setValuesFromOptions(Map<String, Object> options) {

        for (Entry<String, Object> opt : options.entrySet()) {
            try {
                int value = Integer.parseInt(opt.getValue().toString());

                if (!setChannelValueByName(opt.getKey(), value)) {
                    return false;
                }
            } catch (Exception e) {
                return false;
            }
        }
        return true;
    }

    /**
     * Add a channel to this DMX Device
     * used internally by subclasses to define their structure
     * @param channel DMXChannel to add (name and offset)
     * @return True on success, false otherwise.
     */
    public final boolean addChannel(DMXChannel channel) {
        return dmxChannels.addChannel(channel);
    }
}
