"use strict";

if ('serviceWorker' in navigator) {
    console.log("Registering service worker");
    navigator.serviceWorker.register('/sw.js');
}

class CsvxmlValidator {

    fieldList;  // {}{}
    toValidate; // []{}
    errors;     // {}array

    constructor(fieldList, csvRaw) {

        this.errors = {
            parsing: [],
            mandatoryTags: [],
            duplicateInvNos: [],
            dependentColumns: [],
            controlledLists: [],
            mainImageResource: [],
        };

        this.fieldList = Object.freeze(fieldList);

        const data = Papa.parse(csvRaw.trim(), {
            delimiter: ";", // auto-detect
            escapeChar: '"',
            skipEmptyLines: true,
            header: true,
        });

        if (data.errors.length !== 0) {
            console.log("Errors encountered: ");
            console.error(data.errors);

            let msg = '';
            for (let err of data.errors) {
                msg += err.type + ': ' + err.message + "\n";
            }
            window.alert(msg);
        }

        let toValidate = data.data;

        this.toValidate = toValidate;

        if (toValidate.length === 0) {
            alert("Error: No lines of content identified");
        }

        this.validate();
    }

    validate() {

        this.validateMandatoryTagsPresent();
        this.validateInvalidTagsPresent();
        this.checkDuplicateInvNos();
        this.checkDependentColumns();
        this.checkControlledLists();
        // this.checkMainImageResource();

    }

    validateMandatoryTagsPresent() {

        let mandatoryFields = [];
        for (let fieldName in this.fieldList) {
            if (this.fieldList[fieldName].required === true) {
                mandatoryFields.push(fieldName);
            }
        }

        console.log(this.toValidate);
        let lineCounter = 1;
        for (let line of this.toValidate) {
            for (let mandatoryField of mandatoryFields) {
                if (line[mandatoryField] === undefined || line[mandatoryField] === null || line[mandatoryField] === '') {
                    this.errors.mandatoryTags.push("Missing or empty mandatory tag " + mandatoryField + " on line " + lineCounter);
                }
            }
            lineCounter++;
        }

    }

    validateInvalidTagsPresent() {

        const headers = Object.keys(this.toValidate[0]);

        for (let header of headers) {
            if (this.fieldList[header] === undefined) {
                this.errors.parsing.push("Invalid column \"" + header + "\" detected! Please remove this column or use the appropriate name!");
            }
        }

    }

    checkDuplicateInvNos() {

        let invNoEncountered = [];
        let lineCounter = 1;
        for (let line of this.toValidate) {
            if (invNoEncountered.includes(line.inventory_number)) {
                this.errors.duplicateInvNos.push("Duplicate inventory number " + line.inventory_number + " on line " + lineCounter);
            }
            invNoEncountered.push(line.inventory_number);
            lineCounter++;
        }

    }

    checkDependentColumns() {

        const headers = Object.keys(this.toValidate[0]);

        for (let header of headers) {
            if (this.fieldList[header] === undefined || this.fieldList[header].dependsOn === undefined || this.fieldList[header].dependsOn === null) continue;

            let dependencies = this.fieldList[header].dependsOn;
            for (let dep of dependencies) {
                if (headers.includes(dep) === false) {
                    console.error("Dependency issue at column " + header + ": Corresponding column " + dep + " is missing");
                    console.log(headers);
                    this.errors.dependentColumns.push("Dependency issue at column " + header + ": Corresponding column " + dep + " is missing");
                }
            }
        }

        let lineCounter = 1;
        for (let line of this.toValidate) {
            for (let fieldName in line) {

                if (line[fieldName] === '') continue;
                if (this.fieldList[fieldName] === undefined) continue; // This may be the case if invalid fields are present

                const dependencies = this.fieldList[fieldName].dependsOn;
                if (dependencies === undefined) continue;

                for (let dependency of dependencies) {
                    if (line[dependency] === '') {
                        console.error("Dependency issue at column " + fieldName + ": Corresponding column " + dependency + " is missing [on line " + lineCounter + "]");
                        console.log(line);
                        this.errors.dependentColumns.push("Dependency issue at column " + fieldName + " (current value: " + line[fieldName] + "): Corresponding column " + dependency + " is empty [on line " + lineCounter + "]");
                    }
                }
            }
            lineCounter++;
        }

    }

