import {
    cellHeight,
    cellWidth,
    cols,
    getScreenHeight,
    getScreenWidth,
    pointerClickX,
    pointerClickY, resizeTerritory,
    rows
} from "./canvas.js";
import {addGrainsToHamster, getActorDirection, hamster} from "./hamster.js";
import {changeFontSize, changeTheme} from "./cmEditor.js";
import {validateAndSave} from "./saveAndLoad.js";

const defaultFontSize = 12;
let changeTerritorySizeDialog;
let addGrainsTerritoryDialog;
let addGrainsHamsterDialog;
let readNumberDialog;
let readStringDialog;
let writeStringDialog;
let errorDialog;
let saveFilesDialog;
let changeFontSizeDialog;
let changeThemeDialog;
export let errorDialogOpen;
let territoryTooltip;
let territoryTooltipOpen;
let grainsInField;
let grainsInMouth;
let hamsterDirection;
export let rowInput;
export let colInput;
let numberOfGrainsTerritoryInput;
export let numberOfGrainsHamsterInput;
let readNumberInput;
let readStringInput;
let writeStringOutput;
let fontSizeInput;
let closeErrorDialogOkButton;
let changeSizeDialogOkButton;
let changeSizeDialogCancelButton;
let addGrainsHamsterDialogOkButton;
let addGrainsHamsterDialogCancelButton;
let saveFilesDialogSaveButton;
let saveFilesDialogCancelButton;
let changeFontSizeDialogOkButton;
let changeFontSizeDialogCancelButton;
let changeThemeDialogOkButton;
let changeThemeDialogCancelButton;
let changeSizeForm;
let addGrainsTerritoryForm;
let addGrainsHamsterForm;
let readNumberForm;
let readStringForm;
let writeStringForm;
let errorForm;
let saveFileForm;
let changeFontSizeForm;
let changeThemeForm;

/**
 * Initialisiert alle Dialog-Fenster der Anwendung.
 */
export function initDialogs() {
    grainsInField = document.getElementById('grains-in-field');
    grainsInMouth = document.getElementById('grains-in-mouth');
    hamsterDirection = document.getElementById('hamster-direction');
    changeTerritorySizeDialog = document.getElementById("change-size-dialog");
    addGrainsTerritoryDialog = document.getElementById('add-grains-territory-dialog');
    addGrainsHamsterDialog = document.getElementById('add-grains-hamster-dialog');
    readNumberDialog = document.getElementById('read-number-dialog');
    readStringDialog = document.getElementById('read-string-dialog');
    writeStringDialog = document.getElementById('write-string-dialog');
    errorDialog = document.getElementById('error-dialog');
    saveFilesDialog = document.getElementById('save-files-dialog');
    changeFontSizeDialog = document.getElementById('change-font-size-dialog');
    changeThemeDialog = document.getElementById('change-theme-dialog');
    rowInput = document.getElementById('row-input');
    colInput = document.getElementById('col-input');
    numberOfGrainsTerritoryInput = document.getElementById('number-of-grains-territory-input');
    numberOfGrainsHamsterInput = document.getElementById('number-of-grains-hamster-input');
    readNumberInput = document.getElementById('read-number-input');
    readStringInput = document.getElementById('read-string-input');
    fontSizeInput = document.getElementById('font-size-input');
    writeStringOutput = document.getElementById('write-string-output');
    territoryTooltip = document.getElementById('territory-tooltip');
    closeErrorDialogOkButton = document.getElementById('close-error-dialog-ok-button');
    changeSizeDialogOkButton = document.getElementById('change-size-dialog-ok-button');
    changeSizeDialogCancelButton = document.getElementById('change-size-dialog-cancel-button');
    addGrainsHamsterDialogOkButton = document.getElementById('add-grains-hamster-dialog-ok-button');
    addGrainsHamsterDialogCancelButton = document.getElementById('add-grains-hamster-dialog-cancel-button');
    saveFilesDialogSaveButton = document.getElementById('save-files-dialog-save-button');
    saveFilesDialogCancelButton = document.getElementById('save-files-dialog-cancel-button');
    changeFontSizeDialogOkButton = document.getElementById('change-font-size-dialog-ok-button');
    changeFontSizeDialogCancelButton = document.getElementById('change-font-size-dialog-cancel-button');
    changeThemeDialogOkButton = document.getElementById('change-theme-dialog-ok-button');
    changeThemeDialogCancelButton = document.getElementById('change-theme-dialog-cancel-button');
    changeSizeForm = document.getElementById('change-size-form');
    addGrainsTerritoryForm = document.getElementById('add-grains-territory-form');
    addGrainsHamsterForm = document.getElementById('add-grains-hamster-form');
    readNumberForm = document.getElementById('read-number-form');
    readStringForm = document.getElementById('read-string-form');
    writeStringForm = document.getElementById('write-string-form');
    errorForm = document.getElementById('error-form');
    saveFileForm = document.getElementById('save-file-form');
    changeFontSizeForm = document.getElementById('change-font-size-form');
    changeThemeForm = document.getElementById('change-theme-form');

    // Event-Listener für die Button-Elemente in den Dialogen
    changeSizeDialogCancelButton.addEventListener('click', closeChangeSizeDialog);
    addGrainsHamsterDialogCancelButton.addEventListener('click', closeAddGrainsHamsterDialog);
    saveFilesDialogCancelButton.addEventListener('click', closeSaveFilesDialog);
    changeFontSizeDialogCancelButton.addEventListener('click', closeChangeFontSizeDialog);
    changeThemeDialogCancelButton.addEventListener('click', closeChangeThemeDialog);

    // Event-Listener für die Formulare der Dialoge
    changeSizeForm.addEventListener('submit', handleChangeTerritorySizeFormSubmit);
    addGrainsHamsterForm.addEventListener('submit', handleAddGrainsHamsterFormSubmit);
    errorForm.addEventListener('submit', closeErrorDialog);
    saveFileForm.addEventListener('submit', validateAndSave);
    changeFontSizeForm.addEventListener('submit', handleChangeFontSizeFormSubmit);
    changeThemeForm.addEventListener('submit', changeTheme);

    errorDialogOpen = false;
    territoryTooltipOpen = false;

    window.addEventListener('resize', changeSizeOfDialogs, false);
}

