import {Pane} from "./Pane";
import {Modal} from "./Modal";
import {ButtonType} from "./ButtonType";
import {WebHandler} from "./WebHandler";
import {WebSocketPayload} from "../WebSocketPayload";
import {WebSocketEvent} from "../WebSocketEvent";
import {WebWebSocketHandler} from "./WebWebSocketHandler";
import {IContainer} from "../database/IContainer";
import {SensorType} from "../SensorType";
import {RequestType} from "../RequestType";


export class Setup {

    public static arduinoProxyCheckboxes: HTMLInputElement[] = [];
    private static usingProxy = false;
    private static pins_pi = [3, 7, 8, 10, 11, 12, 13, 15, 16, 18, 19, 21, 22, 23, 24, 26, 29, 31, 32, 33, 35, 36, 37, 38];
    private static pins_arduino = [2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53];

    public static onConfigUpdate(payload: WebSocketPayload) {
        // Setup containers updated
        const ledGPIO = document.getElementById("ledGPIO") as HTMLInputElement;
        const ledCheckbox = document.getElementById("ledCheckbox") as HTMLInputElement;
        const ambientColor = document.getElementById("ambientColor") as HTMLInputElement;
        const allowRemoteCheckbox = document.getElementById("allowRemoteCheckbox") as HTMLInputElement;
        const hotspotCheckbox = document.getElementById("hotspotCheckbox") as HTMLInputElement;
        const proxyCheckbox = document.getElementById("proxyCheckbox") as HTMLInputElement;

        proxyCheckbox.checked = !!payload.data["arduino_proxy_enabled"];
        this.usingProxy = proxyCheckbox.checked;
        proxyCheckbox.onchange = () => {
            this.usingProxy = proxyCheckbox.checked;
            for (let checkbox of this.arduinoProxyCheckboxes) {
                checkbox.disabled = !proxyCheckbox.checked;
                if (!proxyCheckbox.checked) {
                    checkbox.checked = false;
                    let event = new Event('change', {bubbles: true});
                    checkbox.dispatchEvent(event); // Trigger virtual onChange event

                }
                console.log(checkbox.disabled ? "Disabled" : "Enabled", checkbox);
            }
        }

        ledCheckbox.checked = !!payload.data["led_enabled"];
        allowRemoteCheckbox.checked = !!payload.data["remote_enabled"];
        hotspotCheckbox.checked = !!payload.data["hotspot_enabled"];

        if (payload.data["led_gpio"]) {
            ledGPIO.value = payload.data["led_gpio"];
        }

        if (payload.data["ambient_color"]) {
            ambientColor.value = payload.data["ambient_color"];
        }

        (document.getElementById("setup_cancelBtn") as HTMLButtonElement).disabled = !payload.data["setupDone"];


        if (!payload.data["setupDone"]) {
            let modal = new Modal("setup", "Willkommen!");
            let txt = document.createElement("p");
            txt.innerHTML = `Dieser iTender ist noch nicht eingerichtet.<br>Um ihn zu nutzen, ist eine Grund-Konfiguration (Setup) nötig.<br><br>Das Setup lässt sich später auch jederzeit aus dem Menü erneut aufrufen.<br>Um die Einrichtung des Gerätes abzuschließen, muss mindestens ein Behälter mit Pumpe (optional auch Sensor) hinzugefügt werden.<br><br>`;
            modal.addContent(txt);
            let btn = document.createElement("button");
            btn.classList.add("btn", "btn-primary");
            btn.innerText = "Weiter";
            btn.onclick = () => {
                txt.innerHTML = `Auf der folgenden Seite können nun verschiedene Module eingerichtet werden.<br><br>
<strong>LED-Modul</strong><br>
In diesem Modul kann die Ambiente-Farbe sowie der LED-Streifen aktiviert werden.<br><br>
<strong>Erweiterte Einstellungen</strong><br>
Hier lässt sich konfigurieren, ob die Nutzung der Remote-Bedienung erlaubt ist, oder ein Hotspot aktiviert werden soll, falls keine WiFI-Verbindung vorliegt.<br>Außerdem lässt sich hier optional die Nutzung eines Arduino Mega als Proxy aktivieren. Pro Container kann dann der Proxy aktiviert werden, welcher anstatt dem Raspberry Pi die PINs steuert.<br><br>
<strong>Behälter-Modul</strong><br>
Dort werden die Behälter definiert, welche in den iTender gestellt werden.<br>Dort müssen GPIO-Pins der Pumpe, etwaige Sensoren-Typen und Pins definiert werden.<br>Ggf. steht hier auch die Proxy-Option.<br><br>`;
                btn.innerText = "Einrichtung starten";
                btn.onclick = () => {
                    modal.close();
                }
            }
            modal.addContent(btn);
            modal.open();
        }
    }