    checkControlledLists() {

        let lineCounter = 1;
        for (let line of this.toValidate) {
            for (let fieldName in line) {

                if (this.fieldList[fieldName] === undefined) {
                    console.log("Undefined but requested field " + fieldName);
                    continue;
                }

                const allowedValues = this.fieldList[fieldName].allowedValues;

                // No error if the field doesn't have a controlled list
                if (allowedValues === undefined || allowedValues === null) continue;
                // No error if the line's content is in the list
                if (Object.values(allowedValues).length === 0 || Object.values(allowedValues).includes(line[fieldName])) {
                    continue;
                }

                if (line[fieldName] === '') continue;
                this.errors.controlledLists.push("Disallowed value used for column " + fieldName + " at line " + lineCounter + " (Allowed values are: " + Object.values(allowedValues).join(", ") + "; current value is " + line[fieldName] + ")");
            }
            lineCounter++;
        }

    }

    checkMainImageResource() {

    }

    isValid() {

        for (let errorClass in this.errors) {
            if (this.errors[errorClass].length !== 0) return false;
        }

        return true;

    }

    /**
     * Generates XML for the parsed lines
     */
    generateXml() {

        let output = [];

        let xmlDoc = document.implementation.createDocument(null, "record");
        for (let line of this.toValidate) {
            let root = xmlDoc.createElement("record");
            for (let fieldName in line) {
                const elem = xmlDoc.createElement(fieldName);
                elem.textContent = line[fieldName];
                root.appendChild(elem);
            }
            output.push(root)
        }

        return output;

    }

}

class CsvxmlTooltip {

    /**
     * Function for setting the alignment of an element.
     *
     * @param {Event}      e    Event triggering the execution of this function.
     * @param {DOMElement} elem Dom element to position.
     *
     * @return {void}
     */
    static getDirection(e, elem) {

        if (window.innerHeight < e.clientY + elem.clientHeight) {
            elem.style.top    = "";
            elem.style.bottom = (window.innerHeight - e.clientY) + "px";
        }
        else {
            elem.style.bottom  = "";
            elem.style.top     = (e.clientY + 4) + "px";
        }
        if (window.innerWidth < e.clientX + elem.clientWidth) {
            elem.style.left  = "";
            elem.style.right = (window.innerWidth - e.clientX) + "px";
        } else {
            elem.style.right = "";
            elem.style.left  = (e.clientX + 3) + "px";
        }

    }

    static positionMobile(newMain) {

        if (window.matchMedia && window.matchMedia('(max-width:75em)').matches) {

            newMain.style.left = "";
            newMain.style.right = "";
            newMain.style.top = "";
            newMain.style.bottom = "";

            if (newMain.classList.contains("atBottom") === false) {
                newMain.classList.add("atBottom");
            }

        }
    }

    static triggerMouseMove(e) {
        const newMain = document.getElementById("newToolTipMain");
        if (newMain === undefined || newMain === null) return;
        CsvxmlTooltip.getDirection(e, newMain);
        CsvxmlTooltip.positionMobile(newMain);
    }

    static triggerMouseOut(e) {

        const newMain = document.getElementById("newToolTipMain");
        if (newMain !== undefined && newMain !== null) {
            newMain.classList.remove("visible");
            document.body.removeChild(newMain);
        }
        e.target.removeEventListener('mouseout', CsvxmlTooltip.triggerMouseOut);

    }