/**
 * Führt Aktionen aus, wenn das Schriftgröße-Ändern-Formular abgeschickt wurde.
 *
 * @param event, das auslösende Event.
 */
function handleChangeFontSizeFormSubmit(event) {
    if (!changeFontSizeForm.checkValidity()) {
        changeFontSizeForm.reportValidity();
        return;
    }

    changeFontSize(event);
}

/**
 * Führt Aktionen aus, wenn das Körner-zum-Hamster-hinzufügen-Formular abgeschickt wurde.
 *
 * @param event, das auslösende Event.
 */
function handleAddGrainsHamsterFormSubmit(event) {
    if (addGrainsHamsterForm.checkValidity()) {
        addGrainsToHamster(event);
    } else {
        addGrainsHamsterForm.reportValidity();
    }
}

/**
 * Führt Aktionen aus, wenn das Größe-des-Territoriums-Ändern-Formular abgeschickt wurde.
 *
 * @param event, das auslösende Event.
 */
function handleChangeTerritorySizeFormSubmit(event) {
    if (changeSizeForm.checkValidity()) {
        resizeTerritory(event);
    } else {
        changeSizeForm.reportValidity();
    }
}

/**
 * Erstellt ein Versprechen-Objekt für den Korn-hinzufügen-Dialog, welches je nach Benutzereingabe einen anderen
 * Rückgabewert liefert und verwaltet die EventListener für die Buttons des Dialogs.
 */