    static async openSetup() {
        // new

        WebHandler.openPane(Pane.SETUP);
        let menuBtn = document.getElementById("menuBtn") as HTMLButtonElement;
        menuBtn.disabled = true;

        const cancelBtn = document.getElementById("setup_cancelBtn") as HTMLButtonElement;
        cancelBtn.onclick = () => {
            let payload = new WebSocketPayload(WebSocketEvent.SETUP, false);
            menuBtn.disabled = false;
            WebWebSocketHandler.send(payload);
        }

        const containerAddBtn = document.getElementById("containerAddBtn") as HTMLButtonElement;
        containerAddBtn.onclick = () => {
            Setup.addSetupContainer()
        };


        const setupSaveBtn = document.getElementById("setup_saveBtn") as HTMLButtonElement;
        setupSaveBtn.onclick = async () => {
            const containers = document.getElementById("setupContainers") as HTMLDivElement;

            let errorModal = new Modal("setup", "Fehler!");
            let ele = document.createElement("p");
            ele.innerHTML = `Das Setup konnte nicht abgeschlossen werden.<br>`;
            errorModal.addContent(ele);
            errorModal.addBR();
            errorModal.addButton(ButtonType.PRIMARY, "Schließen", () => {
                errorModal.close();
            });

            if (containers.childNodes.length == 0) {
                ele.innerHTML += `Es muss mindestens ein Behälter hinzugefügt worden sein.`;
                await errorModal.open();
                return;
            }

            for (let c of containers.getElementsByTagName("div")) {
                let selects = c.getElementsByTagName("select");
                if (selects[0].value == "-1") {
                    ele.innerHTML += `Es müssen alle Pumpen-Pins gesetzt sein.`;
                    await errorModal.open();
                    c.classList.add("error");
                    setTimeout(() => {
                        c.classList.remove("error");
                    }, 2500);
                    return;
                }

                if (selects[1].value == "0") {
                    ele.innerHTML += `Die Sensorart Ultraschall wird nicht mehr unterstützt.<br>Sollten Sie diese Funktion wünschen wenden Sie sich an den Kundensupport.`;
                    await errorModal.open();
                    return;
                }

                if (selects[1].value != "-1" && (selects[2].value == "-1" || selects[3].value == "-1")) {
                    ele.innerHTML += `Wenn ein Sensor-Typ definiert ist, müssen alle Sensor-Pins gesetzt sein.`;
                    await errorModal.open();
                    c.classList.add("error");
                    setTimeout(() => {
                        c.classList.remove("error");
                    }, 2500);
                    return;
                }

            }

            if (!Setup.checkContainers()) {
                ele.innerHTML = `Einige Pins sind doppelt belegt.<br>Jeder GPIO-Pin kann nur einmal belegt werden!`;
                await errorModal.open();
                return;
            }


            const ledCheckbox = document.getElementById("ledCheckbox") as HTMLInputElement;
            const ledGPIO = document.getElementById("ledGPIO") as HTMLInputElement;
            const ambientColor = document.getElementById("ambientColor") as HTMLInputElement;
            const allowRemoteCheckbox = document.getElementById("allowRemoteCheckbox") as HTMLInputElement;
            const hotspotCheckbox = document.getElementById("hotspotCheckbox") as HTMLInputElement;
            const proxyCheckbox = document.getElementById("proxyCheckbox") as HTMLInputElement;


            // Containers
            let cons: { id: string, pumpPin: number; sensorType: SensorType; sensor1: number; sensor2: number; useProxy: boolean }[] = [];
            for (let c of (document.getElementById("setupContainers") as HTMLDivElement).getElementsByTagName("div")) {
                let sensorType = c.getElementsByTagName("select")[1].value;
                let type;
                if (sensorType == "-1")
                    type = SensorType.NONE;
                else if (sensorType == "0")
                    type = SensorType.ULTRASOUND;
                else
                    type = SensorType.LOADCELL;

                cons.push({
                    "useProxy": (c.getElementsByTagName("input")[0] as HTMLInputElement).checked,
                    "pumpPin": parseInt(c.getElementsByTagName("select")[0].value),
                    "sensorType": type,
                    "sensor1": parseInt(c.getElementsByTagName("select")[2].value),
                    "sensor2": parseInt(c.getElementsByTagName("select")[3].value),
                    "id": c.id,
                });
            }
            let newConf = {
                "led_enabled": ledCheckbox.checked,
                "remote_enabled": allowRemoteCheckbox.checked,
                "hotspot_enabled": hotspotCheckbox.checked,
                "arduino_proxy_enabled": proxyCheckbox.checked,
                "led_gpio": parseInt(ledGPIO.value),
                "ambient_color": ambientColor.value
            }

            // Check
            let answer = await WebWebSocketHandler.request(RequestType.CHECK, newConf);

            if (!(answer.data["success"] as boolean)) {
                ele.innerHTML = `Die Überprüfung schlug fehl!<br>${answer.data["msg"]}`;
                await errorModal.open();
                return;
            }

            setupSaveBtn.disabled = true;

            let saveModal = new Modal("setup", "Setup");
            let txt = document.createElement("p");
            txt.innerHTML = `Die Einstellungen werden gespeichert...<br>
<div class="lds-ellipsis"><div></div><div></div><div></div><div></div></div>`;
            saveModal.addContent(txt);
            await saveModal.open();

            // Push
            let configPayload = new WebSocketPayload(WebSocketEvent.CONFIG, newConf);


            setupSaveBtn.disabled = false;

            WebWebSocketHandler.send(configPayload).then(() => {
                let containerPayload = new WebSocketPayload(WebSocketEvent.CONTAINERS, cons);
                WebWebSocketHandler.send(containerPayload);

                setTimeout(() => {
                    saveModal.close();
                    this.startTare();
                }, 1000);

            }).catch(() => {
                txt.innerHTML = `Fehler beim Speichern.<br>iTender hat nicht reagiert.`;
                setTimeout(() => saveModal.close(), 2000);
            });


        }
    }

