import { defineVxeComponent } from '../../ui/src/comp';
import XEUtils from 'xe-utils';
import { getConfig, createEvent, globalMixins } from '../../ui';
import { getLastZIndex, nextZIndex } from '../../ui/src/utils';
import { toCssUnit } from '../../ui/src/dom';
import { getSlotVNs } from '../../ui/src/vn';
function createReactData() {
    return {
        target: null,
        isUpdate: false,
        visible: false,
        tipPos: null,
        tipContent: '',
        tipActive: false,
        tipTarget: null,
        tipZindex: 0,
        tipStore: {
            style: {},
            placement: '',
            arrowStyle: {}
        }
    };
}
function createInternalData() {
    return {
    // showDelayTip: undefined
    };
}
export default {
    name: 'VxeTooltip',
    mixins: [
        globalMixins.sizeMixin
    ],
    props: {
        value: Boolean,
        size: {
            type: String,
            default: () => getConfig().tooltip.size || getConfig().size
        },
        selector: String,
        trigger: {
            type: String,
            default: () => getConfig().tooltip.trigger || 'hover'
        },
        theme: {
            type: String,
            default: () => getConfig().tooltip.theme || 'dark'
        },
        content: {
            type: [String, Number],
            default: null
        },
        useHtml: {
            type: Boolean,
            default: () => XEUtils.isBoolean(getConfig().tooltip.useHtml) ? getConfig().tooltip.useHtml : null
        },
        // 已废弃，请使用 useHtml
        useHTML: {
            type: Boolean,
            default: () => getConfig().tooltip.useHTML
        },
        zIndex: [String, Number],
        popupClassName: [String, Function],
        width: {
            type: [String, Number],
            default: () => getConfig().tooltip.Width
        },
        height: {
            type: [String, Number],
            default: () => getConfig().tooltip.height
        },
        minWidth: {
            type: [String, Number],
            default: () => getConfig().tooltip.minWidth
        },
        minHeight: {
            type: [String, Number],
            default: () => getConfig().tooltip.minHeight
        },
        maxWidth: {
            type: [String, Number],
            default: () => getConfig().tooltip.maxWidth
        },
        maxHeight: {
            type: [String, Number],
            default: () => getConfig().tooltip.maxHeight
        },
        placement: {
            type: String,
            default: () => getConfig().tooltip.placement
        },
        defaultPlacement: {
            type: String,
            default: () => getConfig().tooltip.defaultPlacement
        },
        isArrow: {
            type: Boolean,
            default: () => getConfig().tooltip.isArrow
        },
        enterable: {
            type: Boolean,
            default: () => getConfig().tooltip.enterable
        },
        enterDelay: {
            type: Number,
            default: () => getConfig().tooltip.enterDelay
        },
        leaveDelay: {
            type: Number,
            default: () => getConfig().tooltip.leaveDelay
        }
    },
    data() {
        const xID = XEUtils.uniqueId();
        const reactData = createReactData();
        return Object.assign(Object.assign({}, {}), { xID,
            reactData });
    },
    computed: Object.assign(Object.assign({}, {}), { computeWrapperStyle() {
            const $xeTooltip = this;
            const props = $xeTooltip;
            const { width, height, minHeight, minWidth, maxHeight, maxWidth } = props;
            const stys = {};
            if (width) {
                stys.width = toCssUnit(width);
            }
            if (height) {
                stys.height = toCssUnit(height);
            }
            if (minWidth) {
                stys.minWidth = toCssUnit(minWidth);
            }
            if (minHeight) {
                stys.minHeight = toCssUnit(minHeight);
            }
            if (maxWidth) {
                stys.maxWidth = toCssUnit(maxWidth);
            }
            if (maxHeight) {
                stys.maxHeight = toCssUnit(maxHeight);
            }
            return stys;
        } }),
    methods: {
        //
        // Method
        //
        dispatchEvent(type, params, evnt) {
            const $xeTooltip = this;
            $xeTooltip.$emit(type, createEvent(evnt, { $tooltip: $xeTooltip }, params));
        },
        emitModel(value) {
            const $xeTooltip = this;
            const { _events } = $xeTooltip;
            $xeTooltip.$emit('input', value);
            if (_events && _events.modelValue) {
                $xeTooltip.$emit('modelValue', value);
            }
            else {
                $xeTooltip.$emit('model-value', value);
            }
        },
        getSelectorEl() {
            const $xeTooltip = this;
            const props = $xeTooltip;
            const { selector } = props;
            if (selector) {
                if (XEUtils.isElement(selector)) {
                    return selector;
                }
                if (XEUtils.isString(selector)) {
                    return document.querySelector(selector);
                }
            }
            return null;
        },
        openByEvent(evnt, target, content) {
            const $xeTooltip = this;
            const reactData = $xeTooltip.reactData;
            return $xeTooltip.handleVisible(target || reactData.target || $xeTooltip.getSelectorEl(), content, evnt);
        },
        open(target, content) {
            const $xeTooltip = this;
            const reactData = $xeTooltip.reactData;
            return $xeTooltip.handleVisible(target || reactData.target || $xeTooltip.getSelectorEl(), content);
        },
        close() {
            const $xeTooltip = this;
            const reactData = $xeTooltip.reactData;
            reactData.tipPos = null;
            reactData.tipTarget = null;
            reactData.tipActive = false;
            Object.assign(reactData.tipStore, {
                style: {},
                placement: '',
                arrowStyle: null
            });
            $xeTooltip.updateValue(false);
            return $xeTooltip.$nextTick();
        },
        toVisible(target, content) {
            const $xeTooltip = this;
            return $xeTooltip.handleVisible(target, content);
        },
        updatePlacement() {
            const $xeTooltip = this;
            const reactData = $xeTooltip.reactData;
            const { visible, tipTarget } = reactData;
            let el = $xeTooltip.$refs.refElem;
            if (visible && tipTarget && el) {
                $xeTooltip.updateTipStyle();
            }
            return $xeTooltip.$nextTick().then(() => {
                const { tipTarget } = reactData;
                el = $xeTooltip.$refs.refElem;
                if (tipTarget && el) {
                    $xeTooltip.updateTipStyle();
                    return $xeTooltip.$nextTick().then(() => {
                        $xeTooltip.updateTipStyle();
                    });
                }
            });
        },
        isActived() {
            const $xeTooltip = this;
            const reactData = $xeTooltip.reactData;
            return reactData.tipActive;
        },
        setActived(active) {
            const $xeTooltip = this;
            const reactData = $xeTooltip.reactData;
            reactData.tipActive = !!active;
        },
        updateTipStyle() {
            const $xeTooltip = this;
            const props = $xeTooltip;
            const reactData = $xeTooltip.reactData;
            const { isArrow, placement, defaultPlacement } = props;
            const { tipTarget: targetElem, tipStore, tipPos } = reactData;
            let top = '';
            let left = '';
            let panelPlacement = 'bottom';
            let arrowLeft = '';
            const panelElem = $xeTooltip.$refs.refElem;
            if (panelElem && targetElem) {
                const documentElement = document.documentElement;
                const bodyElem = document.body;
                const targetWidth = targetElem.offsetWidth;
                const targetHeight = targetElem.offsetHeight;
                const panelHeight = panelElem.offsetHeight;
                const panelWidth = panelElem.offsetWidth;
                const targetRect = targetElem.getBoundingClientRect();
                const visibleHeight = documentElement.clientHeight || bodyElem.clientHeight;
                const visibleWidth = documentElement.clientWidth || bodyElem.clientWidth;
                const marginSize = 6;
                left = targetRect.left;
                top = targetRect.top + targetHeight;
                if (tipPos && (tipPos.oLeft || tipPos.oTop)) {
                    if (isArrow) {
                        left = left + Math.max(8, Math.min(targetWidth - 8, tipPos.oLeft)) - panelWidth / 2;
                    }
                    else {
                        left = tipPos.x + 1;
                        top = tipPos.y + 1;
                    }
                }
                else {
                    left = targetRect.left + (targetWidth - panelWidth) / 2;
                }
                if (placement === 'top') {
                    panelPlacement = 'top';
                    top = targetRect.top - panelHeight;
                }
                else if (!placement) {
                    if (defaultPlacement === 'top') {
                        panelPlacement = 'top';
                        top = targetRect.top - panelHeight;
                        // 如果上面不够放，则向下
                        if (top < marginSize) {
                            panelPlacement = 'bottom';
                            top = targetRect.top + targetHeight;
                        }
                        // 如果下面不够放，则向上（优先）
                        if (top + panelHeight + marginSize > visibleHeight) {
                            panelPlacement = 'top';
                            top = targetRect.top - panelHeight;
                        }
                    }
                    else {
                        // 如果下面不够放，则向上
                        if (top + panelHeight + marginSize > visibleHeight) {
                            panelPlacement = 'top';
                            top = targetRect.top - panelHeight;
                        }
                        // 如果上面不够放，则向下（优先）
                        if (top < marginSize) {
                            panelPlacement = 'bottom';
                            top = targetRect.top + targetHeight;
                        }
                    }
                }
                // 如果溢出右边
                if (left + panelWidth + marginSize > visibleWidth) {
                    left -= left + panelWidth + marginSize - visibleWidth;
                }
                // 如果溢出左边
                if (left < marginSize) {
                    left = marginSize;
                }
                // 箭头
                if (left === targetRect.left) {
                    if (targetWidth <= panelWidth) {
                        arrowLeft = targetWidth / 2;
                    }
                }
                else if (left < targetRect.left) {
                    if (left + panelWidth > targetRect.left + targetWidth) {
                        arrowLeft = (targetRect.left - left) + targetWidth / 2;
                    }
                    else {
                        arrowLeft = (targetRect.left - left) + (panelWidth - (targetRect.left - left)) / 2;
                    }
                }
                tipStore.placement = panelPlacement;
                tipStore.style.top = `${top}px`;
                tipStore.style.left = `${left}px`;
                tipStore.arrowStyle.left = `${arrowLeft}px`;
            }
        },
        updateValue(value) {
            const $xeTooltip = this;
            const reactData = $xeTooltip.reactData;
            if (value !== reactData.visible) {
                reactData.visible = value;
                reactData.isUpdate = true;
                $xeTooltip.emitModel(value);
            }
        },
        updateZindex() {
            const $xeTooltip = this;
            const reactData = $xeTooltip.reactData;
            if (reactData.tipZindex < getLastZIndex()) {
                reactData.tipZindex = nextZIndex();
            }
        },
        clickEvent() {
            const $xeTooltip = this;
            const props = $xeTooltip;
            const reactData = $xeTooltip.reactData;
            if (reactData.visible) {
                $xeTooltip.close();
            }
            else {
                $xeTooltip.handleVisible(reactData.target || $xeTooltip.getSelectorEl(), props.content);
            }
        },
        targetMouseenterEvent() {
            const $xeTooltip = this;
            const props = $xeTooltip;
            const reactData = $xeTooltip.reactData;
            $xeTooltip.handleVisible(reactData.target || $xeTooltip.getSelectorEl(), props.content);
        },
        targetMouseleaveEvent() {
            const $xeTooltip = this;
            const props = $xeTooltip;
            const reactData = $xeTooltip.reactData;
            const { trigger, enterable, leaveDelay } = props;
            reactData.tipActive = false;
            if (enterable && trigger === 'hover') {
                setTimeout(() => {
                    if (!reactData.tipActive) {
                        $xeTooltip.close();
                    }
                }, leaveDelay);
            }
            else {
                $xeTooltip.close();
            }
        },
        wrapperMouseenterEvent() {
            const $xeTooltip = this;
            const reactData = $xeTooltip.reactData;
            reactData.tipActive = true;
        },
        wrapperMouseleaveEvent() {
            const $xeTooltip = this;
            const props = $xeTooltip;
            const reactData = $xeTooltip.reactData;
            const { trigger, enterable, leaveDelay } = props;
            reactData.tipActive = false;
            if (enterable && trigger === 'hover') {
                setTimeout(() => {
                    if (!reactData.tipActive) {
                        $xeTooltip.close();
                    }
                }, leaveDelay);
            }
        },
        showTip() {
            const $xeTooltip = this;
            const props = $xeTooltip;
            const reactData = $xeTooltip.reactData;
            const { tipStore } = reactData;
            const el = $xeTooltip.$refs.refElem;
            if (el) {
                const parentNode = el.parentNode;
                if (!parentNode) {
                    document.body.appendChild(el);
                }
            }
            $xeTooltip.updateValue(true);
            $xeTooltip.updateZindex();
            tipStore.placement = 'top';
            tipStore.style = { width: 'auto', left: 0, top: 0, zIndex: props.zIndex || reactData.tipZindex };
            tipStore.arrowStyle = { left: '50%' };
            return $xeTooltip.updatePlacement();
        },
        handleDelayFn() {
            const $xeTooltip = this;
            const props = $xeTooltip;
            const reactData = $xeTooltip.reactData;
            const internalData = $xeTooltip.internalData;
            internalData.showDelayTip = XEUtils.debounce(() => {
                if (reactData.tipActive) {
                    $xeTooltip.showTip();
                }
            }, props.enterDelay, { leading: false, trailing: true });
        },
        handleVisible(target, content, evnt) {
            const $xeTooltip = this;
            const props = $xeTooltip;
            const slots = $xeTooltip.$scopedSlots;
            const reactData = $xeTooltip.reactData;
            const internalData = $xeTooltip.internalData;
            const contentSlot = slots.content;
            if (!contentSlot && (content === '' || XEUtils.eqNull(content))) {
                return $xeTooltip.$nextTick();
            }
            if (target) {
                const { showDelayTip } = internalData;
                const { trigger, enterDelay } = props;
                if (evnt) {
                    reactData.tipPos = {
                        x: evnt.clientX,
                        y: evnt.clientY,
                        oLeft: evnt.offsetX,
                        oTop: evnt.offsetY
                    };
                }
                else {
                    reactData.tipPos = null;
                }
                reactData.tipActive = true;
                reactData.tipTarget = target;
                reactData.tipContent = content;
                if (reactData.visible) {
                    return $xeTooltip.updatePlacement();
                }
                if (enterDelay && trigger === 'hover') {
                    if (showDelayTip) {
                        showDelayTip();
                    }
                }
                else {
                    return $xeTooltip.showTip();
                }
            }
            return $xeTooltip.$nextTick();
        },
        wheelEvent(evnt) {
            evnt.stopPropagation();
        },
        //
        // Render
        //
        renderContent(h) {
            const $xeTooltip = this;
            const props = $xeTooltip;
            const slots = $xeTooltip.$scopedSlots;
            const reactData = $xeTooltip.reactData;
            const { useHtml, useHTML } = props;
            const { tipContent } = reactData;
            const wrapperStyle = $xeTooltip.computeWrapperStyle;
            const contentSlot = slots.content;
            const contVNs = [];
            if (contentSlot) {
                contVNs.push(h('div', {
                    key: 'ct'
                }, getSlotVNs(contentSlot({}))));
            }
            else if (XEUtils.isBoolean(useHtml) ? useHtml : useHTML) {
                contVNs.push(h('div', {
                    key: 'ch',
                    domProps: {
                        innerHTML: tipContent
                    }
                }));
            }
            else {
                contVNs.push(h('span', {
                    key: 'cd'
                }, `${tipContent}`));
            }
            return h('div', {
                key: 3,
                ref: 'contentWrapperfElem',
                class: 'vxe-tooltip--content',
                style: wrapperStyle
            }, contVNs);
        },
        renderVN(h) {
            const $xeTooltip = this;
            const props = $xeTooltip;
            const slots = $xeTooltip.$scopedSlots;
            const reactData = $xeTooltip.reactData;
            const { popupClassName, theme, isArrow, enterable } = props;
            const { tipActive, visible, tipStore } = reactData;
            const defaultSlot = slots.default;
            const vSize = $xeTooltip.computeSize;
            let ons;
            if (enterable) {
                ons = {
                    mouseenter: $xeTooltip.wrapperMouseenterEvent,
                    mouseleave: $xeTooltip.wrapperMouseleaveEvent
                };
            }
            return h('div', {
                ref: 'refElem',
                class: ['vxe-tooltip--wrapper', `theme--${theme}`, popupClassName ? (XEUtils.isFunction(popupClassName) ? popupClassName({ $tooltip: $xeTooltip }) : popupClassName) : '', {
                        [`size--${vSize}`]: vSize,
                        [`placement--${tipStore.placement}`]: tipStore.placement,
                        'is--enterable': enterable,
                        'is--visible': visible,
                        'is--arrow': isArrow,
                        'is--active': tipActive
                    }],
                style: tipStore.style,
                on: ons
            }, [
                h('div', {
                    key: 'tby',
                    class: 'vxe-tooltip--body'
                }, [
                    $xeTooltip.renderContent(h),
                    h('div', {
                        class: 'vxe-tooltip--arrow',
                        style: tipStore.arrowStyle
                    })
                ]),
                ...(defaultSlot ? getSlotVNs(defaultSlot({})) : [])
            ]);
        }
    },
    watch: {
        content(val) {
            const $xeTooltip = this;
            const reactData = $xeTooltip.reactData;
            reactData.tipContent = val;
        },
        value(val) {
            const $xeTooltip = this;
            const props = $xeTooltip;
            const reactData = $xeTooltip.reactData;
            if (!reactData.isUpdate) {
                if (val) {
                    $xeTooltip.handleVisible(reactData.target || $xeTooltip.getSelectorEl(), props.content);
                }
                else {
                    $xeTooltip.close();
                }
            }
            reactData.isUpdate = false;
        },
        enterDelay() {
            const $xeTooltip = this;
            $xeTooltip.handleDelayFn();
        }
    },
    created() {
        const $xeTooltip = this;
        $xeTooltip.internalData = createInternalData();
        $xeTooltip.handleDelayFn();
    },
    mounted() {
        const $xeTooltip = this;
        const props = $xeTooltip;
        const reactData = $xeTooltip.reactData;
        const contentWrapperfEl = $xeTooltip.$refs.contentWrapperfElem;
        if (contentWrapperfEl) {
            contentWrapperfEl.addEventListener('wheel', $xeTooltip.wheelEvent, { passive: false });
        }
        $xeTooltip.$nextTick(() => {
            const { trigger, content } = props;
            const wrapperElem = $xeTooltip.$refs.refElem;
            if (wrapperElem) {
                const parentNode = wrapperElem.parentNode;
                if (parentNode) {
                    reactData.tipContent = content;
                    reactData.tipZindex = nextZIndex();
                    XEUtils.arrayEach(wrapperElem.children, (elem, index) => {
                        if (index) {
                            parentNode.insertBefore(elem, wrapperElem);
                            if (!reactData.target) {
                                reactData.target = elem;
                            }
                        }
                    });
                    parentNode.removeChild(wrapperElem);
                    const { target } = reactData;
                    if (target) {
                        if (trigger === 'hover') {
                            target.onmouseenter = $xeTooltip.targetMouseenterEvent;
                            target.onmouseleave = $xeTooltip.targetMouseleaveEvent;
                        }
                        else if (trigger === 'click') {
                            target.onclick = $xeTooltip.clickEvent;
                        }
                    }
                    if (props.value) {
                        $xeTooltip.handleVisible(target || $xeTooltip.getSelectorEl(), content);
                    }
                }
            }
        });
    },
    beforeDestroy() {
        const $xeTooltip = this;
        const reactData = $xeTooltip.reactData;
        const { target } = reactData;
        const wrapperElem = $xeTooltip.$refs.refElem;
        if (target) {
            target.onmouseenter = null;
            target.onmouseleave = null;
            target.onclick = null;
        }
        const contentWrapperfEl = $xeTooltip.$refs.contentWrapperfElem;
        if (contentWrapperfEl) {
            contentWrapperfEl.removeEventListener('wheel', $xeTooltip.wheelEvent);
        }
        if (wrapperElem) {
            const parentNode = wrapperElem.parentNode;
            if (parentNode) {
                parentNode.removeChild(wrapperElem);
            }
        }
    },
    destroyed() {
        const $xeTooltip = this;
        const reactData = $xeTooltip.reactData;
        const internalData = $xeTooltip.internalData;
        XEUtils.assign(reactData, createReactData());
        XEUtils.assign(internalData, createInternalData());
    },
    render(h) {
        return this.renderVN(h);
    }
};