export function createPromiseForAddGrainsDialog() {
    return new Promise(function (resolve) {
        let cancelBtn = document.getElementById('add-grains-territory-dialog-cancel-button');

        /**
         * Liefert das Ergebnis, wenn der Ok-Button gedrückt wird.
         */
        function onSubmitClick(event) {
            event.preventDefault();
            let input = parseInt(numberOfGrainsTerritoryInput.value);

            if (!addGrainsTerritoryForm.checkValidity()) {
                addGrainsTerritoryForm.reportValidity();
                return;
            }

            // Überprüfe, ob Eingabe innerhalb der Range und zeige andernfalls manuell den Input-Element-Fehler
            if (input < 1 || input > Number.MAX_SAFE_INTEGER) {
                numberOfGrainsTerritoryInput.reportValidity();
                return;
            }

            closeAddGrainsTerritoryDialog();
            resolve(input);
            cleanupButtons();
        }

        /**
         * Liefert das Ergebnis, wenn der Abbrechen-Button gedrückt wird.
         */
        function onCancelClick() {
            closeAddGrainsTerritoryDialog();
            resolve(null);
            cleanupButtons();
        }

        /**
         * Entfernt die EventListener der Buttons.
         */
        function cleanupButtons() {
            cancelBtn.removeEventListener('click', onCancelClick);
            addGrainsTerritoryForm.removeEventListener('submit', onSubmitClick);
        }

        cancelBtn.addEventListener('click', onCancelClick);
        addGrainsTerritoryForm.addEventListener('submit', onSubmitClick);
        openAddGrainsTerritoryDialog();
    });
}

/**
 * Erstellt ein Versprechen-Objekt für den Lies-Zahl-Dialog, welches je nach Benutzereingabe einen anderen
 * Rückgabewert liefert und verwaltet die EventListener für die Buttons des Dialogs.
 */
export function createPromiseForReadNumberDialog(text) {
    return new Promise(function (resolve) {
        let submitBtn = document.getElementById('submit-button-read-number');
        /**
         * Liefert das Ergebnis, wenn der Ok-Button gedrückt wird.
         */
        function onSubmitClick(event) {
            event.preventDefault();
            let input = parseFloat(readNumberInput.value);
            
            if (isNaN(input) || !Number.isInteger(input)) {
                input = 0;
            }

            // Überprüfe, ob Eingabe innerhalb der Range und eine gültige Zahl, zeige andernfalls manuell den Input-Element-Fehler.
            if (input < Number.MIN_SAFE_INTEGER || input > Number.MAX_SAFE_INTEGER) {
                readNumberInput.reportValidity();
                return;
            }

            closeReadNumberDialog();
            resolve(input);
            submitBtn.removeEventListener('click', onSubmitClick);
            readNumberForm.removeEventListener('submit', onSubmitClick);
        }

        submitBtn.addEventListener('click', onSubmitClick);
        readNumberForm.addEventListener('submit', onSubmitClick);
        openReadNumberDialog(text);
    });
}

/**
 * Erstellt ein Versprechen-Objekt für den Lies-Zeichenkette-Dialog, welches je nach Benutzereingabe einen anderen
 * Rückgabewert liefert und verwaltet die EventListener für die Buttons des Dialogs.
 */
export function createPromiseForReadStringDialog(text) {
    return new Promise(function (resolve) {
        let submitBtn = document.getElementById('submit-button-read-string');

        /**
         * Liefert das Ergebnis, wenn der Ok-Button gedrückt wird.
         */
        function onSubmitClick(event) {
            event.preventDefault();
            let input = readStringInput.value;

            closeReadStringDialog();
            resolve(input);
            submitBtn.removeEventListener('click', onSubmitClick);
            readStringForm.removeEventListener('submit', onSubmitClick);
        }

        submitBtn.addEventListener('click', onSubmitClick);
        readStringForm.addEventListener('submit', onSubmitClick);
        openReadStringDialog(text);
    });
}

/**
 * Erstellt ein Versprechen-Objekt für den Lies-Zeichenkette-Dialog, welches je nach Benutzereingabe einen anderen
 * Rückgabewert liefert und verwaltet die EventListener für die Buttons des Dialogs.
 */
export function createPromiseForWriteStringDialog(text) {
    return new Promise(function (resolve) {
        let submitBtn = document.getElementById('submit-button-write-string');

        /**
         * Liefert das Ergebnis, wenn der Ok-Button gedrückt wird.
         */
        function onSubmitClick(event) {
            event.preventDefault();

            closeWriteStringDialog();
            resolve();
            submitBtn.removeEventListener('click', onSubmitClick);
        }

        submitBtn.addEventListener('click', onSubmitClick);
        openWriteStringDialog(text);
    });
}

/**
 * Öffnet den Schreibe-Zeichenkette-Dialog.
 */
function openWriteStringDialog(text) {
    writeStringOutput.innerHTML = text;
    toggleVisibility(writeStringDialog);
    writeStringDialog.showModal();
}

