/*
This file is part of the Notesnook project (https://notesnook.com/)

Copyright (C) 2023 Streetwriters (Private) Limited

This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/
import { DOMParser as ProsemirrorDOMParser } from "@tiptap/pm/model";
import { encodeNonAsciiHTML } from "entities";
import { inferLanguage } from "../code-block";
import { hasPermission } from "../../types";
export class ClipboardDOMParser extends ProsemirrorDOMParser {
    static fromSchema(schema) {
        return (schema.cached.clipboardDomParser ||
            (schema.cached.clipboardDomParser = new ClipboardDOMParser(schema, ProsemirrorDOMParser.schemaRules(schema))));
    }
    parseSlice(dom, options) {
        if (dom instanceof HTMLElement || dom instanceof Document) {
            convertGoogleDocsChecklist(dom);
            formatCodeblocks(dom);
            convertBrToSingleSpacedParagraphs(dom);
            removeImages(dom);
        }
        return super.parseSlice(dom, options);
    }
}
export function formatCodeblocks(dom) {
    var _a, _b;
    for (const pre of dom.querySelectorAll("pre")) {
        pre.innerHTML = (_a = pre.innerHTML) === null || _a === void 0 ? void 0 : _a.replaceAll(/<br.*?>/g, "\n");
        const codeAsText = pre.textContent;
        const languageElement = pre.querySelector('[class*="language-"],[class*="lang-"]');
        const language = inferLanguage(languageElement || pre);
        if (language)
            pre.classList.add(`language-${language}`);
        const code = document.createElement("code");
        code.innerHTML = encodeNonAsciiHTML(codeAsText || "");
        pre.replaceChildren(code);
    }
    for (const div of dom.querySelectorAll(".w3-code")) {
        div.innerHTML = (_b = div.innerHTML) === null || _b === void 0 ? void 0 : _b.replaceAll(/<br.*?>/g, "\n");
        const codeAsText = div.textContent;
        const pre = document.createElement("pre");
        const code = document.createElement("code");
        code.innerHTML = encodeNonAsciiHTML(codeAsText || "");
        pre.replaceChildren(code);
        div.replaceChildren(pre);
    }
}
export function convertBrToSingleSpacedParagraphs(dom) {
    for (const br of dom.querySelectorAll("br")) {
        let paragraph = br.closest("p");
        if (!paragraph) {
            // we split and wrap all text nodes into their own single spaced
            // paragraphs
            const nodes = getSiblingTextNodes(br);
            if (nodes.length > 0) {
                paragraph = document.createElement("p");
                paragraph.dataset.spacing = "single";
                paragraph.append(...nodes);
                br.replaceWith(paragraph);
                continue;
            }
            // we convert the next pargraph into a single spaced paragraph
            if (br.nextElementSibling instanceof HTMLParagraphElement) {
                br.nextElementSibling.dataset.spacing = "single";
            }
            // just convert all br tags into single spaced paragraphs
            const newParagraph = document.createElement("p");
            newParagraph.dataset.spacing = "single";
            br.replaceWith(newParagraph);
        }
        if (paragraph &&
            (paragraph.childNodes.length === 1 ||
                !paragraph.textContent ||
                paragraph.textContent.trim().length === 0)) {
            // if paragraph is empty, we clean out the paragraph and move on.
            paragraph.innerHTML = "";
            continue;
        }
        if (paragraph) {
            splitOn(paragraph, br);
            const children = Array.from(paragraph.childNodes);
            const newParagraph = document.createElement("p");
            newParagraph.dataset.spacing = "single";
            newParagraph.append(...children.slice(children.indexOf(br) + 1));
            paragraph.insertAdjacentElement("afterend", newParagraph);
            br.remove();
        }
    }
}
export function convertGoogleDocsChecklist(dom) {
    var _a, _b;
    for (const li of dom.querySelectorAll(`ul li[role="checkbox"]`)) {
        if (!((_a = li.parentElement) === null || _a === void 0 ? void 0 : _a.classList.contains("checklist")))
            li.parentElement.classList.add("checklist");
        li.className = "checklist--item";
        if (((_b = li.firstElementChild) === null || _b === void 0 ? void 0 : _b.tagName) === "IMG")
            li.firstElementChild.remove();
        if (li.getAttribute("aria-checked") === "true") {
            li.classList.add("checked");
        }
    }
}
export function removeImages(dom) {
    let canInsertImages = null;
    for (const img of dom.querySelectorAll(`img`)) {
        canInsertImages = canInsertImages !== null && canInsertImages !== void 0 ? canInsertImages : hasPermission("insertImage");
        if (!canInsertImages)
            img.remove();
    }
}
function splitOn(bound, cutElement) {
    let grandparent = null;
    for (let parent = cutElement.parentNode; bound != parent; parent = grandparent) {
        if (parent) {
            const right = parent.cloneNode(false);
            while (cutElement.nextSibling)
                right.appendChild(cutElement.nextSibling);
            grandparent = parent.parentNode;
            grandparent === null || grandparent === void 0 ? void 0 : grandparent.insertBefore(right, parent.nextSibling);
            grandparent === null || grandparent === void 0 ? void 0 : grandparent.insertBefore(cutElement, right);
        }
    }
}
const inlineTags = new Set([
    "A",
    "ABBR",
    "B",
    "BDI",
    "BDO",
    // "BR",
    "CITE",
    "CODE",
    "DATA",
    "DFN",
    "EM",
    "I",
    // "IMG",
    // "INPUT",
    "KBD",
    "LABEL",
    "MARK",
    "Q",
    "S",
    "SAMP",
    "SMALL",
    "SPAN",
    "STRONG",
    "SUB",
    "SUP",
    "TEXTAREA",
    "TIME",
    "U",
    "VAR",
    "WBR"
]);
function getSiblingTextNodes(element) {
    const siblings = [];
    let sibling = element;
    while ((sibling = sibling.previousSibling)) {
        if (isElement(sibling) && !inlineTags.has(sibling.tagName))
            break;
        else
            siblings.push(sibling);
    }
    return siblings;
}
function isElement(node) {
    return node.nodeType === Node.ELEMENT_NODE;
}