    static bindTooltipToElement(elem, tooltipTitle, tooltipContent) {

        elem.addEventListener('mouseover', function(e) {

            let newMain = document.getElementById("newToolTipMain");
            if (newMain !== null) return;
            newMain = document.createElement("div");
            newMain.classList.add("newToolTip");
            newMain.id = "newToolTipMain";

            // Insert contents loaded.

            newMain.setAttribute("data-title", tooltipTitle);

            newMain.appendChild(tooltipContent);

            document.body.appendChild(newMain);
            newMain.classList.add("visible");
            CsvxmlTooltip.getDirection(e, newMain);
            CsvxmlTooltip.positionMobile(newMain);

            newMain.addEventListener("mouseout", function(e) {
                const newMain = document.getElementById("newToolTipMain");
                if (newMain !== undefined && newMain !== null) {
                    newMain.classList.remove("visible");
                    document.body.removeChild(newMain);
                }
            });

            elem.addEventListener("mouseout", CsvxmlTooltip.triggerMouseOut);

        }, {passive: true});

        elem.addEventListener("mousemove", CsvxmlTooltip.triggerMouseMove, {passive: true});

    }

}

class CsvxmlDialogue {

    static closeDialogue(e) {

        if (e !== undefined) {
            e.preventDefault();
            e.stopPropagation();
        }

        let dialogueArea = document.getElementById("dialogueArea");
        if (dialogueArea !== null && dialogueArea !== false) {
            while (dialogueArea.firstChild) {
                dialogueArea.removeChild(dialogueArea.firstChild);
            }
            dialogueArea.parentElement.removeChild(dialogueArea);

            document.removeEventListener('keydown', CsvxmlDialogue.closeDialogueByEscape, false);
        }

    }

    static closeDialogueByEscape(e) {

        if (e.keyCode === 27) { // 27 = Esc

            CsvxmlDialogue.closeDialogue(e);

        }

    }

    /**
     * Function for drawing a dialogue and attaching it to the body elem.
     *
     * @param {DOMElement} contents Contents.
     */
    static drawDialogue(contents) {
        let dialogueArea = document.createElement("div");
        dialogueArea.id = "dialogueArea";

        let dialogue = document.createElement("div");
        dialogue.id = "dialogue";

        dialogue.appendChild(contents);
        dialogueArea.appendChild(dialogue);

        document.body.appendChild(dialogueArea);

        document.addEventListener('keydown', CsvxmlDialogue.closeDialogueByEscape);

        return dialogue;
    }

}

class CsvxmlPage {

    fieldList;
    fieldListFlat;
    tls;
    domHelpWrapper;
    domUploaderWrapper;
    domMainWrapper;
    selectedFields;

    csvBySelectionButton;
    unsetSelectionButton;

    constructor(fieldList, tls) {
        this.fieldList = Object.freeze(fieldList);

        let list = {};
        for (let sectionName in fieldList) {
            list = Object.assign(list, fieldList[sectionName]);
        }
        this.fieldListFlat = Object.freeze(list);

        this.tls = Object.freeze(tls);

        let domHelpWrapper = document.createElement("div");
        domHelpWrapper.id = "helpSection";
        this.domHelpWrapper = domHelpWrapper;

        let domUploaderWrapper = document.createElement("div");
        domUploaderWrapper.id = "uploader";
        domUploaderWrapper.classList.add("uploader");
        this.domUploaderWrapper = domUploaderWrapper;

        let domMainWrapper = document.createElement("main");
        this.domMainWrapper = domMainWrapper;

        this.selectedFields = [];
    }

