import { defineVxeComponent } from '../../ui/src/comp';
import XEUtils from 'xe-utils';
import { getConfig, getI18n, getIcon, createEvent, globalMixins, globalEvents, renderEmptyElement, GLOBAL_EVENT_KEYS } from '../../ui';
import { getLastZIndex, nextSubZIndex, nextZIndex, getFuncText } from '../../ui/src/utils';
import { getDomNode, getEventTargetNode, toCssUnit } from '../../ui/src/dom';
import { getSlotVNs } from '../../ui/src/vn';
function createInternalData() {
    return {
    // leaveTime: null
    };
}
function createReactData() {
    return {
        visible: false,
        activeOption: null,
        activeChildOption: null,
        popupStyle: {
            top: '',
            left: '',
            zIndex: 0
        },
        childOffsetX: 0
    };
}
function hasChildMenu(item) {
    const { children } = item;
    return children && children.some((child) => child.visible !== false);
}
function hasValidItem(item) {
    return !item.loading && !item.disabled && item.visible !== false;
}
function findNextFirstItem(allFirstList, firstItem) {
    for (let i = 0; i < allFirstList.length; i++) {
        const item = allFirstList[i];
        if (firstItem === item) {
            const nextItem = allFirstList[i + 1];
            if (nextItem) {
                return nextItem;
            }
        }
    }
    return XEUtils.first(allFirstList);
}
function findPrevFirstItem(allFirstList, firstItem) {
    for (let i = 0; i < allFirstList.length; i++) {
        const item = allFirstList[i];
        if (firstItem === item) {
            if (i > 0) {
                return allFirstList[i - 1];
            }
        }
    }
    return XEUtils.last(allFirstList);
}
function findFirstChildItem(firstItem) {
    const { children } = firstItem;
    if (children) {
        for (let i = 0; i < children.length; i++) {
            const item = children[i];
            if (hasValidItem(item)) {
                return item;
            }
        }
    }
    return null;
}
function findPrevChildItem(firstItem, childItem) {
    const { children } = firstItem;
    let prevValidItem = null;
    if (children) {
        for (let i = 0; i < children.length; i++) {
            const item = children[i];
            if (childItem === item) {
                break;
            }
            if (hasValidItem(item)) {
                prevValidItem = item;
            }
        }
        if (!prevValidItem) {
            for (let len = children.length - 1; len >= 0; len--) {
                const item = children[len];
                if (hasValidItem(item)) {
                    return item;
                }
            }
        }
    }
    return prevValidItem;
}
function findNextChildItem(firstItem, childItem) {
    const { children } = firstItem;
    let firstValidItem = null;
    if (children) {
        let isMetch = false;
        for (let i = 0; i < children.length; i++) {
            const item = children[i];
            if (!firstValidItem) {
                if (hasValidItem(item)) {
                    firstValidItem = item;
                }
            }
            if (isMetch) {
                if (hasValidItem(item)) {
                    return item;
                }
            }
            else {
                isMetch = childItem === item;
            }
        }
    }
    return firstValidItem;
}
function openMenu($xeContextMenu) {
    const props = $xeContextMenu;
    const reactData = $xeContextMenu.reactData;
    const { value: modelValue } = props;
    const { visible } = reactData;
    const value = true;
    reactData.visible = value;
    $xeContextMenu.handleLocate();
    $xeContextMenu.updateZindex();
    if (modelValue !== value) {
        $xeContextMenu.emitModel(value);
        $xeContextMenu.dispatchEvent('change', { value }, null);
    }
    if (visible !== value) {
        $xeContextMenu.dispatchEvent('show', { visible: value }, null);
    }
    return $xeContextMenu.$nextTick().then(() => {
        $xeContextMenu.updateLocate();
    });
}
function closeMenu($xeContextMenu) {
    const props = $xeContextMenu;
    const reactData = $xeContextMenu.reactData;
    const { value: modelValue } = props;
    const { visible } = reactData;
    const value = false;
    reactData.visible = value;
    if (modelValue !== value) {
        $xeContextMenu.emitModel(value);
        $xeContextMenu.dispatchEvent('change', { value }, null);
    }
    if (visible !== value) {
        $xeContextMenu.dispatchEvent('hide', { visible: value }, null);
    }
    return $xeContextMenu.$nextTick();
}
export default {
    name: 'VxeContextMenu',
    mixins: [
        globalMixins.sizeMixin
    ],
    props: {
        value: Boolean,
        className: String,
        size: {
            type: String,
            default: () => getConfig().contextMenu.size || getConfig().size
        },
        options: Array,
        x: [Number, String],
        y: [Number, String],
        autoLocate: {
            type: Boolean,
            default: () => getConfig().contextMenu.autoLocate
        },
        zIndex: [Number, String],
        position: {
            type: String,
            default: () => getConfig().contextMenu.position
        },
        destroyOnClose: {
            type: Boolean,
            default: () => getConfig().contextMenu.destroyOnClose
        },
        transfer: {
            type: Boolean,
            default: () => getConfig().contextMenu.transfer
        }
    },
    data() {
        const reactData = createReactData();
        return Object.assign(Object.assign({}, {}), { xID: XEUtils.uniqueId(), reactData });
    },
    computed: Object.assign(Object.assign({}, {}), { computeMenuGroups() {
            const $xeContextMenu = this;
            const props = $xeContextMenu;
            const { options } = props;
            return options || [];
        },
        computeAllFirstMenuList() {
            const $xeContextMenu = this;
            const menuGroups = $xeContextMenu.computeMenuGroups;
            const firstList = [];
            for (let i = 0; i < menuGroups.length; i++) {
                const list = menuGroups[i];
                for (let j = 0; j < list.length; j++) {
                    const firstItem = list[j];
                    if (hasValidItem(firstItem)) {
                        firstList.push(firstItem);
                    }
                }
            }
            return firstList;
        },
        computeTopAndLeft() {
            const $xeContextMenu = this;
            const props = $xeContextMenu;
            const { x, y } = props;
            return `${x}${y}`;
        } }),
    methods: {
        //
        // Method
        //
        dispatchEvent(type, params, evnt) {
            const $xeContextMenu = this;
            $xeContextMenu.$emit(type, createEvent(evnt, { $contextMenu: $xeContextMenu }, params));
        },
        emitModel(value) {
            const $xeContextMenu = this;
            const { _events } = $xeContextMenu;
            $xeContextMenu.$emit('input', value);
            if (_events && _events.modelValue) {
                $xeContextMenu.$emit('modelValue', value);
            }
            else {
                $xeContextMenu.$emit('model-value', value);
            }
        },
        open() {
            const $xeContextMenu = this;
            return openMenu($xeContextMenu);
        },
        close() {
            const $xeContextMenu = this;
            return closeMenu($xeContextMenu);
        },
        handleLocate() {
            const $xeContextMenu = this;
            const props = $xeContextMenu;
            const reactData = $xeContextMenu.reactData;
            const { x, y } = props;
            const { popupStyle } = reactData;
            popupStyle.left = toCssUnit(x || 0);
            popupStyle.top = toCssUnit(y || 0);
            $xeContextMenu.updateLocate();
        },
        updateZindex() {
            const $xeContextMenu = this;
            const props = $xeContextMenu;
            const reactData = $xeContextMenu.reactData;
            const { zIndex, transfer } = props;
            const { popupStyle } = reactData;
            const menuZIndex = popupStyle.zIndex;
            if (zIndex) {
                popupStyle.zIndex = XEUtils.toNumber(zIndex);
            }
            else {
                if (menuZIndex < getLastZIndex()) {
                    popupStyle.zIndex = transfer ? nextSubZIndex() : nextZIndex();
                }
            }
        },
        updateLocate() {
            const $xeContextMenu = this;
            const props = $xeContextMenu;
            const reactData = $xeContextMenu.reactData;
            const { autoLocate, position } = props;
            const { popupStyle } = reactData;
            if (autoLocate) {
                const wrapperEl = $xeContextMenu.$refs.refElem;
                if (wrapperEl) {
                    const { visibleWidth, visibleHeight } = getDomNode();
                    const wrapperStyle = getComputedStyle(wrapperEl);
                    const offsetTop = XEUtils.toNumber(wrapperStyle.top);
                    const offsetLeft = XEUtils.toNumber(wrapperStyle.left);
                    const wrapperWidth = wrapperEl.offsetWidth;
                    const wrapperHeight = wrapperEl.offsetHeight;
                    if (position === 'absolute') {
                        //
                    }
                    else {
                        if (offsetTop + wrapperHeight > visibleHeight) {
                            popupStyle.top = `${Math.max(0, offsetTop - wrapperHeight)}px`;
                        }
                        if (offsetLeft + wrapperWidth > visibleWidth) {
                            popupStyle.left = `${Math.max(0, offsetLeft - wrapperWidth)}px`;
                        }
                    }
                }
            }
            $xeContextMenu.updateChildLocate();
        },
        updateChildLocate() {
            const $xeContextMenu = this;
            const reactData = $xeContextMenu.reactData;
            const wrapperEl = $xeContextMenu.$refs.refElem;
            if (wrapperEl) {
                const { visibleWidth } = getDomNode();
                const owSize = 4;
                const handleStyle = () => {
                    const wrapperStyle = getComputedStyle(wrapperEl);
                    const offsetLeft = XEUtils.toNumber(wrapperStyle.left);
                    const wrapperWidth = wrapperEl.offsetWidth;
                    const childEl = wrapperEl.querySelector('.vxe-context-menu--children-wrapper');
                    const childWidth = childEl ? childEl.offsetWidth : wrapperWidth;
                    if ((offsetLeft + wrapperWidth) > (visibleWidth - childWidth)) {
                        // 往左
                        reactData.childOffsetX = -childWidth + owSize;
                    }
                    else {
                        // 往右
                        reactData.childOffsetX = wrapperWidth - owSize;
                    }
                };
                handleStyle();
                $xeContextMenu.$nextTick(() => {
                    handleStyle();
                });
            }
        },
        handleVisible() {
            const $xeContextMenu = this;
            const props = $xeContextMenu;
            const { value: modelValue } = props;
            if (modelValue) {
                openMenu($xeContextMenu);
            }
            else {
                closeMenu($xeContextMenu);
            }
        },
        handleItemClickEvent(evnt, item) {
            const $xeContextMenu = this;
            evnt.preventDefault();
            evnt.stopPropagation();
            if (!item.disabled && !item.loading && !hasChildMenu(item)) {
                $xeContextMenu.dispatchEvent('option-click', { option: item }, evnt);
                closeMenu($xeContextMenu);
            }
        },
        handleItemMouseenterEvent(evnt, item, parentitem) {
            const $xeContextMenu = this;
            const reactData = $xeContextMenu.reactData;
            const internalData = $xeContextMenu.internalData;
            const { leaveTime } = internalData;
            if (leaveTime) {
                clearTimeout(leaveTime);
            }
            reactData.activeOption = parentitem || item;
            if (parentitem) {
                reactData.activeOption = parentitem;
                reactData.activeChildOption = item;
            }
            else {
                reactData.activeOption = item;
                if (hasChildMenu(item)) {
                    reactData.activeChildOption = findFirstChildItem(item);
                    $xeContextMenu.$nextTick(() => {
                        $xeContextMenu.updateChildLocate();
                    });
                }
                else {
                    reactData.activeChildOption = null;
                }
            }
        },
        handleItemMouseleaveEvent() {
            const $xeContextMenu = this;
            const reactData = $xeContextMenu.reactData;
            const internalData = $xeContextMenu.internalData;
            const { leaveTime } = internalData;
            if (leaveTime) {
                clearTimeout(leaveTime);
            }
            internalData.leaveTime = setTimeout(() => {
                internalData.leaveTime = null;
                reactData.activeOption = null;
                reactData.activeChildOption = null;
            }, 300);
        },
        handleGlobalMousewheelEvent(evnt) {
            const $xeContextMenu = this;
            const reactData = $xeContextMenu.reactData;
            const { visible } = reactData;
            if (visible) {
                const el = $xeContextMenu.$refs.refElem;
                if (!getEventTargetNode(evnt, el, '').flag) {
                    closeMenu($xeContextMenu);
                }
            }
        },
        handleGlobalKeydownEvent(evnt) {
            const $xeContextMenu = this;
            const reactData = $xeContextMenu.reactData;
            const { visible, activeOption, activeChildOption } = reactData;
            const allFirstMenuList = $xeContextMenu.computeAllFirstMenuList;
            if (visible) {
                const isLeftArrow = globalEvents.hasKey(evnt, GLOBAL_EVENT_KEYS.ARROW_LEFT);
                const isUpArrow = globalEvents.hasKey(evnt, GLOBAL_EVENT_KEYS.ARROW_UP);
                const isRightArrow = globalEvents.hasKey(evnt, GLOBAL_EVENT_KEYS.ARROW_RIGHT);
                const isDwArrow = globalEvents.hasKey(evnt, GLOBAL_EVENT_KEYS.ARROW_DOWN);
                const isEnter = globalEvents.hasKey(evnt, GLOBAL_EVENT_KEYS.ENTER);
                const isEsc = globalEvents.hasKey(evnt, GLOBAL_EVENT_KEYS.ESCAPE);
                if (isEsc) {
                    closeMenu($xeContextMenu);
                    return;
                }
                // 回车选中
                if (isEnter) {
                    if (activeOption || activeChildOption) {
                        evnt.preventDefault();
                        evnt.stopPropagation();
                        if (!activeChildOption && hasChildMenu(activeOption)) {
                            reactData.activeChildOption = findFirstChildItem(activeOption);
                            $xeContextMenu.updateChildLocate();
                            return;
                        }
                        $xeContextMenu.handleItemClickEvent(evnt, activeChildOption || activeOption);
                        return;
                    }
                }
                // 方向键操作
                if (activeChildOption) {
                    if (isUpArrow) {
                        evnt.preventDefault();
                        reactData.activeChildOption = findPrevChildItem(activeOption, activeChildOption);
                        $xeContextMenu.updateChildLocate();
                    }
                    else if (isDwArrow) {
                        evnt.preventDefault();
                        reactData.activeChildOption = findNextChildItem(activeOption, activeChildOption);
                        $xeContextMenu.updateChildLocate();
                    }
                    else if (isLeftArrow) {
                        evnt.preventDefault();
                        reactData.activeChildOption = null;
                    }
                }
                else if (activeOption) {
                    evnt.preventDefault();
                    if (isUpArrow) {
                        reactData.activeOption = findPrevFirstItem(allFirstMenuList, activeOption);
                    }
                    else if (isDwArrow) {
                        reactData.activeOption = findNextFirstItem(allFirstMenuList, activeOption);
                    }
                    else {
                        if (hasChildMenu(activeOption)) {
                            if (isRightArrow) {
                                reactData.activeChildOption = findFirstChildItem(activeOption);
                                $xeContextMenu.updateChildLocate();
                            }
                        }
                    }
                }
                else {
                    evnt.preventDefault();
                    reactData.activeOption = XEUtils.first(allFirstMenuList);
                }
            }
        },
        handleGlobalMousedownEvent(evnt) {
            const $xeContextMenu = this;
            const reactData = $xeContextMenu.reactData;
            const { visible } = reactData;
            if (visible) {
                const el = $xeContextMenu.$refs.refElem;
                if (!getEventTargetNode(evnt, el, '').flag) {
                    closeMenu($xeContextMenu);
                }
            }
        },
        handleGlobalBlurEvent() {
            const $xeContextMenu = this;
            const reactData = $xeContextMenu.reactData;
            const { visible } = reactData;
            if (visible) {
                closeMenu($xeContextMenu);
            }
        },
        //
        // Render
        //
        renderMenuItem(h, item, parentItem, hasChildMenus) {
            const $xeContextMenu = this;
            const { visible, disabled, loading } = item;
            if (visible === false) {
                return renderEmptyElement($xeContextMenu);
            }
            const prefixOpts = Object.assign({}, item.prefixConfig);
            const prefixIcon = prefixOpts.icon || item.prefixIcon;
            const suffixOpts = Object.assign({}, item.suffixConfig);
            const suffixIcon = suffixOpts.icon || item.suffixIcon;
            const menuContent = loading ? getI18n('vxe.contextMenu.loadingText') : getFuncText(item.name);
            return h('div', {
                class: ['vxe-context-menu--item-inner', {
                        'is--disabled': disabled,
                        'is--loading': loading
                    }],
                on: {
                    click(evnt) {
                        $xeContextMenu.handleItemClickEvent(evnt, item);
                    },
                    mouseenter(evnt) {
                        $xeContextMenu.handleItemMouseenterEvent(evnt, item, parentItem);
                    },
                    mouseleave: $xeContextMenu.handleItemMouseleaveEvent
                }
            }, [
                h('div', {
                    class: ['vxe-context-menu--item-prefix', prefixOpts.className || '']
                }, loading
                    ? [
                        h('span', {
                            key: '1'
                        }, [
                            h('i', {
                                class: getIcon().CONTEXT_MENU_OPTION_LOADING
                            })
                        ])
                    ]
                    : [
                        prefixIcon && XEUtils.isFunction(prefixIcon)
                            ? h('span', {
                                key: '2'
                            }, getSlotVNs(prefixIcon({})))
                            : h('span', {
                                key: '3'
                            }, [
                                h('i', {
                                    class: prefixIcon
                                })
                            ]),
                        prefixOpts.content
                            ? h('span', {
                                key: '4'
                            }, `${prefixOpts.content || ''}`)
                            : renderEmptyElement($xeContextMenu)
                    ]),
                h('div', {
                    class: 'vxe-context-menu--item-label'
                }, menuContent),
                !loading && (suffixIcon || suffixOpts.content)
                    ? h('div', {
                        class: ['vxe-context-menu--item-suffix', suffixOpts.className || '']
                    }, [
                        suffixIcon && XEUtils.isFunction(suffixIcon)
                            ? h('span', {
                                key: '2'
                            }, getSlotVNs(suffixIcon({})))
                            : h('span', {
                                key: '3'
                            }, [
                                h('i', {
                                    class: suffixIcon
                                })
                            ]),
                        suffixOpts.content
                            ? h('span', {
                                key: '4'
                            }, `${suffixOpts.content || ''}`)
                            : renderEmptyElement($xeContextMenu)
                    ])
                    : renderEmptyElement($xeContextMenu),
                hasChildMenus
                    ? h('div', {
                        class: 'vxe-context-menu--item-subicon'
                    }, [
                        h('i', {
                            class: getIcon().CONTEXT_MENU_CHILDREN
                        })
                    ])
                    : renderEmptyElement($xeContextMenu)
            ]);
        },
        renderMenus(h) {
            const $xeContextMenu = this;
            const reactData = $xeContextMenu.reactData;
            const { activeOption, activeChildOption, childOffsetX } = reactData;
            const menuGroups = $xeContextMenu.computeMenuGroups;
            const mgVNs = [];
            menuGroups.forEach((menuList, gIndex) => {
                const moVNs = [];
                menuList.forEach((firstItem, i) => {
                    const { children } = firstItem;
                    const hasChildMenus = children && children.some((child) => child.visible !== false);
                    const isActiveFirst = activeOption === firstItem;
                    const showChild = isActiveFirst && !!activeChildOption;
                    moVNs.push(h('div', {
                        key: `${gIndex}_${i}`,
                        class: ['vxe-context-menu--item-wrapper vxe-context-menu--first-item', firstItem.className || '', {
                                'is--active': isActiveFirst,
                                'is--subactive': isActiveFirst && !!activeChildOption
                            }]
                    }, [
                        hasChildMenus && showChild
                            ? h('div', {
                                class: 'vxe-context-menu--children-wrapper',
                                style: {
                                    transform: `translate(${childOffsetX}px, -2px)`
                                }
                            }, children.map(twoItem => {
                                return h('div', {
                                    class: ['vxe-context-menu--item-wrapper vxe-context-menu--child-item', twoItem.className || '', {
                                            'is--active': activeChildOption === twoItem
                                        }]
                                }, [
                                    $xeContextMenu.renderMenuItem(h, twoItem, firstItem)
                                ]);
                            }))
                            : renderEmptyElement($xeContextMenu),
                        $xeContextMenu.renderMenuItem(h, firstItem, null, hasChildMenus)
                    ]));
                });
                mgVNs.push(h('div', {
                    key: gIndex,
                    class: 'vxe-context-menu--group-wrapper'
                }, moVNs));
            });
            return mgVNs;
        },
        renderVN(h) {
            const $xeContextMenu = this;
            const props = $xeContextMenu;
            const reactData = $xeContextMenu.reactData;
            const { className, position, destroyOnClose } = props;
            const { visible, popupStyle } = reactData;
            const vSize = $xeContextMenu.computeSize;
            return h('div', {
                ref: 'refElem',
                class: ['vxe-context-menu vxe-context-menu--wrapper', position === 'absolute' ? ('is--' + position) : 'is--fixed', className || '', {
                        [`size--${vSize}`]: vSize,
                        'is--visible': visible
                    }],
                style: popupStyle
            }, (destroyOnClose ? visible : true) ? $xeContextMenu.renderMenus(h) : []);
        }
    },
    watch: {
        value() {
            const $xeContextMenu = this;
            $xeContextMenu.handleVisible();
            $xeContextMenu.$nextTick(() => {
                $xeContextMenu.updateLocate();
            });
        },
        computeTopAndLeft() {
            const $xeContextMenu = this;
            $xeContextMenu.handleLocate();
        }
    },
    created() {
        const $xeContextMenu = this;
        $xeContextMenu.internalData = createInternalData();
        $xeContextMenu.handleVisible();
        globalEvents.on($xeContextMenu, 'mousewheel', $xeContextMenu.handleGlobalMousewheelEvent);
        globalEvents.on($xeContextMenu, 'keydown', $xeContextMenu.handleGlobalKeydownEvent);
        globalEvents.on($xeContextMenu, 'mousedown', $xeContextMenu.handleGlobalMousedownEvent);
        globalEvents.on($xeContextMenu, 'blur', $xeContextMenu.handleGlobalBlurEvent);
    },
    beforeDestroy() {
        const $xeContextMenu = this;
        const reactData = $xeContextMenu.reactData;
        const internalData = $xeContextMenu.internalData;
        const { leaveTime } = internalData;
        if (leaveTime) {
            clearTimeout(leaveTime);
        }
        globalEvents.off($xeContextMenu, 'mousewheel');
        globalEvents.off($xeContextMenu, 'keydown');
        globalEvents.off($xeContextMenu, 'mousedown');
        globalEvents.off($xeContextMenu, 'blur');
        XEUtils.assign(reactData, createReactData());
        XEUtils.assign(internalData, createInternalData());
    },
    render(h) {
        return this.renderVN(h);
    }
};
