import { html } from 'parse5';
import { Element, Document, ProcessingInstruction, Comment, Text, isDirective, isText, isComment, isTag, } from 'domhandler';
function enquoteDoctypeId(id) {
    const quote = id.includes('"') ? "'" : '"';
    return quote + id + quote;
}
/** @internal */
export function serializeDoctypeContent(name, publicId, systemId) {
    let str = '!DOCTYPE ';
    if (name) {
        str += name;
    }
    if (publicId) {
        str += ` PUBLIC ${enquoteDoctypeId(publicId)}`;
    }
    else if (systemId) {
        str += ' SYSTEM';
    }
    if (systemId) {
        str += ` ${enquoteDoctypeId(systemId)}`;
    }
    return str;
}
export const adapter = {
    // Re-exports from domhandler
    isCommentNode: isComment,
    isElementNode: isTag,
    isTextNode: isText,
    //Node construction
    createDocument() {
        const node = new Document([]);
        node['x-mode'] = html.DOCUMENT_MODE.NO_QUIRKS;
        return node;
    },
    createDocumentFragment() {
        return new Document([]);
    },
    createElement(tagName, namespaceURI, attrs) {
        const attribs = Object.create(null);
        const attribsNamespace = Object.create(null);
        const attribsPrefix = Object.create(null);
        for (let i = 0; i < attrs.length; i++) {
            const attrName = attrs[i].name;
            attribs[attrName] = attrs[i].value;
            attribsNamespace[attrName] = attrs[i].namespace;
            attribsPrefix[attrName] = attrs[i].prefix;
        }
        const node = new Element(tagName, attribs, []);
        node.namespace = namespaceURI;
        node['x-attribsNamespace'] = attribsNamespace;
        node['x-attribsPrefix'] = attribsPrefix;
        return node;
    },
    createCommentNode(data) {
        return new Comment(data);
    },
    createTextNode(value) {
        return new Text(value);
    },
    //Tree mutation
    appendChild(parentNode, newNode) {
        const prev = parentNode.children[parentNode.children.length - 1];
        if (prev) {
            prev.next = newNode;
            newNode.prev = prev;
        }
        parentNode.children.push(newNode);
        newNode.parent = parentNode;
    },
    insertBefore(parentNode, newNode, referenceNode) {
        const insertionIdx = parentNode.children.indexOf(referenceNode);
        const { prev } = referenceNode;
        if (prev) {
            prev.next = newNode;
            newNode.prev = prev;
        }
        referenceNode.prev = newNode;
        newNode.next = referenceNode;
        parentNode.children.splice(insertionIdx, 0, newNode);
        newNode.parent = parentNode;
    },
    setTemplateContent(templateElement, contentElement) {
        adapter.appendChild(templateElement, contentElement);
    },
    getTemplateContent(templateElement) {
        return templateElement.children[0];
    },
    setDocumentType(document, name, publicId, systemId) {
        const data = serializeDoctypeContent(name, publicId, systemId);
        let doctypeNode = document.children.find((node) => isDirective(node) && node.name === '!doctype');
        if (doctypeNode) {
            doctypeNode.data = data !== null && data !== void 0 ? data : null;
        }
        else {
            doctypeNode = new ProcessingInstruction('!doctype', data);
            adapter.appendChild(document, doctypeNode);
        }
        doctypeNode['x-name'] = name;
        doctypeNode['x-publicId'] = publicId;
        doctypeNode['x-systemId'] = systemId;
    },
    setDocumentMode(document, mode) {
        document['x-mode'] = mode;
    },
    getDocumentMode(document) {
        return document['x-mode'];
    },
    detachNode(node) {
        if (node.parent) {
            const idx = node.parent.children.indexOf(node);
            const { prev, next } = node;
            node.prev = null;
            node.next = null;
            if (prev) {
                prev.next = next;
            }
            if (next) {
                next.prev = prev;
            }
            node.parent.children.splice(idx, 1);
            node.parent = null;
        }
    },
    insertText(parentNode, text) {
        const lastChild = parentNode.children[parentNode.children.length - 1];
        if (lastChild && isText(lastChild)) {
            lastChild.data += text;
        }
        else {
            adapter.appendChild(parentNode, adapter.createTextNode(text));
        }
    },
    insertTextBefore(parentNode, text, referenceNode) {
        const prevNode = parentNode.children[parentNode.children.indexOf(referenceNode) - 1];
        if (prevNode && isText(prevNode)) {
            prevNode.data += text;
        }
        else {
            adapter.insertBefore(parentNode, adapter.createTextNode(text), referenceNode);
        }
    },
    adoptAttributes(recipient, attrs) {
        for (let i = 0; i < attrs.length; i++) {
            const attrName = attrs[i].name;
            if (recipient.attribs[attrName] === undefined) {
                recipient.attribs[attrName] = attrs[i].value;
                recipient['x-attribsNamespace'][attrName] = attrs[i].namespace;
                recipient['x-attribsPrefix'][attrName] = attrs[i].prefix;
            }
        }
    },
    //Tree traversing
    getFirstChild(node) {
        return node.children[0];
    },
    getChildNodes(node) {
        return node.children;
    },
    getParentNode(node) {
        return node.parent;
    },
    getAttrList(element) {
        return element.attributes;
    },
    //Node data
    getTagName(element) {
        return element.name;
    },
    getNamespaceURI(element) {
        return element.namespace;
    },
    getTextNodeContent(textNode) {
        return textNode.data;
    },
    getCommentNodeContent(commentNode) {
        return commentNode.data;
    },
    getDocumentTypeNodeName(doctypeNode) {
        var _a;
        return (_a = doctypeNode['x-name']) !== null && _a !== void 0 ? _a : '';
    },
    getDocumentTypeNodePublicId(doctypeNode) {
        var _a;
        return (_a = doctypeNode['x-publicId']) !== null && _a !== void 0 ? _a : '';
    },
    getDocumentTypeNodeSystemId(doctypeNode) {
        var _a;
        return (_a = doctypeNode['x-systemId']) !== null && _a !== void 0 ? _a : '';
    },
    //Node types
    isDocumentTypeNode(node) {
        return isDirective(node) && node.name === '!doctype';
    },
    // Source code location
    setNodeSourceCodeLocation(node, location) {
        if (location) {
            node.startIndex = location.startOffset;
            node.endIndex = location.endOffset;
        }
        node.sourceCodeLocation = location;
    },
    getNodeSourceCodeLocation(node) {
        return node.sourceCodeLocation;
    },
    updateNodeSourceCodeLocation(node, endLocation) {
        if (endLocation.endOffset != null)
            node.endIndex = endLocation.endOffset;
        node.sourceCodeLocation = {
            ...node.sourceCodeLocation,
            ...endLocation,
        };
    },
};