    generateCsv(selectedFields = []) {

        let line1 = [];
        let line2 = [];
        let line3 = [];

        for (let fieldName in this.fieldListFlat) {
            console.log(fieldName);
            console.log(selectedFields);

            if (selectedFields.length !== 0 && selectedFields.includes(fieldName) === false) continue;
            const field = this.fieldListFlat[fieldName];

            line1.push(fieldName);
            line2.push(field.name_human_readable);

            if (field.allowedValues !== undefined) {
                // Join for object
                let values = [];
                for (let key in field.allowedValues) {
                    values.push(field.allowedValues[key]);
                }
                line3.push(values.join(","));
            }
            else line3.push("");

        }

        const csvLine1 = '"' + line1.join('";"') + '"';
        const csvLine2 = '"' + line2.join('";"') + '"';
        const csvLine3 = '"' + line3.join('";"') + '"';

        const toStore = csvLine1 + "\n" + csvLine2 + "\n" + csvLine3;

        // Download
        const triggerLink = document.createElement('a');
        triggerLink.setAttribute('href', 'data:text/plain;charset=utf-8,' + encodeURIComponent(toStore));
        triggerLink.setAttribute('download', "csvxml_museum-digital_template.csv");

        triggerLink.style.display = 'none';

        document.body.appendChild(triggerLink);
        triggerLink.click();
        document.body.removeChild(triggerLink);


    }

    zipUploadToXml(validator) {

        // Wrap zipping in function to postload it
        function runZipping() {

            let zip = new JSZip();

            let xmlFiles = validator.generateXml();
            const serializer = new XMLSerializer();
            let lineCounter = 0;
            for (let xml of xmlFiles) {
                zip.file(lineCounter + ".xml", serializer.serializeToString(xml));
                lineCounter++;
            }

            zip.generateAsync({type:"blob"})
            .then(function(content) {
                const triggerLink = document.createElement('a');
                triggerLink.href = window.URL.createObjectURL(content);
                triggerLink.setAttribute('download', "csvxml.zip");

                triggerLink.style.display = 'none';

                document.body.appendChild(triggerLink);
                triggerLink.click();
                document.body.removeChild(triggerLink);
            });

        }

        if (typeof JSZip === "undefined") {

            const loadScript = document.createElement("script");
            loadScript.setAttribute("src", "assets/js/jszip/dist/jszip.min.js");
            loadScript.addEventListener('load', function() {
                // console.log("Post-loaded OpenLayers");
                runZipping();
            }, {passive: true, once: true});

            document.body.appendChild(loadScript);
        }
        else {
            runZipping();
        }

    }

    generateDialogueCloseButton() {

        const cancelB = document.createElement("a");
        cancelB.classList.add("icons");
        cancelB.classList.add("iconsClose");
        cancelB.classList.add("dialogueCloseX");
        cancelB.id = "dialogueClose";
        cancelB.textContent = "X";
        cancelB.title = "Close";
        cancelB.href = "#" + location.href;
        cancelB.addEventListener('click', CsvxmlDialogue.closeDialogue);
        return cancelB;

    }

    listValidationErrors(validator) {

        console.log("Listing validation errors");

        const dialogueContent = document.createElement("div");

        const headline = document.createElement("h3");
        headline.textContent = this.tls.validation_errors;
        headline.appendChild(this.generateDialogueCloseButton());
        dialogueContent.appendChild(headline);

        const domErrorsSection = document.createElement("div");

        for (let errorType in validator.errors) {

            if (validator.errors[errorType].length === 0) continue;

            const ulHl = document.createElement("h4");
            ulHl.textContent = this.tls['errors_' + errorType] + " (" + validator.errors[errorType].length + ")";
            ulHl.style.cursor = "pointer";
            domErrorsSection.appendChild(ulHl);

            const ul = document.createElement("ul");

            for (let error of validator.errors[errorType]) {
                const li = document.createElement("li");
                li.textContent = error;
                ul.appendChild(li);
            }

            ulHl.addEventListener('click', function() {
                ul.classList.toggle("minimized");
            });

            domErrorsSection.appendChild(ul);

        }

        dialogueContent.appendChild(domErrorsSection);

        const domDlSection = document.createElement("div");
        const domDlA = document.createElement("span");
        domDlA.textContent = this.tls.download;
        domDlA.classList.add("buttonLike");
        let app = this;
        domDlA.addEventListener('click', function() {
            app.zipUploadToXml(validator);
        });
        domDlSection.appendChild(domDlA);
        dialogueContent.appendChild(domDlSection);

        dialogue = CsvxmlDialogue.drawDialogue(dialogueContent);

    }