/**
 * Schließt den Schreibe-Zeichenkette-Dialog.
 */
function closeWriteStringDialog() {
    toggleVisibility(writeStringDialog);
    writeStringDialog.close();
}

/**
 * Öffnet den Körner-zum-Territorium-hinzufügen-Dialog.
 */
function openAddGrainsTerritoryDialog() {
    toggleVisibility(addGrainsTerritoryDialog);
    addGrainsTerritoryDialog.showModal();
}

/**
 * Schließt den Körner-zum-Territorium-hinzufügen-Dialog.
 */
function closeAddGrainsTerritoryDialog() {
    toggleVisibility(addGrainsTerritoryDialog);
    addGrainsTerritoryDialog.close();
}

/**
 * Öffnet den Körner-zum-Hamster-hinzufügen-Dialog.
 */
export function openAddGrainsHamsterDialog() {
    numberOfGrainsHamsterInput.value = hamster.getNumberOfGrains();
    toggleVisibility(addGrainsHamsterDialog)
    addGrainsHamsterDialog.showModal();
}

/**
 * Schließt den Körner-zum-Hamster-hinzufügen-Dialog.
 */
export function closeAddGrainsHamsterDialog() {
    toggleVisibility(addGrainsHamsterDialog);
    addGrainsHamsterDialog.close();
}

/**
 * Öffnet den Schriftgröße-ändern-Dialog.
 */
export function openChangeFontSizeDialog() {
    let actualFontSize = document.getElementById('editor-wrapper').style.fontSize;

    // Beim ersten Laden der Schriftgröße ist kein Wert hinterlegt, daher wird der hinterlegte Standard-Wert genutzt.
    if (actualFontSize === "") {
        actualFontSize = defaultFontSize;
    } else {
        actualFontSize = actualFontSize.replace("px", "");
    }

    fontSizeInput.value = actualFontSize;
    toggleVisibility(changeFontSizeDialog);
    changeFontSizeDialog.showModal();
}

/**
 * Schließt den Schriftgröße-ändern-Dialog.
 */
export function closeChangeFontSizeDialog() {
    toggleVisibility(changeFontSizeDialog);
    changeFontSizeDialog.close();
}

/**
 * Öffnet den Theme-ändern-Dialog.
 */
export function openChangeThemeDialog() {
    toggleVisibility(changeThemeDialog);
    changeThemeDialog.showModal();
}

/**
 * Schließt den Theme-ändern-Dialog.
 * */
export function closeChangeThemeDialog() {
    toggleVisibility(changeThemeDialog);
    changeThemeDialog.close();
}
/**
 * Öffnet den Territorium-Größe-Ändern-Dialog.
 */
export function openChangeSizeDialog() {
    rowInput.value = rows;
    colInput.value = cols;
    toggleVisibility(changeTerritorySizeDialog);
    changeTerritorySizeDialog.showModal();
}

/**
 * Schließt den Territorium-Größe-Ändern-Dialog.
 */
export function closeChangeSizeDialog() {
    toggleVisibility(changeTerritorySizeDialog);
    changeTerritorySizeDialog.close();
}

/**
 * Öffnet den Lies-Zahl-Dialog.
 */
function openReadNumberDialog(text) {
    let textField = document.getElementById('read-number-text');
    textField.innerHTML = text;
    
    toggleVisibility(readNumberDialog);
    readNumberDialog.showModal();
}

/**
 * Schließt den Lies-Zahl-Dialog.
 */
function closeReadNumberDialog() {
    toggleVisibility(readNumberDialog);
    readNumberDialog.close();
}

/**
 * Öffnet den Lies-Zeichenkette-Dialog.
 */
function openReadStringDialog(text) {
    let textField = document.getElementById('read-string-text');
    textField.innerHTML = text;
    
    toggleVisibility(readStringDialog);
    readStringDialog.showModal();
}

/**
 * Schließt den Lies-Zeichenkette-Dialog.
 */
function closeReadStringDialog() {
    toggleVisibility(readStringDialog);
    readStringDialog.close();
}

/**
 * Öffnet den Error-Dialog.
 */
export function openErrorDialog(text) {
    errorDialogOpen = true;
    
    let textField = document.getElementById('error-output');
    textField.innerHTML = text;

    toggleVisibility(errorDialog);
    errorDialog.showModal();
}

