/* lib/browser/content/message - A library for executing the text messaging application. */ import browser from "../utilities/browser.js";
import common from "../../common/common.js";
import configuration from "./configuration.js";
import modal from "../utilities/modal.js";
import modal_configuration from "../utilities/modal_configurations.js";
import network from "../utilities/network.js";
import util from "../utilities/util.js";
// cspell:words agenttype, arrowdown, arrowup
/**
 * Generates text message modals and all associated interactions.
 * ```typescript
 * interface module_message {
 *     content: {
 *         footer: (mode:messageMode, value:string) => HTMLElement;                 // Called from modal.create to supply the footer area modal content.
 *         modal : (text:string, placeholder:string) => [HTMLElement, HTMLElement]; // Generates a message modal.
 *     };
 *     events: {
 *         keySubmit  : (event:KeyboardEvent) => void;            // Submits a text message on key press, such as pressing the 'Enter' key.
 *         modeToggle : (event:MouseEvent) => void;               // Toggles between code type input and text type input.
 *         shareButton: (event:MouseEvent) => void;               // Creates a message button for the *share* modals.
 *         submit     : (event:KeyboardEvent|MouseEvent) => void; // Submit event handler to take message text into a data object for transmission across a network.
 *     };
 *     tools: {
 *         populate:(modalId:string) => void;                                           // Populate stored messages into message modals.
 *         post    : (item:message_item, target:messageTarget, modalId:string) => void; // Visually display the submitted and received messages as modal content.
 *         receive : (socketData:socketData) => void;                                   // Receives message updates from the network.
 *     };
 * }
 * type messageMode = "code" | "text";
 * type messageTarget = "agentFrom" | "agentTo";
 * ``` */ const message = {
    /* Render a message modal */ content: {
        modal: function browser_content_message_content(text, placeholder) {
            const content = document.createElement("div"), footer = document.createElement("div"), table = document.createElement("table"), button = document.createElement("button"), pButton = document.createElement("p"), pToggle = document.createElement("p"), spanClear = document.createElement("span"), spanToggle = document.createElement("span"), spanTextArea = document.createElement("span"), textArea = document.createElement("textarea"), label = document.createElement("label"), inputControl = function browser_content_message_content_input(type) {
                const input = document.createElement("input"), label = document.createElement("label");
                if (placeholder === type) {
                    input.checked = true;
                }
                input.name = name;
                input.onclick = message.events.modeToggle;
                input.type = "radio";
                input.value = type;
                label.appendChild(input);
                label.appendText(`${common.capitalize(type)} Mode`);
                pToggle.appendChild(label);
            }, name = `message-${Math.random()}-mode`;
            table.setAttribute("class", "message-content");
            table.appendChild(document.createElement("tbody"));
            content.appendChild(table);
            inputControl("text");
            inputControl("code");
            pToggle.setAttribute("class", "message-toggle");
            pToggle.appendChild(spanToggle);
            footer.appendChild(pToggle);
            textArea.onmouseup = modal.events.footerResize;
            textArea.onblur = modal.events.textSave;
            textArea.onkeyup = modal.events.textTimer;
            textArea.placeholder = "Write a message.";
            textArea.value = text;
            textArea.setAttribute("class", placeholder);
            if (placeholder === "code") {
                textArea.onkeyup = null;
            } else {
                textArea.onkeyup = message.events.keySubmit;
            }
            label.setAttribute("class", "text-pad");
            spanTextArea.appendText("Write a message.");
            label.appendChild(spanTextArea);
            label.appendChild(textArea);
            button.appendText("✉ Send Message");
            button.setAttribute("class", "confirm");
            button.setAttribute("type", "button");
            button.onclick = message.events.submit;
            pButton.appendChild(button);
            pButton.setAttribute("class", "footer-buttons");
            footer.setAttribute("class", "footer");
            footer.appendChild(label);
            footer.appendChild(pButton);
            spanClear.setAttribute("class", "clear");
            footer.appendChild(spanClear);
            return [
                content,
                footer
            ];
        },
        /* Called from modal.create to supply the footer area modal content */ footer: function browser_content_message_footer(mode, value) {
            const textArea = document.createElement("textarea"), label = document.createElement("label"), span = document.createElement("span"), button = document.createElement("button"), paragraph = document.createElement("p"), footer = document.createElement("div"), clear = document.createElement("span");
            textArea.onmouseup = modal.events.footerResize;
            textArea.onblur = modal.events.textSave;
            textArea.onkeyup = modal.events.textTimer;
            textArea.placeholder = "Write a message.";
            textArea.value = value;
            textArea.setAttribute("class", mode);
            if (mode === "code") {
                textArea.onkeyup = null;
            } else {
                textArea.onkeyup = message.events.keySubmit;
            }
            label.setAttribute("class", "text-pad");
            span.appendText("Write a message.");
            label.appendChild(span);
            label.appendChild(textArea);
            button.appendText("✉ Send Message");
            button.setAttribute("class", "confirm");
            button.setAttribute("type", "button");
            button.onclick = message.events.submit;
            paragraph.appendChild(button);
            paragraph.setAttribute("class", "footer-buttons");
            footer.setAttribute("class", "footer");
            footer.appendChild(label);
            footer.appendChild(paragraph);
            clear.setAttribute("class", "clear");
            footer.appendChild(clear);
            return footer;
        }
    },
    events: {
        /* Submits a text message on key press, such as pressing the 'Enter' key. */ keySubmit: function browser_content_message_keySubmit(event) {
            const input = event.target, box = input.getAncestor("box", "class"), id = box.getAttribute("id"), keyboardEvent = window.event, key = keyboardEvent.key.toLowerCase();
            if (key === "enter" && keyboardEvent.shiftKey === false && keyboardEvent.altKey === false && keyboardEvent.ctrlKey === false) {
                message.events.submit(event);
            } else if (key === "arrowup" || key === "arrowdown") {
                const total = browser.message.length, agency = util.getAgent(input), agentFrom = agency[2] === "device" ? browser.identity.hashDevice : browser.identity.hashUser;
                let step = browser.ui.modals[id].historyIndex === undefined ? total : browser.ui.modals[id].historyIndex;
                if (key === "arrowup") {
                    if (step > 0) {
                        do {
                            step = step - 1;
                        }while (step > -1 && (browser.message[step].agentType !== agency[2] || browser.message[step].agentTo !== agency[0] || browser.message[step].agentFrom !== agentFrom))
                        if (step > -1 && browser.message[step].agentType === agency[2] && browser.message[step].agentTo === agency[0] && browser.message[step].agentFrom === agentFrom) {
                            input.value = browser.message[step].message;
                            browser.ui.modals[id].historyIndex = step;
                        }
                    }
                } else {
                    if (step < total) {
                        do {
                            step = step + 1;
                        }while (step < total && (browser.message[step].agentType !== agency[2] || browser.message[step].agentTo !== agency[0] || browser.message[step].agentFrom !== agentFrom))
                        if (step === total) {
                            input.value = browser.ui.modals[id].text_value;
                            browser.ui.modals[id].historyIndex = total;
                        } else if (browser.message[step].agentType === agency[2] && browser.message[step].agentTo === agency[0] && browser.message[step].agentFrom === agentFrom) {
                            input.value = browser.message[step].message;
                            browser.ui.modals[id].historyIndex = step;
                        }
                    }
                }
            } else {
                browser.ui.modals[id].text_value = input.value;
            }
        },
        /* Toggle message textarea input between text input and code input preferences */ modeToggle: function browser_content_message_modeToggle(event) {
            const element = event.target, box = element.getAncestor("box", "class"), id = box.getAttribute("id"), textarea = box.getElementsByClassName("footer")[0].getElementsByTagName("textarea")[0], value = element.value;
            browser.ui.modals[id].text_placeholder = value;
            browser.ui.modals[id].text_value = textarea.value;
            configuration.tools.radio(element);
            if (value === "code") {
                textarea.onkeyup = null;
            } else {
                textarea.onkeyup = message.events.keySubmit;
            }
            textarea.setAttribute("class", value);
            network.configuration();
        },
        /* Generate a message modal from a share button */ shareButton: function browser_content_message_shareButton(event) {
            const element = event.target, source = element.lowName() === "button" ? element : element.parentNode, className = source.getAttribute("class"), box = element.getAncestor("box", "class"), grandParent = source.parentNode.parentNode, agentAttribute = box.dataset.agent, agentHash = agentAttribute === "" ? className === "share-tool-message" ? grandParent.dataset.hash : browser.identity.hashDevice : agentAttribute, agentType = agentAttribute === "" ? className === "share-tool-message" ? grandParent.getAttribute("class") : source.getAttribute("class").replace("text-button-", "") : box.dataset.agenttype, modals = document.getModalsByModalType("message");
            let a = modals.length, messageModal = null;
            if (a > 0) {
                do {
                    a = a - 1;
                    if (modals[a].dataset.agenttype === agentType && modals[a].dataset.agent === agentHash) {
                        modals[a].click();
                        return;
                    }
                }while (a > 0)
            }
            messageModal = modal_configuration.modals.message(event, null);
            message.tools.populate(messageModal.getAttribute("id"));
        },
        /* Submit event handler to take message text into a data object for transmission across a network. */ submit: function browser_content_message_submit(event) {
            const element = event.target, agency = util.getAgent(element), box = element.getAncestor("box", "class"), footer = element.getAncestor("footer", "class"), textArea = footer.getElementsByTagName("textarea")[0], payload = {
                agentFrom: agency[2] === "device" ? browser.identity.hashDevice : browser.identity.hashUser,
                agentTo: agency[0],
                agentType: agency[2],
                date: Date.now(),
                message: textArea.value,
                mode: textArea.getAttribute("class"),
                userDevice: false
            };
            delete browser.ui.modals[box.getAttribute("id")].historyIndex;
            if (agency[2] === "user" && agency[0] === browser.identity.hashUser) {
                payload.agentTo = "user";
            } else if (agency[2] === "device" && agency[0] === browser.identity.hashDevice) {
                payload.agentTo = "device";
            } else if (agency[0] === "") {
                payload.agentTo = "";
            }
            message.tools.post(payload, "agentTo", box.getAttribute("id"));
            network.send([
                payload
            ], "message");
            textArea.value = "";
        }
    },
    tools: {
        /* Populate stored messages into message modals */ populate: function browser_content_message_populate(modalId) {
            if (browser.message.length > 0) {
                const messageLength = browser.message.length;
                let messageIndex = 0;
                do {
                    if (browser.message[messageIndex].agentType === "device") {
                        if (browser.message[messageIndex].agentTo === browser.identity.hashDevice) {
                            message.tools.post(browser.message[messageIndex], "agentFrom", modalId);
                        } else {
                            message.tools.post(browser.message[messageIndex], "agentTo", modalId);
                        }
                    } else if (browser.message[messageIndex].agentType === "user") {
                        if (browser.message[messageIndex].agentTo === browser.identity.hashUser) {
                            message.tools.post(browser.message[messageIndex], "agentFrom", modalId);
                        } else {
                            message.tools.post(browser.message[messageIndex], "agentTo", modalId);
                        }
                    }
                    messageIndex = messageIndex + 1;
                }while (messageIndex < messageLength)
            }
        },
        /* Visually display a text message */ post: function browser_content_message_post(item, target, modalId) {
            const tr = document.createElement("tr"), meta = document.createElement("th"), messageCell = document.createElement("td"), self = function browser_content_message_post_self(hash) {
                if (item.agentType === "device" && hash === browser.identity.hashDevice) {
                    return true;
                }
                if (item.agentType === "user" && hash === browser.identity.hashUser) {
                    return true;
                }
                return false;
            }, unicode = function browser_content_message_post_unicode(reference) {
                const output = [];
                reference.split("\\u").forEach(function browser_content_message_post_unicode(value) {
                    output.push(String.fromCharCode(Number(`0x${value}`)));
                });
                return output.join("");
            }, decimal = function browser_content_message_post_decimal(reference) {
                return String.fromCodePoint(Number(reference.replace("&#", "").replace(";", "")));
            }, html = function browser_content_message_post_html(reference) {
                return String.fromCodePoint(Number(reference.replace("&#x", "0x").replace(";", "")));
            }, writeMessage = function browser_content_message_post_writeMessage(box) {
                const tbody = box.getElementsByClassName("message-content")[0].getElementsByTagName("tbody")[0], posts = tbody.getElementsByTagName("tr"), postsLength = posts.length;
                if (postsLength > 0) {
                    if (posts[0].dataset.agentFrom === item.agentFrom) {
                        if (posts[0].getAttribute("class") === null) {
                            posts[0].setAttribute("class", "prior");
                        } else {
                            posts[0].setAttribute("class", `${posts[0].getAttribute("class")} prior`);
                        }
                        if (self(item.agentFrom) === true) {
                            tr.setAttribute("class", "message-self");
                        }
                    } else {
                        if (self(item.agentFrom) === true) {
                            tr.setAttribute("class", "base message-self");
                        } else {
                            tr.setAttribute("class", "base");
                        }
                    }
                } else {
                    if (self(item.agentFrom) === true) {
                        tr.setAttribute("class", "base message-self");
                    } else {
                        tr.setAttribute("class", "base");
                    }
                }
                tbody.insertBefore(tr.cloneNode(true), tbody.firstChild);
                // flag whether to create a new message modal
                writeTest = true;
            }, date = new Date(item.date), modals = document.getModalsByModalType("message");
            let index = modals.length, writeTest = browser.loading === true || modalId !== "", modalAgent, messageText = item.mode === "code" ? `<p>${item.message}</p>` : `<p>${item.message.replace(/^\s+/, "").replace(/\s+$/, "").replace(RegExp("(?<!\\\\)(\\\\u[0-9a-f]{4})+", "g"), unicode).replace(/&#\d+;/g, decimal).replace(/&#x[0-9a-f]+;/, html).replace(/(\r?\n)+/g, "</p><p>")}</p>`;
            if (item.mode === "text") {
                const strings = messageText.split("http"), stringsLength = strings.length;
                if (stringsLength > 1) {
                    let a = 1, b = 0, segment = 0;
                    do {
                        if (/^s?:\/\//.test(strings[a]) === true) {
                            b = 0;
                            segment = strings[a].length;
                            do {
                                if (/\s|</.test(strings[a].charAt(b)) === true) {
                                    break;
                                }
                                b = b + 1;
                            }while (b < segment)
                            if (b === segment) {
                                strings[a] = `<a target="_blank" href="http${strings[a]}">http${strings[a]}</a>`;
                            } else {
                                strings[a] = `<a target="_blank" href="http${strings[a].slice(0, b)}">http${strings[a].slice(0, b)}</a>${strings[a].slice(b)}`;
                            }
                        }
                        a = a + 1;
                    }while (a < stringsLength)
                    messageText = strings.join("");
                }
            }
            // eslint-disable-next-line
            messageCell.innerHTML = messageText;
            messageCell.setAttribute("class", item.mode);
            tr.setAttribute("data-agentFrom", item.agentFrom);
            if (item.agentType === "user" && item.agentFrom === browser.identity.hashUser) {
                const strong = document.createElement("strong"), em = document.createElement("em");
                strong.appendText(browser.identity.nameUser);
                em.appendText(common.dateFormat(date));
                meta.appendChild(strong);
                meta.appendText(" ");
                meta.appendChild(em);
            } else if (item.agentType === "device" && item.agentFrom === browser.identity.hashDevice) {
                const strong = document.createElement("strong"), em = document.createElement("em");
                strong.appendText(browser.identity.nameDevice);
                em.appendText(common.dateFormat(date));
                meta.appendChild(strong);
                meta.appendText(" ");
                meta.appendChild(em);
            } else {
                const strong = document.createElement("strong"), em = document.createElement("em"), span = document.createElement("span");
                span.appendText(common.capitalize(item.agentType));
                strong.appendText(browser.agents[item.agentType][item.agentFrom].name);
                em.appendText(common.dateFormat(date));
                meta.appendChild(span);
                meta.appendText(" ");
                meta.appendChild(strong);
                meta.appendText(" ");
                meta.appendChild(em);
            }
            tr.appendChild(meta);
            tr.appendChild(messageCell);
            // loop through modals
            if (index > 0) {
                do {
                    index = index - 1;
                    modalAgent = modals[index].dataset.agent;
                    if ((modalId === "" || modals[index].getAttribute("id") === modalId) && (item[target] === "all" || modals[index].dataset.agenttype === "user" && (item[target] === "user" || item.agentType === "user" && item[target] === modalAgent) || modals[index].dataset.agenttype === "device" && (item[target] === "device" || item.agentType === "device" && item[target] === modalAgent))) {
                        writeMessage(modals[index]);
                    }
                }while (index > 0)
            }
            // creates a new message modal if none matched
            if (writeTest === false) {
                const identity = item.agentFrom !== browser.identity.hashDevice, modalItem = modal_configuration.modals.message(null, {
                    agent: item.agentFrom,
                    agentIdentity: identity,
                    agentType: item.agentType,
                    content: null,
                    footer: null,
                    inputs: [
                        "close",
                        "maximize",
                        "minimize"
                    ],
                    read_only: false,
                    text_placeholder: "text",
                    text_value: "",
                    title_supplement: identity === true ? null : `all ${item.agentType}s`,
                    type: "message",
                    width: 800
                });
                writeMessage(modalItem);
            }
        },
        /* Receives messages from the network */ receive: function browser_content_message_receive(socketData) {
            const messageData = socketData.data, agentFrom = messageData[0].agentFrom, agentType = messageData[0].agentType, target = agentType === "user" && agentFrom === browser.identity.hashUser || agentType === "device" && agentFrom === browser.identity.hashDevice ? "agentTo" : "agentFrom";
            document.getElementById("message-update").appendText(messageData[0].message, true);
            messageData.forEach(function browser_socketMessage_messagePost_each(item) {
                message.tools.post(item, target, "");
            });
            if (browser.visible === false && Notification.permission === "granted") {
                const messageBody = messageData[0].message, messageString = messageBody.length > 100 ? `${messageBody.slice(0, 100)}\u2026` : messageBody, notifyOptions = {
                    body: `Received new message from ${agentType} ${browser.agents[agentType][agentFrom].name}.\r\n\r\n${messageString}`,
                    vibrate: [
                        200,
                        100
                    ]
                }, notify = new Notification(`${browser.title} - New Message`, notifyOptions);
                notify.onshow = function browser_content_message_receive() {
                    notify.close();
                };
            }
        }
    }
};
export default message;