    uploadFileForValidation(file) {

        const reader = new FileReader();
        let utf8 = true;
        reader.readAsText(file, utf8 ? 'UTF-8' : 'CP1251');

        let app = this;

        document.body.classList.add("loading");

        reader.onload = function() {

            function handleValidation() {

                (async function() {
                    const result = reader.result;
                    if (utf8 && result.includes('�')) {
                        window.alert('The file encoding appears to not be UTF-8!\n\nTry exporting the file using the format "CSV (UTF-8)" if you use MS Excel or set the encoding to UTF-8 when exporting through LibreOffice!');
                    }
                })();

                // On loading success, check if the upload is valid JSON
                console.log("Read file");
                // Validate the file
                let validator = new CsvxmlValidator(app.fieldListFlat, reader.result);
                document.body.classList.remove("loading");
                if (validator.isValid() === true) {
                    alert("Document is valid. Press ok to download.");
                    app.zipUploadToXml(validator);
                }
                else {
                    console.log("Identified invalid upload document");
                    app.listValidationErrors(validator);
                }

            }

            console.log("Postload papaparse");
            if (typeof Papa === "undefined") {

                const loadScript = document.createElement("script");
                loadScript.setAttribute("src", "assets/js/papaparse/papaparse.min.js");
                loadScript.addEventListener('load', function() {
                    // console.log("Post-loaded OpenLayers");
                    handleValidation();
                }, {passive: true, once: true});
                document.body.appendChild(loadScript);

            }
            else {
                handleValidation();
            }

        };
        reader.onerror = function() {
            alert(reader.error);
        };

    }

    renderGenHeader() {

        const header = document.createElement("header");
        header.id = "mainHeader";

        const logoArea = document.createElement("a");
        logoArea.id = "logoArea";
        logoArea.href = "https://www.museum-digital.org/";

        const logoImg = document.createElement("img");
        logoImg.src = "assets/img/mdlogo-code-128px.png";
        logoImg.alt = "Logo of museum-digital";
        logoArea.appendChild(logoImg);

        const h2 = document.createElement("h2");
        h2.textContent = "museum-digital";
        logoArea.appendChild(h2);

        header.appendChild(logoArea);

        // Right side of the header
        const nav = document.createElement("nav");

        const lAbout = document.createElement("a");
        lAbout.href = "https://en.about.museum-digital.org/about";
        lAbout.textContent = this.tls.about;
        nav.appendChild(lAbout);

        const lContactList = document.createElement("div");

        const lContact = document.createElement("a");
        lContact.textContent = this.tls.contact;
        lContact.href = "https://en.about.museum-digital.org/contact/";
        lContactList.appendChild(lContact);

        const lContactDiv = document.createElement("div");

        const lImprint = document.createElement("a");
        lImprint.textContent = this.tls.imprint;
        lImprint.href = "https://en.about.museum-digital.org/impressum";
        lContactDiv.appendChild(lImprint);

        const lPrivacy = document.createElement("a");
        lPrivacy.textContent = this.tls.privacy_policy;
        lPrivacy.href = "https://en.about.museum-digital.org/privacy/";
        lContactDiv.appendChild(lPrivacy);

        lContactList.appendChild(lContactDiv);
        nav.appendChild(lContactList);

        const lNews = document.createElement("a")
        lNews.textContent = this.tls.news;
        lNews.href = "https://blog.museum-digital.org/";
        nav.appendChild(lNews);

        header.appendChild(nav);
        document.body.appendChild(header);

    }

    renderHeader() {

        const appHeader = document.createElement("header");

        const h1 = document.createElement("h1");

        const img = document.createElement("img");
        img.width = "70";
        img.height = "70";
        img.src = "assets/img/mdlogo-csvxml.svg";
        img.alt = "";
        h1.appendChild(img);

        const h1Span = document.createElement("span");
        h1Span.textContent = "museum-digital:csvxml";
        h1.appendChild(h1Span);

        appHeader.appendChild(h1);

        document.body.appendChild(appHeader);

    }

