package de.ctdo.bunti.dmx;

import de.ctdo.bunti.DeviceChangedEvent;
import de.ctdo.bunti.artnet.SimpleArtNetSender;
import de.ctdo.bunti.model.BuntiDMXDevice;
import de.ctdo.bunti.model.BuntiDevice;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationListener;
import org.springframework.scheduling.annotation.Scheduled;

import java.util.Collections;
import java.util.HashMap;
import java.util.Map;

public class DMXMixerImpl implements DMXMixer, ApplicationListener<DeviceChangedEvent> {
    private static final Logger LOGGER = LoggerFactory.getLogger(DMXMixerImpl.class);
    private static final int NET_SEND_INTERVAL = 100;
    private String artNetDeviceAddress;

    private final Map<Integer, Integer> dmxMap = Collections.synchronizedMap(new HashMap<Integer, Integer>());
    private SimpleArtNetSender artNetSender;
    private boolean hasDataChanged = true;

    @Autowired
    public final void setArtNetSender(SimpleArtNetSender artNetSender) {
        this.artNetSender = artNetSender;
    }

    public void setArtNetDeviceAddress(String artNetDeviceAddress) {
        this.artNetDeviceAddress = artNetDeviceAddress;
    }

    private void initDMXData() {
        for (int i = DMX.DMX_CHANNEL_INDEX_MIN; i <= DMX.DMX_CHANNEL_INDEX_MAX; i++) {
            dmxMap.put(i, DMX.DMX_CHANNEL_VALUE_MIN);
        }
    }

    @Scheduled(fixedDelay = NET_SEND_INTERVAL)
    public final void sendOutDMXBuffer() {
        if (dmxMap.size() == 0) {
            initDMXData();
        }

        if (hasDataChanged) {
            artNetSender.sendDMXData(dmxMap, artNetDeviceAddress);
            hasDataChanged = false;
        }
    }

    @Override
    public final boolean updateDevice(BuntiDevice device, Map<String, Object> options) {
        if(device == null || options == null || options.size() == 0) {
            return false;
        }

        BuntiDMXDevice dmxDev = (BuntiDMXDevice) device;

        if (dmxDev.setValuesFromOptions(options)) {

            dmxMap.putAll(dmxDev.getChannelData());

            LOGGER.info("setValuesFromOptions on " + device);
            return true;
        }

        LOGGER.info("setValuesFromOptions on " + device + " failed");
        return false;
    }

    @Override
    public final boolean setDMX512Channel(int channel, int value) {
        if (!DMX.checkChannelBoundaries(channel)) {
            return false;
        }

        dmxMap.put(channel, DMX.sanitizeDMXValue(value));
        hasDataChanged = true;
        return true;
    }

    @Override
    public final void onApplicationEvent(DeviceChangedEvent arg0) {
        if (arg0.getDevice() instanceof BuntiDMXDevice) {
            updateDevice(arg0.getDevice(), arg0.getOptions());

            hasDataChanged = true;
            // TODO: hier kann man z.B. auch noch direkt einmal die DMX Daten zu verschicken veranlassen
        }
    }


}