/**
 * Schließt den Error-Dialog.
 */
export function closeErrorDialog(event) {
    event.preventDefault();
    errorDialogOpen = false;

    toggleVisibility(errorDialog);
    errorDialog.close();
}

/**
 * Öffnet den Dateien-Speichern-Dialog.
 */
export function openSaveFilesDialog() {
    toggleVisibility(saveFilesDialog);
    saveFilesDialog.showModal();
}

/**
 * Schließt den Dateien-Speichern-Dialog.
 */
export function closeSaveFilesDialog() {
    toggleVisibility(saveFilesDialog);
    saveFilesDialog.close();
}

/**
 * Ändert die Sichtbarkeit und Größe des übergebenen Elements.
 * 
 * @param dialog, dessen Sichtbarkeit geändert werden soll.
 */
export const toggleVisibility = (dialog) => {
    dialog.classList.toggle('hidden');
    dialog.classList.toggle('flex');

    // Wird benötigt da manche Browser eine geringere maxWidth festlegen und sonst ein ungewollter Rand entsteht.
    if (getScreenWidth() <= 768) {
        dialog.style.maxWidth = "100vw";
    } else {
        dialog.style.maxWidth = "auto";
    }
}

/**
 * Ändert die maxWidth des Dialogfensters, damit sich dieses responsiv verhält.
 */
function changeSizeOfDialogs() {
    if (document.body.clientWidth <= 768) {
        changeTerritorySizeDialog.style.maxWidth = "100vw";
        addGrainsTerritoryDialog.style.maxWidth = "100vw";
    } else {
        changeTerritorySizeDialog.style.maxWidth = "auto";
        addGrainsTerritoryDialog.style.maxWidth = "auto";
    }
}

/**
 * Ändert die Sichtbarkeit des Territorium-Tooltips.
 */
function toggleTerritoryTooltip() {
    territoryTooltipOpen = !territoryTooltipOpen;
    territoryTooltip.classList.toggle('invisible');
}

/**
 * Setzt die Platzierungswerte für den Territorium-Tooltip und richtet sich dabei nach der Bildschirmbreite und -höhe
 * des Clients.
 */
function setTerritoryTooltipPlacementValues() {
    let tooltipWidth = territoryTooltip.getBoundingClientRect().width;
    let tooltipHeight = territoryTooltip.getBoundingClientRect().height;

    if (getScreenWidth() < 1024) {
        if (pointerClickX + tooltipWidth > getScreenWidth()) {
            territoryTooltip.style.left = getScreenWidth() - tooltipWidth + "px";
        } else if (pointerClickX - cellWidth/2 < 0) {
            territoryTooltip.style.left = 0 + "px";
        } else {
            territoryTooltip.style.left = pointerClickX - cellWidth/2 + "px";
        }

        if (pointerClickY + tooltipHeight > getScreenHeight()) {
            territoryTooltip.style.top = pointerClickY - (tooltipHeight + cellHeight/2) + "px";
        } else {
            territoryTooltip.style.top = pointerClickY + cellHeight/2 + "px";
        }
    } else {
        territoryTooltip.style.left = pointerClickX - (tooltipWidth + cellWidth/2) + "px";

        if (pointerClickY + tooltipHeight > getScreenHeight()) {
            territoryTooltip.style.top = getScreenHeight() - tooltipHeight + "px";
        } else {
            territoryTooltip.style.top = pointerClickY - cellHeight/2 + "px";
        }
    }
}

/**
 * Öffnet den Tooltip des Territoriums und fügt die Informationen über den Hamster und den aktuellen Zelleninhalt ein.
 * 
 * @param actualCellContent, betreffende Zelle und deren Inhalt.
 */
export function showTerritoryTooltip(actualCellContent) {
    grainsInField.innerHTML = actualCellContent.getNumberOfGrains();
    grainsInMouth.innerHTML = hamster.getNumberOfGrains();
    hamsterDirection.innerHTML = getActorDirection();
    setTerritoryTooltipPlacementValues();
    toggleTerritoryTooltip();
}

/**
 * Schließt den Tooltip des Territoriums.
 */
export function closeTerritoryTooltip() {
    if (territoryTooltipOpen) {
        toggleTerritoryTooltip();
    }
}