    public static startTare() {
        let tareModal = new Modal("tare", "Sensoren kalibrieren?");
        let txt = document.createElement("p");
        txt.innerHTML = `Damit alle Sensoren korrekte Werte liefern sollte möglichst oft eine Kalibrierung vorgenommen werden.<br>Grundsätzlich muss diese beim Einrichten neuer Sensoren ausgeführt werden und kann danach übersprungen werden.<br><br>Soll das Kalibrieren aller Sensoren gestartet werden?<br>Dieser Vorgang nimmt circa eine Minute in Anspruch.<br><br>`;
        tareModal.addContent(txt);

        tareModal.addButton(ButtonType.PRIMARY, "Nein", () => {
            tareModal.close();
            // Needs to be after selectPin.append because noSel-Pin should always be at pos 0
            let event = new Event('click', {bubbles: true});
            let btn = document.getElementById("setup_cancelBtn");
            if (btn)
                btn.dispatchEvent(event);

        });

        let ul;
        tareModal.addButton(ButtonType.PRIMARY, "Ja", async () => {
            tareModal.close();

            let modal = new Modal("tare", "Kalibrierung");

            let txt = document.createElement("p");
            txt.innerHTML = `Kalibrierung<br>
Bitte <strong>alle</strong> Behälter von den Sensoren entfernen.<br>
Die Gewichtssensoren werden beim Bestätigen austariert<br><br>Zum fortfahren Tarieren drücken.<br>`;
            modal.addContent(txt);

            ul = document.createElement("ul");
            modal.addContent(ul);

            let tareInterval: NodeJS.Timer;

            let btn = document.createElement("button");
            btn.classList.add("btn", "btn-primary");
            btn.innerText = "Tarieren";
            btn.style.marginTop = "3%";
            btn.onclick = () => {
                txt.innerHTML = `Kalibriere...<br>
<strong>Bitte warten!</strong> - Keine Behälter oder sonstige Gegenstände auf die Sensoren stellen!<br>
Die Gewichtssensoren werden gerade austariert...<br>`;
                btn.disabled = true;
                btn.innerText = "Abschließen"

                WebWebSocketHandler.request(RequestType.TARE, true).then((result) => {
                    let data: { success: boolean, msg: string } = result.data;
                    btn.disabled = false;
                    if (data.success) {
                        txt.innerHTML = `Kalibrierung abgeschlossen!<br>
Alle Sensoren wurden erfolgreich kalibriert.<br>`;
                        btn.onclick = () => {
                            modal.close();
                            clearInterval(tareInterval);
                            let event = new Event('click', {bubbles: true});
                            let btn = document.getElementById("setup_cancelBtn");
                            if (btn)
                                btn.dispatchEvent(event);
                        };
                    } else {
                        btn.innerText = "Erneut versuchen";
                        txt.innerHTML = `Kalibrierung fehlgeschlagen!<br>
Mindestens ein Sensor konnte nicht kalibriert werden.<br>${data.msg}<br>`;
                    }
                });
            };

            modal.addContent(btn);
            await modal.open();

        });

        tareModal.open();

    }

