function elemIsCodeBlock(elem) {
    return elem.tagName.toLowerCase() === 'code-block';
}
/**
 * @param {Editor} editor
 * @param {String} code
 * @param {String} language
 * @param {function(string, string)} callback (Receives (code: string,language: string)
 */
function showPopup(editor, code, language, callback) {
    window.components.first('code-editor').open(code, language, (newCode, newLang) => {
        callback(newCode, newLang)
        editor.focus()
    });
}
/**
 * @param {Editor} editor
 * @param {CodeBlockElement} codeBlock
 */
function showPopupForCodeBlock(editor, codeBlock) {
    showPopup(editor, codeBlock.getContent(), codeBlock.getLanguage(), (newCode, newLang) => {
        codeBlock.setContent(newCode, newLang);
    });
}
/**
 * Define our custom code-block HTML element that we use.
 * Needs to be delayed since it needs to be defined within the context of the
 * child editor window and document, hence its definition within a callback.
 * @param {Editor} editor
 */
function defineCodeBlockCustomElement(editor) {
    const doc = editor.getDoc();
    const win = doc.defaultView;
    class CodeBlockElement extends win.HTMLElement {
        constructor() {
            super();
            this.attachShadow({mode: 'open'});
            const linkElem = document.createElement('link');
            linkElem.setAttribute('rel', 'stylesheet');
            linkElem.setAttribute('href', window.baseUrl('/dist/styles.css'));
            const cmContainer = document.createElement('div');
            cmContainer.style.pointerEvents = 'none';
            cmContainer.contentEditable = 'false';
            cmContainer.classList.add('CodeMirrorContainer');
            this.shadowRoot.append(linkElem, cmContainer);
        }
        getLanguage() {
            const getLanguageFromClassList = (classes) => {
                const langClasses = classes.split(' ').filter(cssClass => cssClass.startsWith('language-'));
                return (langClasses[0] || '').replace('language-', '');
            };
            const code = this.querySelector('code');
            const pre = this.querySelector('pre');
            return getLanguageFromClassList(pre.className) || (code && getLanguageFromClassList(code.className)) || '';
        }
        setContent(content, language) {
            if (this.cm) {
                importVersioned('code').then(Code => {
                    Code.setContent(this.cm, content);
                    Code.setMode(this.cm, language, content);
                });
            }
            let pre = this.querySelector('pre');
            if (!pre) {
                pre = doc.createElement('pre');
                this.append(pre);
            }
            pre.innerHTML = '';
            const code = doc.createElement('code');
            pre.append(code);
            code.innerText = content;
            code.className = `language-${language}`;
        }
        getContent() {
            const code = this.querySelector('code') || this.querySelector('pre');
            const tempEl = document.createElement('pre');
            tempEl.innerHTML = code.innerHTML.replace().replace(/
/gi ,'\n').replace(/\ufeff/g, '');
            return tempEl.textContent;
        }
        connectedCallback() {
            const connectedTime = Date.now();
            if (this.cm) {
                return;
            }
            this.cleanChildContent();
            const content = this.getContent();
            const lines = content.split('\n').length;
            const height = (lines * 19.2) + 18 + 24;
            this.style.height = `${height}px`;
            const container = this.shadowRoot.querySelector('.CodeMirrorContainer');
            const renderCodeMirror = (Code) => {
                this.cm = Code.wysiwygView(container, content, this.getLanguage());
                Code.updateLayout(this.cm);
                setTimeout(() => {
                    this.style.height = null;
                }, 1);
            };
            window.importVersioned('code').then((Code) => {
                const timeout = (Date.now() - connectedTime < 20) ? 20 : 0;
                setTimeout(() => renderCodeMirror(Code), timeout);
            });
        }
        cleanChildContent() {
            const pre = this.querySelector('pre');
            if (!pre) return;
            for (const preChild of pre.childNodes) {
                if (preChild.nodeName === '#text' && preChild.textContent === '') {
                    preChild.remove();
                }
            }
        }
    }
    win.customElements.define('code-block', CodeBlockElement);
}
/**
 * @param {Editor} editor
 * @param {String} url
 */
function register(editor, url) {
    editor.ui.registry.addIcon('codeblock', '')
    editor.ui.registry.addButton('codeeditor', {
        tooltip: 'Insert code block',
        icon: 'codeblock',
        onAction() {
            editor.execCommand('codeeditor');
        }
    });
    editor.addCommand('codeeditor', () => {
        const selectedNode = editor.selection.getNode();
        const doc = selectedNode.ownerDocument;
        if (elemIsCodeBlock(selectedNode)) {
            showPopupForCodeBlock(editor, selectedNode);
        } else {
            const textContent = editor.selection.getContent({format: 'text'});
            showPopup(editor, textContent, '', (newCode, newLang) => {
                const pre = doc.createElement('pre');
                const code = doc.createElement('code');
                code.classList.add(`language-${newLang}`);
                code.innerText = newCode;
                pre.append(code);
                editor.insertContent(pre.outerHTML);
            });
        }
    });
    editor.on('dblclick', event => {
        let selectedNode = editor.selection.getNode();
        if (elemIsCodeBlock(selectedNode)) {
            showPopupForCodeBlock(editor, selectedNode);
        }
    });
    editor.on('PreInit', () => {
        editor.parser.addNodeFilter('pre', function(elms) {
            for (const el of elms) {
                const wrapper = new tinymce.html.Node.create('code-block', {
                    contenteditable: 'false',
                });
                const spans = el.getAll('span');
                for (const span of spans) {
                    span.unwrap();
                }
                el.attr('style', null);
                el.wrap(wrapper);
            }
        });
        editor.parser.addNodeFilter('code-block', function(elms) {
            for (const el of elms) {
                el.attr('contenteditable', 'false');
            }
        });
        editor.serializer.addNodeFilter('code-block', function(elms) {
            for (const el of elms) {
                el.unwrap();
            }
        });
    });
    editor.on('PreInit', () => {
        defineCodeBlockCustomElement(editor);
    });
}
/**
 * @param {WysiwygConfigOptions} options
 * @return {register}
 */
export function getPlugin(options) {
    return register;
}