import { detach, updateCSSText } from '@syncfusion/ej2-base';
import { BlockType } from '../../models/enums';
import { getBlockContentElement, getBlockModelById, getNormalizedKey, getParentBlock, getSelectedRange, normalizeRange } from '../../common/utils/index';
import { events } from '../../common/constant';
import { findClosestParent } from '../../common/utils/dom';
import * as constants from '../../common/constant';
/**
 * InlineToolbarModule class is used to render the inline toolbar for the block editor.
 *
 * @hidden
 */
var InlineToolbarModule = /** @class */ (function () {
    function InlineToolbarModule(manager) {
        this.parent = manager;
        this.addEventListeners();
    }
    InlineToolbarModule.prototype.addEventListeners = function () {
        this.parent.observer.on(events.inlineToolbarItemClick, this.handleInlineToolbarItemClick, this);
        this.parent.observer.on('toolbarCreated', this.handleToolbarCreated, this);
        this.parent.observer.on('handleColorpickerChange', this.handleColorChange, this);
        this.parent.observer.on('popupWidthChanged', this.handlePopupWidthChanges, this);
        this.parent.observer.on(events.formattingPerformed, this.toggleToolbarActiveState, this);
        this.parent.observer.on(events.keydown, this.onKeydown, this);
        this.parent.observer.on(events.destroy, this.destroy, this);
    };
    InlineToolbarModule.prototype.removeEventListeners = function () {
        this.parent.observer.off(events.inlineToolbarItemClick, this.handleInlineToolbarItemClick);
        this.parent.observer.off('toolbarCreated', this.handleToolbarCreated);
        this.parent.observer.off('handleColorpickerChange', this.handleColorChange);
        this.parent.observer.off('popupWidthChanged', this.handlePopupWidthChanges);
        this.parent.observer.off(events.formattingPerformed, this.toggleToolbarActiveState);
        this.parent.observer.off(events.keydown, this.onKeydown);
        this.parent.observer.off(events.destroy, this.destroy);
    };
    InlineToolbarModule.prototype.handleToolbarCreated = function () {
        this.toolbarEle = this.parent.rootEditorElement.querySelector('#' + this.parent.rootEditorElement.id + constants.BLOCKEDITOR_INLINETBAR_ID);
        this.init();
    };
    InlineToolbarModule.prototype.init = function () {
        this.popupElement = this.parent.rootEditorElement.querySelector('#' + this.parent.rootEditorElement.id + constants.INLINE_TBAR_POPUP_ID);
        this.popupObj = this.parent.popupRenderer.renderPopup({
            element: this.popupElement,
            content: this.toolbarEle
        });
    };
    InlineToolbarModule.prototype.onKeydown = function (e) {
        var normalizedKey = getNormalizedKey(e);
        if (!normalizedKey) {
            return;
        }
        var command = this.parent.keyCommandMap.get(normalizedKey);
        var allowedCommands = ['bold', 'italic', 'underline', 'strikethrough'];
        var tableBlk = this.parent.currentFocusedBlock &&
            this.parent.currentFocusedBlock.closest("." + constants.TABLE_BLOCK_CLS);
        if (allowedCommands.indexOf(command) !== -1) {
            e.preventDefault();
            if (window.getSelection().isCollapsed && !(tableBlk && this.parent.tableSelectionManager.hasActiveTableSelection(tableBlk))) {
                this.parent.formattingAction.toggleActiveFormats(command);
            }
            else {
                this.parent.execCommand({ command: 'FormattingAction', state: { command: command } });
            }
        }
    };
    InlineToolbarModule.prototype.isSelectionInTableHeader = function (range) {
        // Resolve a reliable HTMLElement anchor from the range
        var startEl = (range.startContainer.nodeType === Node.ELEMENT_NODE
            ? range.startContainer
            : range.startContainer.parentElement);
        if (!startEl) {
            return false;
        }
        // Nearest header cell: either an actual <th> or an element marked as column header
        var headerCell = startEl.closest('th,[role="columnheader"]');
        if (!headerCell) {
            return false;
        }
        // Ensure it's within a THEAD to qualify as header, not body cells styled as header
        var inThead = headerCell.closest('thead');
        return !!inThead;
    };
    /**
     * Shows the inline toolbar at the current selection
     *
     * @param {Range} range - Selection range where the toolbar should appear
     * @param {Event} event - Optional event that triggered the toolbar
     * @returns {void}
     * @hidden
     */
    InlineToolbarModule.prototype.showInlineToolbar = function (range, event) {
        var notAllowedTypes = [BlockType.Code];
        var blockElement = findClosestParent(range.startContainer, '.' + constants.BLOCK_CLS);
        var blockType = blockElement.getAttribute('data-block-type');
        if (!this.parent.inlineToolbarSettings.enable || (notAllowedTypes.indexOf(blockType) !== -1)) {
            return;
        }
        if (this.isSelectionInTableHeader(range)) {
            return;
        }
        this.toggleToolbarActiveState();
        this.parent.popupRenderer.adjustPopupPositionRelativeToTarget(range, this.popupObj);
        this.popupObj.show();
    };
    /**
     * Hides the inline toolbar
     *
     * @param {Event} e - Optional event that triggered hiding the toolbar
     * @returns {void}
     */
    InlineToolbarModule.prototype.hideInlineToolbar = function (e) {
        if (this.popupElement && !this.popupElement.classList.contains('e-popup-open')) {
            return;
        }
        this.popupObj.hide();
    };
    InlineToolbarModule.prototype.handleInlineToolbarItemClick = function (args) {
        var selectedItem = args.item.id;
        if (selectedItem === 'color' || selectedItem === 'bgColor') {
            // Do not proceed further for color and background color commands. It will be handled by the color picker.
            return;
        }
        this.parent.execCommand({ command: 'FormattingAction', state: { command: selectedItem } });
    };
    /**
     * Updates active state of toolbar buttons based on current selection formatting
     *
     * @returns {void}
     * @hidden
     */
    InlineToolbarModule.prototype.toggleToolbarActiveState = function () {
        var range = getSelectedRange();
        if (!range) {
            return;
        }
        var toolbarItems = this.popupElement.querySelectorAll('.e-toolbar-item');
        toolbarItems.forEach(function (item) { return item.classList.remove('e-active'); });
        var blockElements = this.getBlocksInRange(range);
        var selectedContentModels = this.getSelectedContentModels(range, blockElements);
        if (!selectedContentModels || !selectedContentModels.length) {
            return;
        }
        var commonStyles = this.getCommonStyles(selectedContentModels);
        for (var _i = 0, _a = Array.from(toolbarItems); _i < _a.length; _i++) {
            var item = _a[_i];
            var command = item.getAttribute('data-command');
            this.toggleActiveState(item, command, commonStyles);
        }
    };
    InlineToolbarModule.prototype.getSelectedContentModels = function (range, blockElements) {
        var _this = this;
        var selectedContentModels = [];
        blockElements.forEach(function (blockElement) {
            var block = getBlockModelById(blockElement.id, _this.parent.getEditorBlocks());
            var contentElement = getBlockContentElement(blockElement);
            var selectedNodes = _this.getNodesInRange(normalizeRange(range), contentElement);
            selectedNodes.forEach(function (node) {
                var model = block.content.find(function (content) { return content.id === node.id; });
                if (model) {
                    selectedContentModels.push(model);
                }
            });
        });
        return selectedContentModels;
    };
    InlineToolbarModule.prototype.getBlocksInRange = function (range) {
        var blocks = [];
        var startBlock = getParentBlock(range.startContainer);
        var endBlock = getParentBlock(range.endContainer);
        if (!startBlock) {
            return blocks;
        }
        // Single block case
        if (startBlock === endBlock) {
            blocks.push(startBlock);
            return blocks;
        }
        // Multi-block case
        var current = startBlock;
        while (current && current !== endBlock) {
            blocks.push(current);
            current = current.nextElementSibling;
        }
        if (endBlock) {
            blocks.push(endBlock);
        }
        return blocks.filter(function (block) { return block.hasAttribute('data-block-type'); });
    };
    InlineToolbarModule.prototype.getNodesInRange = function (range, container) {
        var selectedNodes = [];
        var walker = document.createTreeWalker(container, NodeFilter.SHOW_ELEMENT, {
            acceptNode: function (node) {
                if (node.nodeType === Node.ELEMENT_NODE) {
                    var nodeStart = range.comparePoint(node, 0);
                    var nodeEnd = range.comparePoint(node, node.childNodes.length);
                    if (nodeStart <= 0 && nodeEnd >= 0) {
                        return NodeFilter.FILTER_ACCEPT;
                    }
                }
                return NodeFilter.FILTER_REJECT;
            }
        });
        while (walker.nextNode()) {
            selectedNodes.push(walker.currentNode);
        }
        return selectedNodes;
    };
    InlineToolbarModule.prototype.toggleActiveState = function (item, command, styles) {
        var isActive = false;
        switch (command) {
            case 'Bold':
                isActive = styles.bold === true;
                break;
            case 'Italic':
                isActive = styles.italic === true;
                break;
            case 'Underline':
                isActive = styles.underline === true;
                break;
            case 'Strikethrough':
                isActive = styles.strikethrough === true;
                break;
            case 'Superscript':
                isActive = styles.superscript === true;
                break;
            case 'Subscript':
                isActive = styles.subscript === true;
                break;
            case 'Uppercase':
                isActive = styles.uppercase === true;
                break;
            case 'Lowercase':
                isActive = styles.lowercase === true;
                break;
            case 'Color':
                this.setColors('color', styles.color);
                break;
            case 'BackgroundColor':
                this.setColors('bgColor', styles.backgroundColor);
                break;
        }
        item.classList.toggle('e-active', isActive);
    };
    InlineToolbarModule.prototype.getCommonStyles = function (contentModels) {
        if (!contentModels.length || !contentModels[0].properties ||
            !contentModels[0].properties.styles) {
            return {};
        }
        var firstStyles = contentModels[0].properties.styles;
        var styleKeys = Object.keys(firstStyles);
        var result = {};
        var _loop_1 = function (key) {
            var firstValue = firstStyles[key];
            var isCommon = contentModels.every(function (model, i) {
                if (i === 0) {
                    return true;
                }
                var styles = model.properties.styles || {};
                return styles[key] === firstValue;
            });
            if (isCommon) {
                result[key] = firstValue;
            }
        };
        for (var _i = 0, styleKeys_1 = styleKeys; _i < styleKeys_1.length; _i++) {
            var key = styleKeys_1[_i];
            _loop_1(key);
        }
        return result;
    };
    InlineToolbarModule.prototype.setColors = function (type, value) {
        var colorBtn = this.toolbarEle.querySelector('#toolbar-color-dropdown');
        var bgColorBtn = this.toolbarEle.querySelector('#toolbar-bgcolor-dropdown');
        var currentColor = value ? value : '#000000';
        if (currentColor) {
            var button = type === 'color' ?
                colorBtn.querySelector('.e-inline-color-icon') :
                bgColorBtn.querySelector('.e-inline-bgColor-icon');
            if (button) {
                updateCSSText(button, "border-bottom-color: " + currentColor + ";");
            }
        }
    };
    InlineToolbarModule.prototype.handleColorChange = function (args) {
        var command = args.type === 'bgColor' ? 'backgroundColor' : args.type;
        this.parent.formattingAction.execCommand({
            command: command,
            value: args.value
        });
        this.setColors(args.type, args.value);
    };
    InlineToolbarModule.prototype.handlePopupWidthChanges = function (data) {
        this.popupObj.width = this.parent.inlineToolbarSettings.popupWidth = data.value;
    };
    /**
     * Checks whether the slash command popup is opened or not.
     *
     * @returns {boolean} - Returns true if the slash command popup is opened, otherwise false.
     * @hidden
     */
    InlineToolbarModule.prototype.isPopupOpen = function () {
        var inlineTlbrPopupElement = document.querySelector('.e-popup.e-blockeditor-inline-toolbar-popup');
        return (inlineTlbrPopupElement.classList.contains('e-popup-open'));
    };
    /**
     * Destroys the inline toolbar module and cleans up resources
     *
     * @returns {void}
     */
    InlineToolbarModule.prototype.destroy = function () {
        this.removeEventListeners();
        if (this.popupObj) {
            this.popupObj.destroy();
            detach(this.popupElement);
            this.popupObj = null;
        }
    };
    return InlineToolbarModule;
}());
export { InlineToolbarModule };
