package leigh.lighting.net;

import leigh.mecha.log.MechaLogger;
import leigh.mecha.log.MechaLoggerFactory;

import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.SocketException;
import java.nio.ByteBuffer;
import java.util.UUID;

public class SACNTransmitter {
    private static final MechaLogger logger = MechaLoggerFactory.getLogger(SACNTransmitter.class);
    private static final int E131_PACKET_LEN = 638;
    private static final int E131_SEQUENCE_OFS = 111;
    private static final int E131_UNIVERSE_ID_HI = 113;
    private static final int E131_UNIVERSE_ID_LOW = 114;
    private static final int E131_DMX_PAYLOAD_OFS = 126;
    private static final int E131_DMX_PAYLOAD_SIZE = 512;

    private final DatagramSocket ds = new DatagramSocket();
    private final InetAddress dstAddr;
    private final int dstPort;
    private final UUID controllerId = UUID.randomUUID();
    private final byte[] data = new byte[E131_PACKET_LEN];
    private final int universeNum;
    private int sequence = 0;

    public SACNTransmitter(InetAddress dstAddr, int dstPort, int universeNum) throws SocketException {
        this.dstAddr = dstAddr;
        this.dstPort = dstPort;
        this.universeNum = universeNum;

        data[0] = 0x00;
        data[1] = 0x10;
        data[2] = 0x00;
        data[3] = 0x00;
        data[4] = 0x41;
        data[5] = 0x53;
        data[6] = 0x43;
        data[7] = 0x2d;
        data[8] = 0x45;
        data[9] = 0x31;
        data[10] = 0x2e;
        data[11] = 0x31;
        data[12] = 0x37;
        data[13] = 0x00;
        data[14] = 0x00;
        data[15] = 0x00;
        data[16] = 0x72;
        data[17] = 0x6e;
        data[18] = 0x00;
        data[19] = 0x00;
        data[20] = 0x00;
        data[21] = 0x04;
        ByteBuffer pixelControllerUUID = ByteBuffer.wrap(new byte[16]);
        pixelControllerUUID.putLong(controllerId.getMostSignificantBits());
        pixelControllerUUID.putLong(controllerId.getLeastSignificantBits());
        System.arraycopy(pixelControllerUUID.array(), 0, data, 22, 16);
        data[38] = 0x72;
        data[39] = 0x58;
        data[40] = 0x00;
        data[41] = 0x00;
        data[42] = 0x00;
        data[43] = 0x02;
        data[44] = 'K';
        data[45] = 'a';
        data[46] = 'm';
        data[47] = 'i';
        data[48] = 'n';
        data[49] = 'a';
        data[50] = 'r';
        data[51] = 'i';
        data[56] = 0x00;
        data[108] = 100;
        data[109] = 0x00;
        data[110] = 0x00;
        data[111] = 0x00;
        data[112] = 0x00;
        data[115] = 0x72;
        data[116] = 0x0b;
        data[117] = 0x02;
        data[118] = (byte) 0xa1;
        data[119] = 0x00;
        data[120] = 0x00;
        data[121] = 0x00;
        data[122] = 0x01;
        data[123] = 0x02;
        data[124] = 0x01;
        data[125] = 0x00;
    }

    /**
     * MT-Safety: UNSAFE.
     */
    public void send(byte[] channels) throws IOException {
        // https://hoeser-medien.de/wp-content/uploads/ucls_e131_protocol.png

        data[E131_SEQUENCE_OFS] = (byte) (sequence % 255);
        data[E131_UNIVERSE_ID_HI] = (byte) (universeNum >> 8);
        data[E131_UNIVERSE_ID_LOW] = (byte) (universeNum & 255);

        int dataSize = E131_DMX_PAYLOAD_SIZE;
        if (channels.length < E131_DMX_PAYLOAD_SIZE) {
            dataSize = channels.length;
        }

        System.arraycopy(channels, 0, data, E131_DMX_PAYLOAD_OFS, dataSize);
        DatagramPacket dp = new DatagramPacket(data, 0, data.length, dstAddr, dstPort);
        ds.send(dp);
        sequence++;
    }
}
