// JavaScript

// Editor for HTML page


"use strict";


/*
    Editor for HTML page (Thunderbird or Iframe)
*/
class HTMLPageEditor {

	constructor (xDocument, bCheckSignature=false) {
        this.xDocument = xDocument;
        this.xRootNode = xDocument.body;
        //console.log(xDocument.body);
        //console.log(xDocument.body.innerHTML);
        this.lNode = [];
        this.bCheckSignature = bCheckSignature;
        this._lParsableNodes = ["P", "LI", "H1", "H2", "H3", "H4", "H5"];
        this._lRootNodes = ["DIV", "UL", "OL"];
    }

    * _getParsableNodes (xRootNode) {
        // recursive function
        try {
            for (let xNode of xRootNode.childNodes) {
                if (xNode.className !== "moz-cite-prefix" && xNode.tagName !== "BLOCKQUOTE"
                    && (xNode.nodeType == Node.TEXT_NODE || (xNode.nodeType == Node.ELEMENT_NODE && !xNode.textContent.startsWith(">")))
                    && xNode.textContent !== "") {
                    if (xNode.tagName === undefined) {
                        if (!this.bCheckSignature && xNode.textContent.startsWith("-- ")) {
                            break;
                        }
                        yield xNode;
                    }
                    else if (this._lParsableNodes.includes(xNode.tagName)) {
                        yield xNode;
                    }
                    else if (this._lRootNodes.includes(xNode.tagName)) {
                        yield* this._getParsableNodes(xNode);
                    }
                }
            }
        }
        catch (e) {
            showError(e);
        }
    }

    * getParagraphs () {
        try {
            let i = 0;
            for (let xNode of this._getParsableNodes(this.xRootNode)) {
                this.lNode.push(xNode);
                yield [i, xNode.textContent];
                i += 1;
            }
        }
        catch (e) {
            showError(e);
        }
    }

    getText () {
        try {
            let sPageText = "";
            for (let [i, sLine] of this.getParagraphs()) {
                sPageText += sLine + "\n";
            }
            return sPageText;
        }
        catch (e) {
            showError(e);
        }
    }

    getParagraph (iPara) {
        try {
            return this.lNode[iPara].textContent;
        }
        catch (e) {
            showError(e);
        }
    }

    setParagraph (iPara, sText) {
        try {
            return this.lNode[iPara].textContent = sText;
        }
        catch (e) {
            showError(e);
        }
    }

    changeParagraph (iPara, sModif, iStart, iEnd) {
        let sText = this.getParagraph(iPara);
        this.writeParagraph(iPara, sText.slice(0, iStart) + sModif + sText.slice(iEnd));
    }

    clear () {
        this.xDocument = null;
        this.xRootNode = null;
        this.lNode.length = 0;
    }
}


/*
    Editor for TextNode (Textarea or editable node)
*/
class TextNodeEditor {

    constructor (what, xResultNode=null) {
        this.xNode = null;
        this.dParagraph = new Map();
        this.bTextArea = false;
        this.bIframe = false;
        this.bResultInEvent = false; // if true, the node content is not modified, but an event is dispatched on the node with the modified text
        this.xResultNode = null; // only useful for text analysed without node
        if (xResultNode instanceof HTMLElement) {
            this.xResultNode = xResultNode;
            this.bResultInEvent = true;
        }
        if (typeof(what) == "string") {
            // SIMPLE TEXT
            if (!this.xResultNode) {
                oGrammalecte.oGCPanel.addMessageToGCPanel("⛔ Aucun champ textuel défini. Les changements ne seront pas répercutés sur la zone d’où le texte a été extrait.");
            }
            this.loadText(sText);
        }
        else if (what.nodeType && what.nodeType === 1) {
            // NODE
            this.xNode = what;
            this.bResultInEvent = Boolean(this.xNode.dataset.grammalecte_result_via_event && this.xNode.dataset.grammalecte_result_via_event == "true");
            this.bTextArea = (this.xNode.tagName == "TEXTAREA" || this.xNode.tagName == "INPUT");
            this.bIframe = (this.xNode.tagName == "IFRAME");
            if (this.bTextArea) {
                this.xNode.disabled = true;
                this.loadText(this.xNode.value);
            }
            else if (this.bIframe) {
                // iframe
                if (!this.bResultInEvent) {
                    oGrammalecte.oGCPanel.addMessageToGCPanel("⛔ La zone analysée est un cadre contenant une autre page web (“iframe”). Les changements faits ne peuvent être pas répercutés dans cette zone.");
                }
                this.loadText(this.xNode.contentWindow.document.body.innerText);
            }
            else {
                // editable node
                oGrammalecte.oGCPanel.addMessageToGCPanel("❗ La zone de texte analysée est un champ textuel enrichi susceptible de contenir des éléments non textuels qui seront effacés lors de la correction.");
                this.loadText(this.xNode.innerText);
            }
        }
    }

    loadText (sText) {
        // function also used by the text formatter
        if (typeof(sText) === "string") {
            this.dParagraph.clear();
            let i = 0;
            let iStart = 0;
            let iEnd = 0;
            sText = sText.replace(/\r\n/g, "\n").replace(/\r/g, "\n").normalize("NFC");
            while ((iEnd = sText.indexOf("\n", iStart)) !== -1) {
                this.dParagraph.set(i, sText.slice(iStart, iEnd));
                i++;
                iStart = iEnd+1;
            }
            this.dParagraph.set(i, sText.slice(iStart));
            //console.log("Paragraphs number: " + (i+1));
        }
        this.write();
    }

    clear () {
        if (this.xNode !== null) {
            this.xNode.disabled = false;
            this.bTextArea = false;
            this.bIframe = false;
            this.bResultInEvent = false;
            this.xNode = null;
            this.xResultNode = null;
        }
        this.dParagraph.clear();
    }

    getText () {
        return [...this.dParagraph.values()].join("\n").normalize("NFC");
    }

    setParagraph (iParagraph, sText) {
        this.dParagraph.set(iParagraph, sText);
        this.write();
    }

    getParagraph (iParaNum) {
        return this.dParagraph.get(iParaNum);
    }

    _eraseNodeContent () {
        while (this.xNode.firstChild) {
            this.xNode.removeChild(this.xNode.firstChild);
        }
    }

    write () {
        if (this.xNode !== null) {
            if (this.bResultInEvent) {
                const xEvent = new CustomEvent("GrammalecteResult", { detail: JSON.stringify({ sType: "text", sText: this.getText() }) });
                this.xNode.dispatchEvent(xEvent);
                //console.log("[Grammalecte debug] Text sent to xNode via event:", xEvent.detail);
            }
            else if (this.bTextArea) {
                this.xNode.value = this.getText();
                //console.log("[Grammalecte debug] text written in textarea:", this.getText());
            }
            else if (this.bIframe) {
                //console.log(this.getText());
            }
            else {
                this._eraseNodeContent();
                this.dParagraph.forEach((val, key) => {
                    this.xNode.appendChild(document.createTextNode(val.normalize("NFC")));
                    this.xNode.appendChild(document.createElement("br"));
                });
                //console.log("[Grammalecte debug] text written in editable node:", this.getText());
            }
        }
        else if (this.xResultNode !== null) {
            const xEvent = new CustomEvent("GrammalecteResult", { detail: JSON.stringify({ sType: "text", sText: this.getText() }) });
            this.xResultNode.dispatchEvent(xEvent);
            //console.log("[Grammalecte debug] Text sent to xResultNode via event:", xEvent.detail);
        }
    }
}