    renderHelpTexts() {

        let app = this;
        (async function() {

            function appendQA(question, answer) {

                const div = document.createElement("div");
                div.classList.add("qaDiv");

                const qElem = document.createElement("h3");
                qElem.textContent = question;
                qElem.style.cursor = "pointer";
                div.appendChild(qElem);

                qElem.addEventListener('click', function() {

                    console.log("Listing validation errors");

                    const dialogueContent = document.createElement("div");

                    const headline = document.createElement("h3");
                    headline.textContent = question;
                    headline.appendChild(app.generateDialogueCloseButton());
                    dialogueContent.appendChild(headline);

                    const answerDiv = document.createElement("div");
                    answerDiv.textContent = answer;
                    dialogueContent.appendChild(answerDiv);
                    CsvxmlDialogue.drawDialogue(dialogueContent);
                });

                return div;

            }

            const div = document.createElement("div");

            div.appendChild(appendQA(app.tls.help_where_am_i, app.tls.help_where_am_i_content));
            div.appendChild(appendQA(app.tls.help_what_is_csv, app.tls.help_what_is_csv_content));
            div.appendChild(appendQA(app.tls.help_how_to_format_csv, app.tls.help_how_to_format_csv_content));

            app.domHelpWrapper.appendChild(div);

        })();

        document.body.appendChild(this.domHelpWrapper);

    }

    renderUploader() {

        let app = this;
        (async function() {

            const h2 = document.createElement("h2");
            h2.textContent = app.tls.upload;
            app.domUploaderWrapper.appendChild(h2);

            const form = document.createElement("form");

            const label = document.createElement("label");
            label.textContent = app.tls.select_csv_file_for_upload;
            label.setAttribute("for", "fileToUpload");
            form.appendChild(label);

            const input = document.createElement("input");
            input.type = "file";
            input.id = "fileToUpload";
            input.setAttribute("tabindex", "1");
            input.accept = ".csv";
            input.required = "required";
            input.addEventListener('change', async function() {
                app.uploadFileForValidation(input.files[0]);
            });
            form.appendChild(input);

            /*
            const button = document.createElement("button");
            button.textContent = "Upload"; // TODO
            button.type = "submit";
            form.appendChild(button);
            */

            app.domUploaderWrapper.appendChild(form);

        })();

        document.body.appendChild(this.domUploaderWrapper);

    }

    // Takes a callback function
    doForFieldList(callback) {
        let fieldLists = document.getElementsByClassName("fieldList");
        for (let i = 0, max = fieldLists.length; i < max; i++) {

            let fields = fieldLists[i].getElementsByTagName("li");
            for (let j = 0, maxj = fields.length; j < maxj; j++) {

                callback(fields[j]);

            }

        }
    }

    toggleListFieldSelectionState(field) {

        let app = this;

        let newValue = field.getAttribute("data-alt");
        field.setAttribute("data-alt", field.textContent);
        field.textContent = newValue;
        field.classList.toggle("humanTLToggled");

        if (field.classList.contains("humanTLToggled") === false) return;

        let dependencies = this.fieldListFlat[field.id].dependsOn;
        if (dependencies !== undefined && dependencies !== null) {
            let linkedFields = this.fieldListFlat[field.id].dependsOn;
            for (let i = 0, max = linkedFields.length; i < max; i++) {
                let linkedField = document.getElementById(linkedFields[i]);
                if (linkedField.classList.contains("humanTLToggled") === true) continue;
                this.toggleListFieldSelectionState(linkedField);
            }
        }

    }

    checkCSVBySelectionAccessibility() {

        let selected = document.getElementsByClassName("humanTLToggled");
        if (selected.length === 0) {
            this.csvBySelectionButton.classList.add("invisible");
            this.unsetSelectionButton.classList.add("invisible");
        }
        else {
            this.csvBySelectionButton.classList.remove("invisible");
            this.unsetSelectionButton.classList.remove("invisible");
        }

    }

