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

import { LayoutState } from "./interface";

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

import { isString } from "../util/type";

import { isObject, isValid } from "@visactor/vutils";

import { createGroup, Stage, vglobal, waitForAllSubLayers } from "@visactor/vrender-core";

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

import { findMarkGraphic, getDatumOfGraphic } from "../util/mark";

import { diffMarks, findSimpleMarks, toRenderMode, traverseGroupMark } from "./util";

import { log } from "../util/debug";

export class Compiler {
    getRootGroup() {
        return this._rootGroup;
    }
    constructor(container, option) {
        this._count = 0, this._rootMarks = [], this._viewListeners = new Map, this._windowListeners = new Map, 
        this._canvasListeners = new Map, this.isInited = !1, this._released = !1, this._compileChart = null, 
        this.handleLayoutEnd = () => {
            var _a, _b;
            null === (_b = null === (_a = this._compileChart) || void 0 === _a ? void 0 : _a.getEvent()) || void 0 === _b || _b.emit(ChartEvent.afterMarkLayoutEnd, {
                chart: this._compileChart
            });
        }, this.handleStageRender = () => {
            var _a, _b;
            null === (_b = null === (_a = this._compileChart) || void 0 === _a ? void 0 : _a.getEvent()) || void 0 === _b || _b.emit(ChartEvent.afterRender, {
                chart: this._compileChart
            });
        }, this._handleAfterNextRender = () => {
            var _a, _b, _c, _d;
            this._stage && !this._option.disableDirtyBounds && this._stage.enableDirtyBounds(), 
            this._compileChart && (null === (_a = this._compileChart.getEvent()) || void 0 === _a || _a.emit(ChartEvent.renderFinished, {
                chart: this._compileChart,
                vchart: null === (_b = this._compileChart.getOption()) || void 0 === _b ? void 0 : _b.globalInstance
            })), null === (_d = null === (_c = this._option.performanceHook) || void 0 === _c ? void 0 : _c.afterVRenderDraw) || void 0 === _d || _d.call(_c, this._compileChart.getOption().globalInstance);
        }, this.handleProgressiveFrame = () => {
            this._progressiveMarks.length && this._progressiveMarks.forEach((mark => {
                mark.isDoingProgressive() && mark.renderProgressive();
            })), this.doPreProgressive();
        }, this._container = container, this._option = option;
    }
    getChart() {
        return this._compileChart;
    }
    getCanvas() {
        var _a;
        return null === (_a = this._stage) || void 0 === _a ? void 0 : _a.window.getNativeHandler().nativeCanvas;
    }
    getStage() {
        return this._stage;
    }
    initView() {
        var _a, _b, _c, _d;
        if (this._released) return;
        if (this.isInited = !0, this._stage) return;
        const {autoRefreshDpr: autoRefreshDpr, dpr: dpr, mode: mode, modeParams: modeParams, gestureConfig: gestureConfig, interactive: interactive, clickInterval: clickInterval, autoPreventDefault: autoPreventDefault, background: background} = this._option;
        vglobal.setEnv(toRenderMode(mode), null != modeParams ? modeParams : {}), this._stage = null !== (_a = this._option.stage) && void 0 !== _a ? _a : new Stage(Object.assign({
            background: background,
            width: this._width,
            height: this._height,
            container: null !== (_b = this._container.dom) && void 0 !== _b ? _b : null,
            canvas: null !== (_c = this._container.canvas) && void 0 !== _c ? _c : null,
            dpr: dpr,
            viewBox: this._option.viewBox,
            canvasControled: this._option.canvasControled,
            beforeRender: stage => {
                var _a, _b, _c;
                null === (_a = this._compileChart) || void 0 === _a || _a.onBeforeRender(), null === (_c = (_b = this._option).beforeRender) || void 0 === _c || _c.call(_b, stage);
            },
            afterRender: this._option.afterRender,
            disableDirtyBounds: !0,
            autoRender: !0,
            ticker: this._option.ticker,
            pluginList: this._option.pluginList,
            enableHtmlAttribute: this._option.enableHtmlAttribute,
            optimize: this._option.optimize,
            supportsTouchEvents: this._option.supportsTouchEvents,
            supportsPointerEvents: this._option.supportsPointerEvents,
            event: {
                clickInterval: clickInterval,
                autoPreventDefault: autoPreventDefault
            },
            ReactDOM: this._option.ReactDOM,
            autoRefresh: isValid(autoRefreshDpr) ? autoRefreshDpr : !isValid(dpr)
        }, null !== (_d = this._option.renderHooks) && void 0 !== _d ? _d : {})), this._stage.enableIncrementalAutoRender(), 
        this._stage.setTheme({
            symbol: {
                shape: "circle",
                size: 8
            },
            text: {
                fontSize: 14,
                fill: "#000000"
            }
        });
        const group = createGroup({
            x: 0,
            y: 0,
            width: this._width,
            height: this._height
        });
        group.name = "root", this._stage.defaultLayer.appendChild(group), this._rootGroup = group;
        const GestureController = (isValid(gestureConfig) ? gestureConfig : isMobileLikeMode(mode)) && !1 !== interactive && Factory.getStageEventPlugin("gesture");
        GestureController && (this._gestureController = new GestureController(this._stage, isObject(gestureConfig) ? gestureConfig : {})), 
        this._setCanvasStyle(), this.getStage().hooks.afterRender.tap("chart-event", this.handleStageRender), 
        !1 !== interactive && this._viewListeners.forEach((listener => {}));
    }
    getLayoutState() {
        return this._layoutState;
    }
    updateLayoutTag() {
        this._layoutState = LayoutState.before;
    }
    _setCanvasStyle() {
        if (this._stage && this._container.dom && !isString(this._container.dom)) {
            this._container.dom.style.display = "block", this._container.dom.style.position = "relative";
            const canvas = this.getCanvas();
            canvas && (canvas.style.display = "block");
        }
    }
    compile(ctx, option) {
        if (this._released) return;
        const {chart: chart} = ctx;
        this._compileChart = chart, this.initView(), this._stage && ("render" !== (null == option ? void 0 : option.actionSource) && this._cachedMarks && (this.reuseOrMorphing(option.morphConfig), 
        this._cachedMarks = null), chart.compile(), chart.afterCompile());
    }
    clearNextRender() {
        return !!this._nextRafId && (vglobal.getSpecifiedCancelAnimationFrame(10)(this._nextRafId), 
        this._nextRafId = null, !0);
    }
    clear(ctx) {
        const {chart: chart} = ctx;
        this.clearNextRender(), chart.clear();
    }
    renderNextTick(morphConfig) {
        this._released || (this._nextRafId && this.clearNextRender(), this._nextRafId = vglobal.getSpecifiedRequestAnimationFrame(10)((() => {
            this._nextRafId = null, this.render(morphConfig);
        })));
    }
    _commitedAll() {
        return this._rootMarks.some((mark => traverseGroupMark(mark, (m => m.commit()))));
    }
    _hasCommitedMark() {
        return this._rootMarks.some((mark => traverseGroupMark(mark, (m => m.isCommited()), null, null, !0)));
    }
    _doRender(immediately) {
        var _a, _b, _c, _d, _e, _f;
        null === (_b = null === (_a = this._option.performanceHook) || void 0 === _a ? void 0 : _a.beforeDoRender) || void 0 === _b || _b.call(_a, this._compileChart.getOption().globalInstance), 
        this._stage && (this._rootMarks.forEach((g => {
            traverseGroupMark(g, (m => {
                m.needClear && (this._progressiveMarks || m.runAnimation(), m.clearExitGraphics(), 
                m.needClear = !1);
            }), null, !0);
        })), null === (_d = null === (_c = this._option.performanceHook) || void 0 === _c ? void 0 : _c.beforeVRenderDraw) || void 0 === _d || _d.call(_c, this._compileChart.getOption().globalInstance), 
        this._stage.disableDirtyBounds(), this._stage.afterNextRender(this._handleAfterNextRender), 
        immediately && (this._stage.render(), null === (_f = null === (_e = this._option.performanceHook) || void 0 === _e ? void 0 : _e.afterVRenderDraw) || void 0 === _f || _f.call(_e, this._compileChart.getOption().globalInstance)));
    }
    renderMarks() {
        var _a;
        this._hasCommitedMark() && (log(`--- start of renderMarks(${this._count}) ---`), 
        this.clearProgressive(), this._rootMarks.forEach((mark => {
            mark.render();
        })), this._layoutState === LayoutState.before && (this._layoutState = LayoutState.layouting, 
        null === (_a = this._compileChart) || void 0 === _a || _a.onLayout(), this._layoutState = LayoutState.reevaluate, 
        this._hasCommitedMark() && this._rootMarks.forEach((mark => {
            mark.render();
        })), this.handleLayoutEnd()), this.findProgressiveMarks(), this._doRender(!0), this.doPreProgressive(), 
        log(`--- start of renderMarks(${this._count}) ---`), this._count++);
    }
    reuseOrMorphing(morphConfig = {}) {
        const {reuse: reuse = !0, morph: morph = !0, morphAll: morphAll = !1, animation: animation = {}, enableExitAnimation: enableExitAnimation = !1} = morphConfig, newMarks = findSimpleMarks(this._rootMarks), {update: update, exit: exit} = diffMarks(this._cachedMarks, newMarks, {
            morph: morph,
            morphAll: morphAll,
            reuse: reuse
        });
        update.forEach((({prev: prev, next: next}) => {
            if (reuse && 1 === prev.length && 1 === next.length && prev[0].type === next[0].type) next[0].reuse(prev[0]); else {
                const prevMark = prev.filter((item => item.getMarkConfig().morph))[0];
                prevMark && next.forEach((item => {
                    item.getMarkConfig().morph && item.prepareMorph(prevMark);
                }));
            }
        })), exit.forEach((({prev: prev}) => {
            prev.forEach((m => {
                m.removeProduct();
            }));
        }));
    }
    render(morphConfig) {
        if (this._released) return;
        if (this.clearNextRender(), this.initView(), !this._stage) return;
        const {width: width, height: height} = this._rootGroup.attribute;
        this._width === width && this._height === height || this._rootGroup.setAttributes({
            width: this._width,
            height: this._height
        }), this.renderMarks(), this.clearNextRender() && this.renderMarks();
    }
    updateViewBox(viewBox, reRender = !0) {
        if (!this._stage) return;
        const prevViewBox = this._stage.viewBox;
        !viewBox || prevViewBox && prevViewBox.x1 === viewBox.x1 && prevViewBox.y1 === viewBox.y1 && prevViewBox.x2 === viewBox.x2 && prevViewBox.y2 === viewBox.y2 || this._stage.setViewBox(viewBox, reRender);
    }
    resize(width, height, reRender = !0) {
        if (!this._stage) return;
        const hasChange = this._width !== width || this._height !== height;
        this._width = width, this._height = height, hasChange && (this._stage.resize(width, height), 
        this._commitedAll(), reRender && this.render({
            morph: !1
        }));
    }
    setBackground(color) {
        this._stage && (this._stage.background = color);
    }
    setSize(width, height) {
        this._width = width, this._height = height, this._stage;
    }
    setViewBox(viewBox, reRender = !0) {
        this.updateViewBox(viewBox, reRender);
    }
    addEventListener(source, type, callback) {
        var _a, _b;
        if (!1 !== this._option.interactive) if (source === Event_Source_Type.chart) {
            const rootGroup = this.getRootGroup(), wrappedCallback = function(event) {
                var _a;
                const graphic = event.target;
                let markGraphic = null;
                graphic && (markGraphic = isValid(graphic.context) ? graphic : findMarkGraphic(rootGroup, graphic));
                const context = null !== (_a = null == markGraphic ? void 0 : markGraphic.context) && void 0 !== _a ? _a : {}, markId = isValid(context.markId) ? context.markId : null, modelId = isValid(context.modelId) ? context.modelId : null, modelUserId = isValid(context.modelUserId) ? context.modelUserId : null, markUserId = isValid(context.markUserId) ? context.markUserId : null, params = {
                    event: event,
                    type: type,
                    source: source,
                    item: markGraphic,
                    datum: getDatumOfGraphic(markGraphic),
                    markId: markId,
                    modelId: modelId,
                    markUserId: markUserId,
                    modelUserId: modelUserId
                };
                callback.call(null, params);
            }.bind(this);
            this._viewListeners.set(callback, {
                type: type,
                callback: wrappedCallback
            }), null === (_a = this._stage) || void 0 === _a || _a.addEventListener(type, wrappedCallback);
        } else if (source === Event_Source_Type.window) {
            const wrappedCallback = function(event) {
                const params = {
                    event: event,
                    type: type,
                    source: source,
                    item: null,
                    datum: null,
                    markId: null,
                    modelId: null,
                    markUserId: null,
                    modelUserId: null
                };
                callback.call(null, params);
            }.bind(this);
            this._windowListeners.set(callback, {
                type: type,
                callback: wrappedCallback
            });
            const windowObject = this._getGlobalThis();
            null == windowObject || windowObject.addEventListener(type, wrappedCallback);
        } else if (source === Event_Source_Type.canvas) {
            const wrappedCallback = function(event) {
                const params = {
                    event: event,
                    type: type,
                    source: source,
                    item: null,
                    datum: null,
                    markId: null,
                    modelId: null,
                    markUserId: null,
                    modelUserId: null
                };
                callback.call(null, params);
            }.bind(this);
            this._canvasListeners.set(callback, {
                type: type,
                callback: wrappedCallback
            });
            const canvasObject = null === (_b = this.getStage()) || void 0 === _b ? void 0 : _b.window;
            null == canvasObject || canvasObject.addEventListener(type, wrappedCallback);
        }
    }
    removeEventListener(source, type, callback) {
        var _a, _b, _c, _d, _e;
        if (!1 !== this._option.interactive) if (source === Event_Source_Type.chart) {
            const wrappedCallback = null === (_a = this._viewListeners.get(callback)) || void 0 === _a ? void 0 : _a.callback;
            wrappedCallback && (null === (_b = this._stage) || void 0 === _b || _b.removeEventListener(type, wrappedCallback)), 
            this._viewListeners.delete(callback);
        } else if (source === Event_Source_Type.window) {
            const windowObject = this._getGlobalThis(), wrappedCallback = null === (_c = this._windowListeners.get(callback)) || void 0 === _c ? void 0 : _c.callback;
            wrappedCallback && (null == windowObject || windowObject.removeEventListener(type, wrappedCallback)), 
            this._windowListeners.delete(callback);
        } else if (source === Event_Source_Type.canvas) {
            const canvasObject = null === (_d = this.getStage()) || void 0 === _d ? void 0 : _d.window, wrappedCallback = null === (_e = this._canvasListeners.get(callback)) || void 0 === _e ? void 0 : _e.callback;
            canvasObject && wrappedCallback && (null == canvasObject || canvasObject.removeEventListener(type, wrappedCallback)), 
            this._canvasListeners.delete(callback);
        }
    }
    releaseEvent() {
        const stage = this.getStage();
        stage && stage.hooks.afterRender.unTap("chart-event", this.handleStageRender), this._viewListeners.clear(), 
        this._windowListeners.clear(), this._canvasListeners.clear();
    }
    release() {
        var _a;
        this.clearNextRender(), this.releaseEvent(), this._option = this._container = null, 
        this.releaseGrammar(!0), this._stage !== (null === (_a = this._option) || void 0 === _a ? void 0 : _a.stage) && this._stage.release(), 
        this._stage = null, this.isInited = !1, this._compileChart = null, this._released = !0;
    }
    releaseGrammar(removeGraphicItems = !1) {
        removeGraphicItems ? this._rootMarks.forEach((g => {
            traverseGroupMark(g, (m => {
                m.removeProduct();
            }), null, !0);
        })) : this._cachedMarks = findSimpleMarks(this._rootMarks), this._rootMarks = [];
    }
    addRootMark(mark) {
        this._rootMarks.includes(mark) || this._rootMarks.push(mark);
    }
    getRootMarks() {
        return this._rootMarks;
    }
    removeRootMark(mark) {
        const index = this._rootMarks.findIndex((m => m === mark));
        return index >= 0 && (this._rootMarks.splice(index, 1), !0);
    }
    _getGlobalThis() {
        var _a;
        return isTrueBrowser(this._option.mode) ? globalThis : null === (_a = this.getStage()) || void 0 === _a ? void 0 : _a.window;
    }
    _combineIncrementalLayers() {
        this._stage && waitForAllSubLayers(this._stage).then((() => {
            this._stage && this._stage.defaultLayer.combineSubLayer();
        }));
    }
    findProgressiveMarks() {
        const marks = [];
        return this._rootMarks.forEach((mark => {
            traverseGroupMark(mark, (m => {
                m.isProgressive() && marks.push(m);
            }));
        })), marks.length ? (this._progressiveMarks = marks, this._combineIncrementalLayers(), 
        marks) : (this._progressiveMarks = null, null);
    }
    doPreProgressive() {
        if (this._progressiveMarks && this._progressiveMarks.some((mark => mark.isDoingProgressive()))) {
            const raf = vglobal.getSpecifiedRequestAnimationFrame(10);
            this._progressiveRafId = raf(this.handleProgressiveFrame);
        } else this._progressiveMarks && this._progressiveMarks.every((mark => mark.canAnimateAfterProgressive())) ? this._progressiveMarks.forEach((mark => {
            mark.runAnimation();
        })) : this._progressiveMarks && (this._progressiveMarks = null);
    }
    clearProgressive() {
        if (this._progressiveRafId) {
            vglobal.getSpecifiedCancelAnimationFrame(10)(this._progressiveRafId);
        }
        this._progressiveMarks && this._progressiveMarks.length && (this._progressiveMarks.forEach((entry => {
            entry.clearProgressive();
        })), this._progressiveMarks = null);
    }
}
//# sourceMappingURL=compiler.js.map