    public static addSetupContainer(container?: IContainer) {
        let setupContainers = document.getElementById("setupContainers") as HTMLDivElement;

        // Div
        let containerDiv = document.createElement("div");
        if (container)
            containerDiv.id = container._id;


        // Name
        let containerName = document.createElement("p");
        containerName.innerText = "Behälter " + (setupContainers.getElementsByTagName("div").length + 1);
        containerDiv.classList.add("setupContainer");
        containerDiv.append(containerName);

        // General Pin Select
        let selectPin = document.createElement("select");
        selectPin.style.display = "none";
        selectPin.classList.add("input");
        selectPin.style.display = "inline";
        // NoSelect in Pin Select
        let noSel = document.createElement("option") as HTMLOptionElement;
        noSel.innerText = "Bitte wählen";
        noSel.value = "-1";
        noSel.disabled = true;

        // Proxy Label
        let proxyLabel = document.createElement("label");
        proxyLabel.innerText = "Arduino als Proxy";
        proxyLabel.style.display = "block";
        proxyLabel.style.marginBottom = "1%";
        containerDiv.append(proxyLabel);
        // Proxy checkbox

        let proxyCheckbox = document.createElement("input");
        proxyCheckbox.type = "checkbox";
        proxyCheckbox.classList.add("input");
        proxyCheckbox.style.marginLeft = "3%";

        proxyCheckbox.checked = !!(Setup.usingProxy && container?.useProxy); // Auto Check
        proxyCheckbox.disabled = !(Setup.usingProxy);
        this.arduinoProxyCheckboxes.push(proxyCheckbox);

        proxyLabel.append(proxyCheckbox);

        containerDiv.append(document.createElement("br"));

        // Pump Pin Selector
        let pumpPinSelect;
        let pumpLabel = document.createElement("label");
        pumpLabel.innerText = "Pumpen Pin";
        containerDiv.append(pumpLabel);
        pumpPinSelect = selectPin.cloneNode(true);
        pumpPinSelect.onchange = () => Setup.checkContainers();
        pumpPinSelect.selectedIndex = 0;
        pumpLabel.append(pumpPinSelect);

        containerDiv.append(document.createElement("br"));


        // Sensor Type Label
        let sensorTypeLabel = document.createElement("label");
        sensorTypeLabel.innerText = "Sensor Art ";
        containerDiv.append(sensorTypeLabel);

        // Sensor Type Select
        let sensorType = document.createElement("select");
        sensorType.classList.add("noCheckup");
        sensorType.classList.add("input");
        let sensorTypeNone = document.createElement("option") as HTMLOptionElement;
        sensorTypeNone.innerText = "Kein Sensor";
        sensorTypeNone.value = "-1";
        sensorType.append(sensorTypeNone);
        let sensorTypeUltrasound = document.createElement("option") as HTMLOptionElement;
        sensorTypeUltrasound.innerText = "Ultraschall";
        sensorTypeUltrasound.value = "0";
        //sensorType.append(sensorTypeUltrasound); // V2: Removed
        let sensorTypeScale = document.createElement("option") as HTMLOptionElement;
        sensorTypeScale.innerText = "Wägezelle";
        sensorTypeScale.value = "1";
        sensorType.append(sensorTypeScale);
        sensorTypeLabel.append(sensorType);
        containerDiv.append(document.createElement("br"));


        // Sensor 1 Label
        let sensor1Label = document.createElement("label");
        sensor1Label.innerText = "Sensor 1 Pin";
        containerDiv.append(sensor1Label);
        // Sensor 1 Pin Select
        let sensor1Select = selectPin.cloneNode(true) as HTMLSelectElement;
        sensor1Select.selectedIndex = 0;
        sensor1Select.disabled = true;
        sensor1Select.onchange = () => Setup.checkContainers();
        containerDiv.append(sensor1Select);

        containerDiv.append(document.createElement("br"));

        // Sensor 2 Label
        let sensor2Label = document.createElement("label");
        sensor2Label.innerText = "Sensor 2 Pin";
        containerDiv.append(sensor2Label);
        // Sensor 2 Pin Select
        let sensor2Select = selectPin.cloneNode(true) as HTMLSelectElement;
        sensor2Select.selectedIndex = 0;
        sensor2Select.disabled = true;
        sensor2Select.onchange = () => Setup.checkContainers();
        containerDiv.append(sensor2Select);

        sensorType.onchange = () => {
            if (sensorType.value == "0") {
                sensor1Label.innerText = "Trigger Pin";
                sensor2Label.innerText = "Trigger Pin";
                sensor1Select.disabled = false;
                sensor2Select.disabled = false;
            } else if (sensorType.value == "1") {
                sensor1Label.innerText = "Clock Pin";
                sensor2Label.innerText = "Data Pin";
                sensor1Select.disabled = false;
                sensor2Select.disabled = false;
            } else {
                sensor1Label.innerText = "Sensor 1 Pin";
                sensor2Label.innerText = "Sensor 2 Pin";
                sensor1Select.disabled = true;
                sensor2Select.disabled = true;
            }
        };
        containerDiv.append(document.createElement("br"));


        // Proxy On change needs to be at the end to get all vars
        proxyCheckbox.onchange = () => {
            // Clear pins
            pumpPinSelect.innerHTML = '';
            sensor1Select.innerHTML = '';
            sensor2Select.innerHTML = '';

            // Set no-val
            pumpPinSelect.append(noSel.cloneNode(true));
            sensor1Select.append(noSel.cloneNode(true));
            sensor2Select.append(noSel.cloneNode(true));
            pumpPinSelect.selectedIndex = 0;
            sensor1Select.selectedIndex = 0;
            sensor2Select.selectedIndex = 0;
            // Add pins
            for (let pin of (proxyCheckbox.checked ? Setup.pins_arduino : Setup.pins_pi)) {
                let pinEle = document.createElement("option") as HTMLOptionElement;
                pinEle.innerText = "" + pin;
                pinEle.value = "" + pin;
                pumpPinSelect.append(pinEle.cloneNode(true));
                sensor1Select.append(pinEle.cloneNode(true));
                sensor2Select.append(pinEle.cloneNode(true));
            }
        };
        // Needs to be after selectPin.append because noSel-Pin should always be at pos 0
        let event = new Event('change', {bubbles: true});
        proxyCheckbox.dispatchEvent(event); // Trigger virtual onChange event

        // Auto selects (if container is given as arg) needs to be after the event, because the pin selectors may not there
        if (container) {
            pumpPinSelect.value = String(container.pumpPin);
            sensor1Select.value = String(container.sensorPin1);
            sensor2Select.value = String(container.sensorPin2);
        }


        let removeBtn = document.createElement("button");
        removeBtn.classList.add("btn", "btn-danger");

        removeBtn.onclick = () => {
            containerDiv.classList.add("removeSlowly");
            setTimeout(() => {
                containerDiv.remove();
                let i = 1;
                for (let elementsByTagNameElement of setupContainers.getElementsByTagName("div")) {
                    let e = elementsByTagNameElement.getElementsByTagName("p")[0] as HTMLParagraphElement;
                    e.innerText = "Behälter " + i + (containerDiv.id.length != 0 ? " [" + containerDiv.id.substring(containerDiv.id.length - 3) + "]" : "");
                    i++;
                }
            }, 750);

        }
        removeBtn.style.float = "right";
        removeBtn.innerText = "Löschen";
        containerDiv.append(removeBtn);

        setupContainers.append(containerDiv);


    }