    getOptionsSection() {

        function genButton(id, text, link = "") {

            const output = document.createElement("span");
            output.id = id;
            output.setAttribute("tabindex", "1");
            output.textContent = text;
            output.classList.add("buttonLike");
            if (link !== "") output.href = link;
            return output;

        }

        const options = document.createElement("div");
        options.classList.add("options");

        const app = this;

        const dlAllButton = genButton("dlAll", this.tls.download_csv_all);
        dlAllButton.cursor = "pointer";
        dlAllButton.addEventListener('click', function(e) {
            e.preventDefault();
            app.generateCsv();
        });
        options.appendChild(dlAllButton);

        this.csvBySelectionButton = genButton("csvBySelection", this.tls.download_csv_by_selection);
        this.csvBySelectionButton.classList.add("invisible");
        options.appendChild(this.csvBySelectionButton);

        const optionSelectRequired = genButton("selectRequired", this.tls.select_required_fields);
        options.appendChild(optionSelectRequired);

        const optionSelectAll = genButton("selectAll", this.tls.select_all_fields);
        options.appendChild(optionSelectAll);

        this.unsetSelectionButton = genButton("unsetSelection", this.tls.unset_selection);
        this.unsetSelectionButton.classList.add("invisible");
        options.appendChild(this.unsetSelectionButton);

        this.csvBySelectionButton.addEventListener('click', function(e) {
            e.preventDefault();

            let selected = document.getElementsByClassName("humanTLToggled");
            let selectedFields = [];
            for (let i = 0, max = selected.length; i < max; i++) {
                selectedFields += selected[i].getAttribute("data-value");
            }
            app.generateCsv(selectedFields);

        });

        optionSelectRequired.addEventListener('click', function(e) {
            e.preventDefault();

            app.doForFieldList(function(field) {
                if (field.classList.contains("requiredField") === false) return;
                if (field.classList.contains("humanTLToggled") === true) return;

                app.toggleListFieldSelectionState(field);
                app.checkCSVBySelectionAccessibility();

            });

        });

        optionSelectAll.addEventListener('click', function(e) {
            e.preventDefault();

            app.doForFieldList(function(field) {
                if (field.classList.contains("humanTLToggled") === true) return;

                app.toggleListFieldSelectionState(field);
                app.checkCSVBySelectionAccessibility();

            });

        });

        this.unsetSelectionButton.addEventListener('click', function(e) {
            e.preventDefault();

            app.doForFieldList(function(field) {
                if (field.classList.contains("humanTLToggled") === false) return;

                app.toggleListFieldSelectionState(field);
                app.checkCSVBySelectionAccessibility();

            });

        });

        return options;

    }

