import { vglobal } from "@visactor/vtable/es/vrender";

import { MENU_CONTAINER_CLASS, MENU_ITEM_CLASS, MENU_ITEM_SEPARATOR_CLASS, MENU_ITEM_SUBMENU_CLASS, MENU_STYLES, createElement, applyStyles, createIcon, MENU_ITEM_DISABLED_CLASS } from "./styles";

export class MenuManager {
    constructor() {
        this.menuContainer = null, this.activeSubmenus = [], this.clickCallback = null, 
        this.table = null, this.context = {}, this.hideTimeout = null, this.showTimeout = null, 
        this.submenuShowDelay = 100, this.submenuHideDelay = 500, this.handleDocumentClick = event => {
            if (!this.menuContainer || event.target !== this.menuContainer && !this.menuContainer.contains(event.target)) {
                for (const submenu of this.activeSubmenus) if (event.target === submenu || submenu.contains(event.target)) return;
                this.release();
            }
        };
    }
    showMenu(menuItems, x, y, context, table) {
        this.context = context, this.table = table, this.release(), this.menuContainer = createElement("div", MENU_CONTAINER_CLASS), 
        applyStyles(this.menuContainer, MENU_STYLES.menuContainer), document.body.appendChild(this.menuContainer), 
        this.createMenuItems(menuItems, this.menuContainer), this.positionMenu(this.menuContainer, x, y), 
        setTimeout((() => {
            vglobal.addEventListener("click", this.handleDocumentClick);
        }), 0);
    }
    setClickCallback(callback) {
        this.clickCallback = callback;
    }
    createMenuItems(items, container, parentItem) {
        items.forEach((item => {
            if ("string" == typeof item && "---" === item) {
                const separator = createElement("div", MENU_ITEM_SEPARATOR_CLASS);
                applyStyles(separator, MENU_STYLES.menuItemSeparator), container.appendChild(separator);
            } else if ("object" == typeof item) {
                const menuItem = item, menuItemElement = createElement("div", MENU_ITEM_CLASS);
                applyStyles(menuItemElement, MENU_STYLES.menuItem);
                const leftContainer = createElement("div");
                if (leftContainer.style.display = "flex", leftContainer.style.alignItems = "center", 
                menuItem.iconName) {
                    const icon = createIcon(menuItem.iconName);
                    leftContainer.appendChild(icon);
                } else if (menuItem.iconPlaceholder) {
                    const placeholder = createElement("span");
                    applyStyles(placeholder, MENU_STYLES.menuItemIcon), leftContainer.appendChild(placeholder);
                }
                const text = createElement("span");
                if (text.textContent = menuItem.text, applyStyles(text, MENU_STYLES.menuItemText), 
                leftContainer.appendChild(text), item.inputDefaultValue) {
                    const input = createElement("input");
                    input.type = "number", input.min = "1", input.value = item.inputDefaultValue.toString(), 
                    applyStyles(input, MENU_STYLES.inputField), leftContainer.appendChild(input), input.addEventListener("keydown", (e => {
                        "Enter" === e.key && this.handleMenuItemClick(Object.assign({
                            menuKey: menuItem.menuKey,
                            menuText: menuItem.text,
                            inputValue: parseInt(input.value, 10)
                        }, this.context));
                    }));
                }
                menuItemElement.appendChild(leftContainer);
                const rightContainer = createElement("div");
                if (rightContainer.style.display = "flex", rightContainer.style.alignItems = "center", 
                menuItem.shortcut) {
                    const shortcut = createElement("span");
                    shortcut.textContent = menuItem.shortcut, applyStyles(shortcut, MENU_STYLES.menuItemShortcut), 
                    rightContainer.appendChild(shortcut);
                }
                if (menuItem.children && menuItem.children.length > 0) {
                    menuItemElement.classList.add(MENU_ITEM_SUBMENU_CLASS);
                    const arrow = createElement("span");
                    arrow.textContent = "▶", applyStyles(arrow, MENU_STYLES.submenuArrow), rightContainer.appendChild(arrow);
                }
                menuItemElement.appendChild(rightContainer), menuItem.disabled && (menuItemElement.classList.add(MENU_ITEM_DISABLED_CLASS), 
                applyStyles(menuItemElement, MENU_STYLES.menuItemDisabled)), menuItem.disabled || (menuItem.children && 0 !== menuItem.children.length || menuItemElement.addEventListener("click", (e => {
                    e.target instanceof HTMLInputElement || this.handleMenuItemClick(Object.assign({
                        menuKey: menuItem.menuKey,
                        menuText: menuItem.text
                    }, this.context));
                })), menuItemElement.addEventListener("mouseenter", (() => {
                    applyStyles(menuItemElement, MENU_STYLES.menuItemHover), null !== this.hideTimeout && (clearTimeout(this.hideTimeout), 
                    this.hideTimeout = null), null !== this.showTimeout && (clearTimeout(this.showTimeout), 
                    this.showTimeout = null), menuItem.children && menuItem.children.length > 0 ? (this.closeAllSubmenus(), 
                    this.showTimeout = setTimeout((() => {
                        document.body.contains(menuItemElement) && this.showSubmenu(menuItem.children, menuItemElement, menuItem);
                    }), this.submenuShowDelay)) : parentItem || this.closeAllSubmenus();
                })), menuItemElement.addEventListener("mouseleave", (() => {
                    Object.keys(MENU_STYLES.menuItemHover).forEach((key => {
                        menuItemElement.style[key] = "";
                    })), menuItem.children && menuItem.children.length > 0 && (this.hideTimeout = setTimeout((() => {
                        this.closeAllSubmenus();
                    }), this.submenuHideDelay));
                }))), container.appendChild(menuItemElement);
            }
        }));
    }
    showSubmenu(items, parentElement, parentItem) {
        const parentRect = parentElement.getBoundingClientRect(), submenu = createElement("div", MENU_CONTAINER_CLASS);
        applyStyles(submenu, MENU_STYLES.submenuContainer), this.createMenuItems(items, submenu, parentItem), 
        document.body.appendChild(submenu);
        const submenuRect = submenu.getBoundingClientRect();
        let left = parentRect.right, top = parentRect.top;
        left + submenuRect.width > window.innerWidth && (left = parentRect.left - submenuRect.width), 
        top + submenuRect.height > window.innerHeight && (top = window.innerHeight - submenuRect.height), 
        submenu.style.left = `${left}px`, submenu.style.top = `${top}px`, submenu.addEventListener("mouseenter", (() => {
            null !== this.hideTimeout && (clearTimeout(this.hideTimeout), this.hideTimeout = null);
        })), submenu.addEventListener("mouseleave", (() => {
            this.hideTimeout = setTimeout((() => {
                this.closeAllSubmenus();
            }), this.submenuHideDelay);
        })), this.activeSubmenus.push(submenu);
    }
    closeAllSubmenus() {
        this.activeSubmenus.forEach((submenu => {
            submenu && submenu.parentNode && submenu.parentNode.removeChild(submenu);
        })), this.activeSubmenus = [];
    }
    positionMenu(menu, x, y) {
        const menuRect = menu.getBoundingClientRect();
        let left = x, top = y;
        left + menuRect.width > window.innerWidth && (left = window.innerWidth - menuRect.width), 
        top + menuRect.height > window.innerHeight && (top = window.innerHeight - menuRect.height), 
        menu.style.left = `${left}px`, menu.style.top = `${top}px`;
    }
    handleMenuItemClick(args) {
        this.release(), this.clickCallback && this.table && this.clickCallback(args, this.table);
    }
    release() {
        this.closeAllSubmenus(), this.menuContainer && this.menuContainer.parentNode && (this.menuContainer.parentNode.removeChild(this.menuContainer), 
        this.menuContainer = null), null !== this.hideTimeout && (clearTimeout(this.hideTimeout), 
        this.hideTimeout = null), vglobal.removeEventListener("click", this.handleDocumentClick);
    }
}