    public static onContainerUpdate(payload: WebSocketPayload) {
        let containerDiv = document.getElementById("setupContainers") as HTMLDivElement;
        containerDiv.innerHTML = "";

        let containers = payload.data as IContainer[];
        for (let c of containers) {
            Setup.addSetupContainer(c);
        }
    }


    public static checkContainers(): boolean {
        console.log("Checking containers...");
        let returner = true;


        const containers = document.getElementById("setupContainers") as HTMLDivElement;
        let setupContainers = containers.getElementsByTagName("div");

        for (let c of setupContainers) {
            for (let c2 of setupContainers) {
                for (let sel of c.getElementsByTagName("select")) {
                    if (sel.value == "-1") continue; // maybe this shit

                    if (sel.classList.contains("noCheckup")) continue;
                    if (sel.disabled) continue;

                    let cIsProxy: HTMLInputElement = c.getElementsByTagName("input")[0];

                    for (let sel2 of c2.getElementsByTagName("select")) {
                        if (sel2.value == "-1") continue;
                        if (sel2.disabled) continue;
                        if (sel == sel2) continue;
                        if (sel2.classList.contains("noCheckup")) continue;

                        let c2IsProxy: HTMLInputElement = c2.getElementsByTagName("input")[0];


                        // Check if the two pins are the same, but also checks if one is using proxy, or another one is not using it
                        if (sel.value == sel2.value && cIsProxy.checked == c2IsProxy.checked) {
                            c.classList.add("error");
                            c2.classList.add("error");
                            sel.classList.add("error");
                            sel2.classList.add("error");
                            setTimeout(() => {
                                c.classList.remove("error");
                                c2.classList.remove("error");
                                setTimeout( () => {
                                    sel.classList.remove("error");
                                    sel2.classList.remove("error");
                                }, 1500 );

                            }, 2200);
                            returner = false;
                        }
                    }
                }
            }
        }
        return returner;
    }
}