import { ComponentTypeEnum } from "../interface/type";

import { BaseComponent } from "../base/base-component";

import { isMobileLikeMode, isTrueBrowser, isMiniAppLikeMode } from "../../util/env";

import { showTooltip } from "./utils/show-tooltip";

import { isEmptyPos } from "./utils/common";

import { isSameDimensionInfo } from "../../event/events/dimension/util/common";

import { ChartEvent, Event_Source_Type } from "../../constant/event";

import { isDimensionInfo, isMarkInfo } from "./processor/util";

import { isValid, isNil, array, isNumber, throttle, isObject } from "@visactor/vutils";

import { VChart } from "../../core/vchart";

import { Factory } from "../../core/factory";

import { TooltipSpecTransformer } from "./tooltip-transformer";

import { error } from "../../util";

import { DEFAULT_SHOW_DELAY, TooltipHandlerType } from "./constant";

import { tooltip } from "../../theme/builtin/common/component/tooltip";

export class Tooltip extends BaseComponent {
    constructor() {
        super(...arguments), this.layoutZIndex = 1, this.type = ComponentTypeEnum.tooltip, 
        this.name = ComponentTypeEnum.tooltip, this.transformerConstructor = TooltipSpecTransformer, 
        this.specKey = "tooltip", this.layoutType = "none", this._isReleased = !1, this._alwaysShow = !1, 
        this._eventList = [], this._isTooltipShown = !1, this._clickLock = !1, this._mountEvent = (eType, query, callback) => {
            this.event.on(eType, query, callback), this._eventList.push({
                eventType: eType,
                handler: callback
            });
        }, this._handleClickToLock = params => {
            var _a, _b;
            if (this._clickLock) this._handleChartMouseOut(params), this._clickLock = !1; else {
                if (!this._isTooltipShown && !(null === (_b = null === (_a = this.tooltipHandler) || void 0 === _a ? void 0 : _a.isTooltipShown) || void 0 === _b ? void 0 : _b.call(_a))) return;
                this._clickLock = !0;
            }
        }, this._getMouseOutHandler = needPointerDetection => params => {
            var _a, _b, _c, _d, _e;
            if (this._isReleased) return;
            if (this._alwaysShow || this._clickLock) return;
            if (!this._isTooltipShown && !(null === (_b = null === (_a = this.tooltipHandler) || void 0 === _a ? void 0 : _a.isTooltipShown) || void 0 === _b ? void 0 : _b.call(_a))) return;
            const browserEnv = isTrueBrowser(null === (_c = this._option) || void 0 === _c ? void 0 : _c.mode), {clientX: clientX, clientY: clientY} = params.event;
            browserEnv && needPointerDetection && this._isPointerInChart({
                x: clientX,
                y: clientY
            }) || (this._enterable ? this._outTimer = setTimeout((() => {
                this._handleChartMouseOut(params);
            }), null !== (_e = null === (_d = this._spec) || void 0 === _d ? void 0 : _d.showDelay) && void 0 !== _e ? _e : DEFAULT_SHOW_DELAY) : this._handleChartMouseOut(params));
        }, this._handleChartMouseOut = params => {
            var _a;
            this._cacheActiveType && (null === (_a = this._option) || void 0 === _a ? void 0 : _a.componentShowContent) && (!1 === this._option.componentShowContent.tooltip || isObject(this._option.componentShowContent.tooltip) && !1 === this._option.componentShowContent.tooltip[this._cacheActiveType]) || this._alwaysShow || this._isReleased || this._isEnterTooltip || "none" !== this._spec.triggerOff && (this._hideTooltipByHandler(Object.assign(Object.assign({}, params), {
                tooltip: this
            })), this._handleMouseMove && this._handleMouseMove.cancel && this._handleMouseMove.cancel(), 
            this._cacheEnterableRect = null, this._cacheInfo = void 0, this._cacheParams = void 0, 
            this._cacheActiveType = void 0);
        }, this._getMouseMoveHandler = isClick => params => {
            var _a, _b, _c, _d;
            this._isReleased || this._isEnterTooltip || (this._outTimer && (clearTimeout(this._outTimer), 
            this._outTimer = null), this.tooltipHandler || this._initHandler(), this.processor || this._initProcessor(), 
            this._alwaysShow || (this._clickLock ? isClick && (this._handleChartMouseOut(params), 
            this._clickLock = !1) : !isClick && this._enterable && (null === (_b = null === (_a = this.tooltipHandler) || void 0 === _a ? void 0 : _a.isTooltipShown) || void 0 === _b ? void 0 : _b.call(_a)) ? (this._showTimer && clearTimeout(this._showTimer), 
            this._showTimer = setTimeout((() => {
                this._handleChartMouseMove(params, isClick);
            }), null !== (_d = null === (_c = this._spec) || void 0 === _c ? void 0 : _c.showDelay) && void 0 !== _d ? _d : DEFAULT_SHOW_DELAY)) : this._handleChartMouseMove(params, isClick)));
        }, this._handleChartMouseMove = (params, isClick) => {
            if (this._isReleased) return;
            const mouseEventData = this._getMouseEventData(params), {tooltipInfo: {dimension: dimensionInfo}, ignore: {mark: ignoreMark}} = mouseEventData, success = {
                mark: !1,
                dimension: !1,
                group: !1
            }, supportedTooltip = [ "group", "mark", "dimension" ];
            for (let i = 0, len = supportedTooltip.length; i < len; i++) {
                const type = supportedTooltip[i], res = !!this.processor[type] && this._showTooltipByMouseEvent(type, mouseEventData, params, isClick);
                if ("unShowByOption" === res) success[type] = "unShowByOption"; else if (res) {
                    success[type] = !0;
                    break;
                }
            }
            Object.values(success).every((val => !val)) && !isEmptyPos(params) && (ignoreMark && isMarkInfo(this._cacheInfo) ? success.mark = this._showTooltipByMouseEvent("mark", mouseEventData, params, isClick, !0) : isValid(dimensionInfo) && (success.dimension = this._showTooltipByMouseEvent("dimension", mouseEventData, params, isClick))), 
            !0 === success.mark || !0 === success.group || !0 === success.dimension && !isNil(dimensionInfo) ? this._initEventOfTooltipContent() : this._cacheActiveType && "unShowByOption" === success[this._cacheActiveType] || this._handleChartMouseOut(params);
        }, this._showTooltipByMouseEvent = (activeType, mouseEventData, params, isClick, useCache) => {
            var _a;
            if (!this._showContentByEvent(activeType)) return "unShowByOption";
            const processor = this.processor[activeType];
            if (!processor.shouldHandleTooltip(params, mouseEventData.tooltipInfo[activeType])) return !1;
            let success;
            if (this._hideTimer && clearTimeout(this._hideTimer), useCache) success = !processor.showTooltip(this._cacheInfo, params, !0); else {
                const tooltipInfo = mouseEventData.tooltipInfo[activeType], isSameAsCache = this._isSameAsCache(tooltipInfo, params, activeType);
                success = !processor.showTooltip(tooltipInfo, params, isSameAsCache), success && (this._cacheInfo = tooltipInfo, 
                this._cacheParams = params, this._cacheActiveType = activeType);
            }
            success && (this._isTooltipShown = !0, isClick && this._spec.lockAfterClick && !this._clickLock ? this._clickLock = !0 : Number.isFinite(this._spec.hideTimer) && (this._hideTimer = setTimeout((() => {
                this._handleChartMouseOut();
            }), this._spec.hideTimer)));
            const vchart = null === (_a = this._option) || void 0 === _a ? void 0 : _a.globalInstance;
            return success && VChart.globalConfig.uniqueTooltip && vchart && VChart.hideTooltip(vchart.id), 
            success;
        }, this._getMouseEventData = params => {
            const result = {
                tooltipInfo: {},
                ignore: {}
            };
            return Object.keys(this.processor).forEach((activeType => {
                const {tooltipInfo: tooltipInfo, ignore: ignore} = this.processor[activeType].getMouseEventData(params);
                result.tooltipInfo[activeType] = tooltipInfo, result.ignore[activeType] = ignore;
            })), result;
        }, this._hideTooltipByHandler = params => {
            var _a, _b, _c;
            if (!this._isTooltipShown && !(null === (_b = null === (_a = this.tooltipHandler) || void 0 === _a ? void 0 : _a.isTooltipShown) || void 0 === _b ? void 0 : _b.call(_a))) return 0;
            this.event.emit(ChartEvent.tooltipHide, Object.assign(Object.assign({}, params), {
                source: Event_Source_Type.chart,
                tooltip: this
            })), Object.values(this.processor).forEach((processor => {
                processor.clearCache();
            }));
            const handler = null !== (_c = this._spec.handler) && void 0 !== _c ? _c : this.tooltipHandler;
            if (handler.hideTooltip) {
                const result = handler.hideTooltip.call(handler, params);
                return result || (this._isTooltipShown = !1), result;
            }
            return 1;
        }, this.hideTooltip = () => {
            if (this._isReleased) return !1;
            const params = {
                changePositionOnly: !1,
                tooltip: this,
                item: void 0,
                datum: void 0,
                source: Event_Source_Type.chart
            };
            return this._alwaysShow = !1, !this._hideTooltipByHandler(params);
        };
    }
    isTooltipShown() {
        return this._isTooltipShown;
    }
    _registerEvent() {}
    _releaseEvent() {}
    onLayoutEnd() {}
    created() {
        super.created(), this._regions = this._option.getAllRegions(), this._initEvent();
    }
    release() {
        var _a, _b;
        super.release(), this._isReleased = !0, this._hideTimer && clearTimeout(this._hideTimer), 
        this._eventList.forEach((({eventType: eventType, handler: handler}) => {
            this.event.off(eventType, handler);
        })), this._eventList = [], null === (_b = null === (_a = this.tooltipHandler) || void 0 === _a ? void 0 : _a.release) || void 0 === _b || _b.call(_a), 
        this._isTooltipShown = !1;
    }
    beforeRelease() {
        this.event.emit(ChartEvent.tooltipHide, {
            tooltip: this,
            chart: this.getChart()
        }), this.event.emit(ChartEvent.tooltipRelease, {
            tooltip: this,
            chart: this.getChart()
        });
    }
    _initHandler() {
        var _a, _b, _c;
        const renderMode = null !== (_a = this._spec.renderMode) && void 0 !== _a ? _a : "html", userTooltipHandler = this._option.globalInstance.getTooltipHandlerByUser();
        if (userTooltipHandler) this.tooltipHandler = userTooltipHandler, this._enterable = !1; else {
            const type = "canvas" === renderMode ? TooltipHandlerType.canvas : TooltipHandlerType.dom, handlerConstructor = Factory.getComponentPluginInType(type);
            handlerConstructor || error("Can not find tooltip handler: " + type);
            const handler = new handlerConstructor;
            handler.name = `${this._spec.className}-${null !== (_b = this._option.globalInstance.id) && void 0 !== _b ? _b : 0}-${this.getSpecIndex()}`, 
            null === (_c = this.pluginService) || void 0 === _c || _c.load([ handler ]), this.tooltipHandler = handler, 
            this._spec.enterable && "html" === renderMode && this.tooltipHandler ? (this._enterable = !0, 
            this._needInitEventOfTooltip = !0) : this._enterable = !1;
        }
    }
    _initEventOfTooltipContent() {
        var _a, _b;
        if (!this._needInitEventOfTooltip) return;
        const container = null === (_b = (_a = this.tooltipHandler).getRootDom) || void 0 === _b ? void 0 : _b.call(_a);
        container && (container.addEventListener("pointerenter", (() => {
            var _a;
            if (!this._enterable) return;
            this._isEnterTooltip = !0;
            const rect = null === (_a = container.getBoundingClientRect) || void 0 === _a ? void 0 : _a.call(container);
            rect && (this._cacheEnterableRect = {
                width: rect.width,
                height: rect.height
            }), this._outTimer && (clearTimeout(this._outTimer), this._outTimer = null), this._showTimer && (clearTimeout(this._showTimer), 
            this._showTimer = null);
        })), container.addEventListener("pointerleave", (() => {
            var _a, _b, _c;
            if (this._enterable && (this._isEnterTooltip = !1, "none" !== this._spec.triggerOff && this._cacheEnterableRect)) {
                const newRect = null === (_a = container.getBoundingClientRect) || void 0 === _a ? void 0 : _a.call(container);
                newRect && Object.keys(this._cacheEnterableRect).every((k => this._cacheEnterableRect[k] === newRect[k])) && (this._cacheEnterableRect = null, 
                this._outTimer = setTimeout(this.hideTooltip, null !== (_c = null === (_b = this._spec) || void 0 === _b ? void 0 : _b.showDelay) && void 0 !== _c ? _c : DEFAULT_SHOW_DELAY));
            }
        })), this._needInitEventOfTooltip = !1);
    }
    _initProcessor() {
        const activeType = this._spec.activeType;
        this.processor = {}, activeType.forEach((type => {
            const instance = Factory.createTooltipProcessor(type, this);
            instance && (this.processor[type] = instance);
        }));
    }
    _initEvent() {
        var _a;
        if (this._option.disableTriggerEvent) return;
        const trigger = array(null !== (_a = this._spec.trigger) && void 0 !== _a ? _a : "hover"), triggerOff = array(this._spec.triggerOff), mode = this._option.mode;
        trigger.forEach((triggerType => {
            var _a;
            "hover" === triggerType ? (this._handleMouseMove = this._throttle(this._getMouseMoveHandler(!1)), 
            this._mountEvent("pointermove", {
                source: "chart"
            }, this._handleMouseMove), (isMobileLikeMode(mode) || isMiniAppLikeMode(mode)) && (this._mountEvent("pointerdown", {
                source: "chart"
            }, this._getMouseMoveHandler(!1)), this._mountEvent("pointerup", {
                source: "window"
            }, this._getMouseOutHandler(!0))), this._mountEvent("pointerleave", {
                source: "chart"
            }, this._getMouseOutHandler(!1))) : "click" === triggerType ? (this._mountEvent("pointertap", {
                source: "chart"
            }, this._getMouseMoveHandler(!0)), this._mountEvent("pointerup", {
                source: "window"
            }, this._getMouseOutHandler(!0))) : isObject(triggerType) && this._mountEvent(triggerType.eventType, {
                source: null !== (_a = triggerType.source) && void 0 !== _a ? _a : "chart",
                consume: triggerType.consume
            }, this._getMouseMoveHandler(!0));
        }));
        triggerOff.filter((entry => isObject(entry))).forEach((entry => {
            var _a, _b;
            this._mountEvent(entry.eventType, {
                source: null !== (_a = entry.source) && void 0 !== _a ? _a : "chart",
                consume: entry.consume
            }, this._getMouseOutHandler(null !== (_b = entry.checkOutside) && void 0 !== _b && _b));
        })), !trigger.includes("click") && this._spec.lockAfterClick && this._mountEvent("pointertap", {
            source: "chart"
        }, this._handleClickToLock);
    }
    _throttle(callback) {
        let wait;
        return wait = isNumber(this._spec.throttleInterval) ? this._spec.throttleInterval : "html" === this._spec.renderMode && this._spec.transitionDuration ? 50 : 10, 
        throttle(callback, wait);
    }
    _showContentByEvent(activeType) {
        var _a;
        if (null === (_a = this._option) || void 0 === _a ? void 0 : _a.componentShowContent) {
            if (!1 === this._option.componentShowContent.tooltip) return !1;
            if (isObject(this._option.componentShowContent.tooltip) && !1 === this._option.componentShowContent.tooltip[activeType]) return !1;
        }
        return !0;
    }
    reInit(spec) {
        var _a, _b, _c;
        if (super.reInit(spec), this.tooltipHandler) {
            const renderMode = null !== (_a = this._spec.renderMode) && void 0 !== _a ? _a : "html", newEnterable = this._spec.enterable && "html" === renderMode;
            newEnterable && !this._enterable && (this._needInitEventOfTooltip = !0), this._enterable = newEnterable, 
            null === (_c = (_b = this.tooltipHandler).reInit) || void 0 === _c || _c.call(_b);
        } else this._initHandler();
    }
    showTooltip(datum, options) {
        var _a;
        if (this.tooltipHandler || this._initHandler(), this.processor || this._initProcessor(), 
        !(null === (_a = this.tooltipHandler) || void 0 === _a ? void 0 : _a.showTooltip)) return !1;
        const result = showTooltip(datum, options, this);
        return "none" !== result && (this._alwaysShow = !!(null == options ? void 0 : options.alwaysShow), 
        this._cacheActiveType = result), result;
    }
    _isSameAsCache(nextInfo, nextParams, nextActiveType) {
        if (nextActiveType !== this._cacheActiveType) return !1;
        if (nextInfo === this._cacheInfo) return !0;
        if (isNil(this._cacheInfo) || isNil(nextInfo)) return !1;
        if (isDimensionInfo(nextInfo)) {
            if (isMarkInfo(this._cacheInfo)) return !1;
            const prevInfo = this._cacheInfo;
            return prevInfo.length === nextInfo.length && nextInfo.every(((info, i) => isSameDimensionInfo(info, prevInfo[i])));
        }
        if (isDimensionInfo(this._cacheInfo)) return !1;
        const prevInfo = this._cacheInfo;
        if (!((null == nextInfo ? void 0 : nextInfo.datum) === prevInfo.datum && (null == nextInfo ? void 0 : nextInfo.mark) === prevInfo.mark && (null == nextInfo ? void 0 : nextInfo.series) === prevInfo.series)) return !1;
        const prevParams = this._cacheParams;
        return !isNil(prevParams) && !isNil(nextParams) && (prevParams.mark === nextParams.mark && prevParams.model === nextParams.model && prevParams.datum === nextParams.datum);
    }
    _isPointerInChart(point) {
        var _a;
        const globalInstance = null === (_a = this._option) || void 0 === _a ? void 0 : _a.globalInstance;
        if (!globalInstance) return !1;
        if (!globalInstance.getChart()) return !1;
        const {x: x, y: y} = point, canvas = globalInstance.getCanvas(), {x: chartX, y: chartY, width: chartWidth, height: chartHeight} = canvas.getBoundingClientRect();
        return x >= chartX && x <= chartX + chartWidth && y >= chartY && y <= chartY + chartHeight;
    }
    getVisible() {
        return !1 !== this._spec.visible;
    }
}

Tooltip.type = ComponentTypeEnum.tooltip, Tooltip.transformerConstructor = TooltipSpecTransformer, 
Tooltip.builtInTheme = {
    tooltip: tooltip
}, Tooltip.specKey = "tooltip";

export const registerTooltip = () => {
    Factory.registerComponent(Tooltip.type, Tooltip);
};
//# sourceMappingURL=tooltip.js.map
