var __assign = (this && this.__assign) || function () {
    __assign = Object.assign || function(t) {
        for (var s, i = 1, n = arguments.length; i < n; i++) {
            s = arguments[i];
            for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
                t[p] = s[p];
        }
        return t;
    };
    return __assign.apply(this, arguments);
};
import { BlockType } from '../../../models/enums';
import { setCursorPosition, getSelectedRange } from '../../../common/utils/selection';
import { getLanguageItems } from '../../../common/utils/data';
import { getBlockModelById, getParentBlock } from '../../../common/utils/block';
import { decoupleReference, getNormalizedKey } from '../../../common/utils/common';
import { events } from '../../../common/constant';
import { BlockFactory } from '../../services/index';
import { createElement } from '@syncfusion/ej2-base';
import { sanitizeBlock } from '../../../common/utils/transform';
var CodeRenderer = /** @class */ (function () {
    function CodeRenderer(manager) {
        this.ctrlAPressed = false;
        this.INDENT_SIZE = 4;
        this.parent = manager;
        this.addEventListeners();
    }
    CodeRenderer.prototype.addEventListeners = function () {
        this.parent.observer.on(events.keydown, this.handleKeyDownActions, this);
        this.parent.observer.on(events.input, this.handleCodeBlockInput, this);
        this.parent.observer.on(events.localeChanged, this.handleLocaleChange, this);
        this.parent.observer.on(events.destroy, this.destroy, this);
    };
    CodeRenderer.prototype.removeEventListeners = function () {
        this.parent.observer.off(events.keydown, this.handleKeyDownActions);
        this.parent.observer.off(events.input, this.handleCodeBlockInput);
        this.parent.observer.off(events.localeChanged, this.handleLocaleChange);
        this.parent.observer.off(events.destroy, this.destroy);
    };
    /**
     * Renders a code block
     *
     * @param {BlockModel} block - The block model containing data.
     * @returns {HTMLElement} - The rendered code block element.
     * @hidden
     */
    CodeRenderer.prototype.renderCodeBlock = function (block) {
        var codeBlockSettings = block.properties;
        var _a = this.createCodeContainer(block), container = _a.container, preElement = _a.preElement, codeElement = _a.codeElement;
        var _b = this.createCodeToolbar(), toolbar = _b.toolbar, copyButton = _b.copyButton;
        var languageSelector = createElement('input', {
            className: 'e-code-block-languages'
        });
        toolbar.appendChild(languageSelector);
        this.initializeLanguageSelector(codeBlockSettings, preElement, codeElement, languageSelector);
        toolbar.appendChild(copyButton);
        container.appendChild(toolbar);
        container.appendChild(preElement);
        setCursorPosition(codeElement.lastChild, 0);
        return container;
    };
    CodeRenderer.prototype.createCodeToolbar = function () {
        var toolbar = createElement('div', {
            className: 'e-code-block-toolbar',
            attrs: { contenteditable: 'false' }
        });
        var copyButton = createElement('button', {
            className: 'e-code-block-copy-button e-btn',
            innerHTML: '<span class="e-icons e-copy"></span>',
            attrs: {
                title: this.parent.localeJson['codeCopyTooltip'],
                type: 'button',
                contenteditable: 'false'
            }
        });
        copyButton.addEventListener('click', function () {
            var codeElement = toolbar.closest('.e-code-block-container').querySelector('code');
            if (codeElement) {
                var codeText = codeElement.textContent;
                window.navigator.clipboard.writeText(codeText)
                    .then(function () {
                    var iconElement = copyButton.querySelector('.e-icons');
                    if (iconElement) {
                        var originalClass_1 = iconElement.className;
                        iconElement.className = 'e-icons e-check-tick';
                        setTimeout(function () {
                            iconElement.className = originalClass_1;
                        }, 2000);
                    }
                })
                    .catch(function (err) {
                    console.error('Could not copy text: ', err);
                });
            }
        });
        return { toolbar: toolbar, copyButton: copyButton };
    };
    CodeRenderer.prototype.initializeLanguageSelector = function (codeBlockSettings, preElement, codeElement, targetElement) {
        var languageDataSource = getLanguageItems();
        if (this.parent.codeBlockSettings.languages.length === 0) {
            this.parent.codeBlockSettings.languages = languageDataSource;
        }
        this.parent.observer.notify('renderDropdownList', {
            targetElement: targetElement,
            dataSource: this.parent.codeBlockSettings.languages,
            fields: { text: 'label', value: 'language' },
            value: this.parent.codeBlockSettings.defaultLanguage,
            change: function (e) {
                var newLanguage = e.value;
                if (codeElement && preElement) {
                    codeElement.className = "e-code-content e-block-content language-" + newLanguage;
                    preElement.setAttribute('data-language', newLanguage);
                    // Update in Model
                    codeBlockSettings.language = newLanguage;
                }
            }
        });
    };
    CodeRenderer.prototype.createCodeContainer = function (block) {
        var container = createElement('div', { className: 'e-code-block-container' });
        var preElement = createElement('pre', {
            className: 'e-code-block',
            attrs: {
                'data-language': this.parent.codeBlockSettings.defaultLanguage
            }
        });
        var hasContent = block.content && block.content.length > 0;
        var codeElement = createElement('code', {
            id: hasContent ? block.content[0].id : '',
            className: "e-code-content e-block-content language-" + this.parent.codeBlockSettings.defaultLanguage,
            attrs: { contenteditable: 'true' }
        });
        if (hasContent && block.content[0].content) {
            codeElement.textContent = block.content[0].content;
        }
        else {
            codeElement.innerHTML = '<br>';
        }
        preElement.appendChild(codeElement);
        return { container: container, preElement: preElement, codeElement: codeElement };
    };
    CodeRenderer.prototype.handleKeyDownActions = function (e) {
        if (!this.parent.currentFocusedBlock) {
            return;
        }
        var codeElement = this.parent.currentFocusedBlock.querySelector('code');
        var block = getBlockModelById(this.parent.currentFocusedBlock.id, this.parent.getEditorBlocks());
        if (!codeElement || (block && block.blockType !== BlockType.Code)) {
            return;
        }
        var normalizedKey = getNormalizedKey(e);
        if (normalizedKey === 'ctrl+a') {
            this.handleCtrlASelection(e, codeElement);
            return;
        }
        switch (e.key) {
            case 'Enter':
                this.handleEnterKey(e, codeElement, block);
                break;
            case 'Backspace':
                this.handleDeletion(e, codeElement, block, false);
                break;
            case 'Delete':
                this.handleDeletion(e, codeElement, block, true);
                break;
            case 'Tab':
                this.handleTabKey(e, codeElement);
                break;
        }
    };
    CodeRenderer.prototype.handleEnterKey = function (e, codeElement, block) {
        e.preventDefault();
        var action = this.determineEnterAction(codeElement);
        if (action.shouldExit) {
            this.exitCodeBlock(codeElement, block);
            return;
        }
        var afterTwoBr = this.isAfterTwoBrTags();
        this.insertTextAtCursor(!afterTwoBr, action.indentation);
    };
    CodeRenderer.prototype.determineEnterAction = function (codeElement) {
        var cursorPosition = this.getCursorPosition(codeElement);
        var indentation = this.getCurrentLineIndentation(codeElement, cursorPosition);
        var shouldExit = this.shouldExitCodeBlock(codeElement);
        return { shouldExit: shouldExit, indentation: indentation };
    };
    CodeRenderer.prototype.handleDeletion = function (e, codeElement, block, isDeleteKey) {
        var cursorPosition = this.getCursorPosition(codeElement);
        var textContent = codeElement.textContent;
        var range = getSelectedRange();
        var shouldPreventDefault = isDeleteKey
            ? (cursorPosition >= textContent.length)
            : (cursorPosition === 0 || textContent.length === 0);
        if ((textContent.length === 1) || (range.toString() === textContent)) {
            e.preventDefault();
            codeElement.innerHTML = '<br>';
            setCursorPosition(codeElement.firstChild, 0);
            this.updateBlockModel(codeElement, block);
        }
        if (shouldPreventDefault) {
            e.preventDefault();
            return;
        }
    };
    CodeRenderer.prototype.handleTabKey = function (e, codeElement) {
        e.preventDefault();
        if (e.shiftKey) {
            this.removeIndentation(codeElement);
        }
        else {
            this.addIndentation();
        }
    };
    CodeRenderer.prototype.handleCodeBlockInput = function (e) {
        if (!this.parent.currentFocusedBlock) {
            return;
        }
        var codeElement = this.parent.currentFocusedBlock.querySelector('code');
        var block = getBlockModelById(this.parent.currentFocusedBlock.id, this.parent.getEditorBlocks());
        if (!codeElement || block.blockType !== BlockType.Code) {
            return;
        }
        var textContent = codeElement.textContent || '';
        if (!textContent.trim() && codeElement.innerHTML !== '<br>') {
            codeElement.innerHTML = '<br>';
        }
        this.updateBlockModel(codeElement, block);
    };
    CodeRenderer.prototype.handleCtrlASelection = function (e, codeElement) {
        var _this = this;
        if (!this.ctrlAPressed) {
            e.preventDefault();
            this.selectEntireCodeBlock(codeElement);
            this.ctrlAPressed = true;
            setTimeout(function () {
                _this.ctrlAPressed = false;
            }, 500);
        }
        else {
            this.ctrlAPressed = false;
        }
    };
    CodeRenderer.prototype.getCursorPosition = function (element) {
        var range = this.parent.nodeSelection.getRange();
        var preCaretRange = range.cloneRange();
        preCaretRange.selectNodeContents(element);
        preCaretRange.setEnd(range.endContainer, range.endOffset);
        return preCaretRange.toString().length;
    };
    CodeRenderer.prototype.insertTextAtCursor = function (isDoubleBr, indentValue) {
        var selection = window.getSelection();
        if (!selection.rangeCount) {
            return;
        }
        var range = selection.getRangeAt(0);
        var brElement = createElement('br');
        range.insertNode(brElement);
        if (isDoubleBr) {
            var consecutiveBr = createElement('br');
            range.insertNode(consecutiveBr);
            range.setStartAfter(consecutiveBr);
            range.setEndAfter(consecutiveBr);
        }
        else {
            range.setStartAfter(brElement);
            range.setEndAfter(brElement);
        }
        if (indentValue) {
            range.insertNode(document.createTextNode(indentValue));
        }
        selection.removeAllRanges();
        selection.addRange(range);
    };
    CodeRenderer.prototype.getCurrentLineIndentation = function (element, cursorPosition) {
        var textContent = element.textContent;
        var lines = textContent.substring(0, cursorPosition).split('\n');
        var currentLine = lines[lines.length - 1];
        var indentMatch = currentLine.match(/^(\s*)/);
        return indentMatch ? indentMatch[1] : '';
    };
    CodeRenderer.prototype.isAfterTwoBrTags = function () {
        var selection = window.getSelection();
        if (!selection.rangeCount) {
            return false;
        }
        var range = selection.getRangeAt(0);
        var container = range.startContainer;
        var node = container;
        var brCount = 0;
        while (node && brCount < 2) {
            if (node.nodeType === Node.ELEMENT_NODE && node.tagName === 'BR') {
                brCount++;
            }
            else if (node.nodeType === Node.TEXT_NODE && node.textContent.trim() !== '') {
                break;
            }
            node = node.previousSibling;
        }
        return brCount >= 2;
    };
    CodeRenderer.prototype.shouldExitCodeBlock = function (element) {
        var children = element.childNodes;
        var brCount = 0;
        for (var i = children.length - 1; i >= 0; i--) {
            var node = children[i];
            if (node.nodeType === Node.ELEMENT_NODE && node.tagName === 'BR') {
                brCount++;
            }
            else if (node.nodeType === Node.TEXT_NODE && node.textContent.trim() !== '') {
                break;
            }
        }
        return brCount >= 3;
    };
    CodeRenderer.prototype.exitCodeBlock = function (codeElement, block) {
        var _this = this;
        var blockElement = getParentBlock(codeElement);
        setTimeout(function () {
            var nextSibling = blockElement.nextElementSibling;
            if (nextSibling) {
                _this.parent.setFocusAndUIForNewBlock(nextSibling);
            }
            else {
                var newBlock = BlockFactory.createParagraphBlock();
                _this.parent.blockCommand.addBlock({ block: newBlock, targetBlock: blockElement });
            }
        });
    };
    CodeRenderer.prototype.addIndentation = function () {
        var indent = ' '.repeat(this.INDENT_SIZE);
        var range = this.parent.nodeSelection.getRange();
        range.insertNode(document.createTextNode(indent));
        var endContainer = range.endContainer;
        setCursorPosition(endContainer, endContainer.textContent.length);
    };
    CodeRenderer.prototype.removeIndentation = function (element) {
        var cursorPosition = this.getCursorPosition(element);
        var textContent = element.textContent;
        var beforeCursor = textContent.substring(0, cursorPosition);
        var lastNewlineIndex = beforeCursor.lastIndexOf('\n');
        var lineStart = lastNewlineIndex + 1;
        var currentLine = textContent.substring(lineStart, cursorPosition);
        var spacesToRemove = Math.min(this.INDENT_SIZE, currentLine.match(/^ */)[0].length);
        if (spacesToRemove > 0) {
            element.textContent = textContent.substring(0, lineStart) + textContent.substring(lineStart + spacesToRemove);
            setCursorPosition(element, cursorPosition - spacesToRemove);
        }
    };
    CodeRenderer.prototype.selectEntireCodeBlock = function (element) {
        var range = document.createRange();
        range.selectNodeContents(element);
        var selection = window.getSelection();
        selection.removeAllRanges();
        selection.addRange(range);
    };
    CodeRenderer.prototype.updateBlockModel = function (codeElement, block) {
        var textContent = Array.from(codeElement.childNodes)
            .map(function (node) {
            if (node.nodeName === 'BR') {
                return '\n';
            }
            if (node.nodeType === Node.TEXT_NODE) {
                return node.textContent;
            }
            return '';
        })
            .join('')
            .replace(/\n$/, '');
        if (!block.content || block.content.length === 0) {
            this.parent.blockService.replaceBlock(block.id, __assign({}, decoupleReference(sanitizeBlock(block)), { content: [BlockFactory.createTextContent({ content: textContent })] }));
        }
        else {
            block.content[0].content = textContent;
        }
        this.parent.stateManager.updateManagerBlocks();
    };
    CodeRenderer.prototype.handleLocaleChange = function () {
        var codeBlocks = this.parent.rootEditorElement.querySelectorAll('.e-code-block-container');
        for (var _i = 0, _a = Array.from(codeBlocks); _i < _a.length; _i++) {
            var codeBlock = _a[_i];
            var copyButton = codeBlock.querySelector('.e-code-block-copy-button');
            if (copyButton) {
                copyButton.setAttribute('title', this.parent.localeJson['codeCopyTooltip']);
            }
        }
    };
    CodeRenderer.prototype.destroy = function () {
        this.removeEventListeners();
    };
    return CodeRenderer;
}());
export { CodeRenderer };