    renderMain() {

        const domH2 = document.createElement("h2");
        domH2.textContent = this.tls.currently_approved_tags;
        this.domMainWrapper.appendChild(domH2);

        this.domMainWrapper.appendChild(this.getOptionsSection());

        for (let sectionName in this.fieldList) {

            const domDiv = document.createElement("div");

            const domH3 = document.createElement("h3");
            domH3.textContent = sectionName;
            domDiv.appendChild(domH3);

            const domUl = document.createElement("ul");
            domUl.classList.add("fieldList");

            const sectionFields = this.fieldList[sectionName];
            for (let fieldName in sectionFields) {
                const field = sectionFields[fieldName];

                const domLi = document.createElement("li");
                domLi.textContent = fieldName;
                domLi.id = fieldName;
                domLi.setAttribute("data-alt", field.name_human_readable)
                domLi.setAttribute("data-value", fieldName)
                if (field.required === true) domLi.classList.add("requiredField");
                domUl.appendChild(domLi);

                const tooltipContent = document.createElement("div");
                const explicaP = document.createElement("p");
                explicaP.textContent = field.explica;
                tooltipContent.appendChild(explicaP);

                if (field.remarks !== undefined && field.remarks !== '') {

                    const remarkHl = document.createElement("h4");
                    remarkHl.textContent = this.tls.remarks;
                    tooltipContent.appendChild(remarkHl)
                    const remarkCont = document.createElement("p");
                    remarkCont = field.remarks;
                    tooltipContent.appendChild(remarkCont);

                }

                if (field.allowedValues !== undefined && Object.values(field.allowedValues).length !== 0) {
                    const allowedHl = document.createElement("h4");
                    allowedHl.textContent = this.tls.allowed_values;
                    tooltipContent.appendChild(allowedHl);
                    const allowedList = document.createElement("p");
                    allowedList.textContent = Object.values(field.allowedValues).join(', ');
                    tooltipContent.appendChild(allowedList);
                }

                CsvxmlTooltip.bindTooltipToElement(domLi, field.name_human_readable, tooltipContent);

            }

            domDiv.appendChild(domUl);

            this.domMainWrapper.appendChild(domDiv);

        }

        document.body.appendChild(this.domMainWrapper);

        let app = this;
        this.doForFieldList(function(field) {

            // Each field should switch its visible content and human-readable
            // translation on a click.
            field.addEventListener('click', function(e) {

                app.toggleListFieldSelectionState(field);
                app.checkCSVBySelectionAccessibility();

            });

        });

    }

    renderFooter() {

        const footer = document.createElement("footer");

        const licenseStatement = document.createElement("p");
        licenseStatement.textContent = "This work is licensed under the GNU Affero Public License Version 3.";
        footer.appendChild(licenseStatement);

        const footerOptions = document.createElement("div");

        const codeLink = document.createElement("a");
        codeLink.textContent = "Source code";
        codeLink.href = "https://gitea.armuli.eu/museum-digital/csvxml";
        footerOptions.appendChild(codeLink);

        if ('serviceWorker' in navigator) {
        const refreshB = document.createElement("span");
        refreshB.textContent = "Reload application";
        refreshB.setAttribute("tabindex", 1);
        refreshB.addEventListener('click', function(e) {

            Promise.all(['csvxml-cache-v1'].map(function(cache) {
                caches.has(cache).then(function(hasCache) {
                    if (hasCache === true) {
                        caches.delete(cache).then(function(deletionStatus) {});
                    }
                })
            }))
            location.reload()

        }, {passive: true, once: true});
        footerOptions.appendChild(refreshB);
        }

        footer.appendChild(footerOptions);
        document.body.appendChild(footer);

    }

}

(async function() {

    function getLang() {

        const allowedLangs = document.documentElement.getAttribute("data-allowed-langs").split(',');

        if (navigator.language === undefined) return 'en';

        const browserLang = navigator.language.toLowerCase().substr(0, 2);
        console.log(browserLang);

        if (allowedLangs.includes(browserLang)) return browserLang;
        else return 'en';

    }

    const lang = getLang();
    document.documentElement.setAttribute("lang", lang);

    document.body.classList.add("loading");

    let loaded = 0;

    let fieldList;
    let tls;

    function loadPage() {

        document.body.classList.remove("loading");

        const page = new CsvxmlPage(fieldList, tls);
        page.renderGenHeader();
        page.renderHeader();
        page.renderHelpTexts();
        page.renderUploader();
        page.renderMain();
        page.renderFooter();

    }

    window.fetch('/json/fields.' + lang + '.json', {
        method: 'GET', cache: 'no-cache', credentials: 'same-origin',
    }).then(function(response) {
        return response.json();
    }).then(function(elements) {
        fieldList = elements;
        loaded++;
        if (loaded === 2) loadPage();
    });

    window.fetch('/json/tls.' + lang + '.json', {
        method: 'GET', cache: 'no-cache', credentials: 'same-origin',
    }).then(function(response) {
        return response.json();
    }).then(function(elements) {
        tls = elements;
        loaded++;
        if (loaded === 2) loadPage();
    });

})();