import { TABLE_EVENT_TYPE } from "@visactor/vtable";

import { captureCellPreChangeContent, parseA1Notation, popCellPreChangeContent } from "./formula";

import { replayCommand } from "./replay";

import { captureSnapshot, cloneDeep, cloneRecord } from "./snapshot";

import { resolveSheetKey } from "./sheet-key";

import { FilterActionType } from "../filter/types";

export class HistoryPlugin {
    constructor(options) {
        var _a, _b, _c;
        this.id = "history-plugin", this.name = "History", this.runTime = [ TABLE_EVENT_TYPE.BEFORE_INIT, TABLE_EVENT_TYPE.INITIALIZED, TABLE_EVENT_TYPE.BEFORE_KEYDOWN, TABLE_EVENT_TYPE.CHANGE_CELL_VALUE, TABLE_EVENT_TYPE.CHANGE_CELL_VALUES, TABLE_EVENT_TYPE.PASTED_DATA, TABLE_EVENT_TYPE.MERGE_CELLS, TABLE_EVENT_TYPE.UNMERGE_CELLS, TABLE_EVENT_TYPE.ADD_RECORD, TABLE_EVENT_TYPE.DELETE_RECORD, TABLE_EVENT_TYPE.UPDATE_RECORD, TABLE_EVENT_TYPE.ADD_COLUMN, TABLE_EVENT_TYPE.DELETE_COLUMN, TABLE_EVENT_TYPE.CHANGE_HEADER_POSITION, TABLE_EVENT_TYPE.SORT_CLICK, TABLE_EVENT_TYPE.RESIZE_ROW, TABLE_EVENT_TYPE.RESIZE_ROW_END, TABLE_EVENT_TYPE.RESIZE_COLUMN, TABLE_EVENT_TYPE.RESIZE_COLUMN_END ], 
        this.table = null, this.undoStack = [], this.redoStack = [], this.currentTransaction = null, 
        this.isReplaying = !1, this.prevColumnsSnapshot = null, this.prevRecordsSnapshot = null, 
        this.prevFormulasSnapshot = null, this.prevResizedRowHeightsSnapshot = null, this.prevResizedColWidthsSnapshot = null, 
        this.lastKnownSortState = null, this.sortStartSnapshot = null, this.sortPending = !1, 
        this.sortEventBound = !1, this.cellPreChangeContent = new Map, this.formulaCache = new Map, 
        this.formulaEventBound = !1, this.resizeRowStartHeight = new Map, this.resizeColStartWidth = new Map, 
        this.filterEventBound = !1, this.filterUnsubscribe = null, this.filterApplyingSnapshot = !1, 
        this.id = null !== (_a = null == options ? void 0 : options.id) && void 0 !== _a ? _a : this.id, 
        this.maxHistory = null !== (_b = null == options ? void 0 : options.maxHistory) && void 0 !== _b ? _b : 100, 
        this.enableCompression = null !== (_c = null == options ? void 0 : options.enableCompression) && void 0 !== _c && _c, 
        this.onTransactionPushed = null == options ? void 0 : options.onTransactionPushed;
    }
    run(...args) {
        var _a, _b, _c, _d, _e, _f;
        const eventArgs = args[0], runtime = args[1], table = args[2];
        if (!table) return;
        if (this.table) this.vtableSheet || (this.vtableSheet = table.__vtableSheet); else if (this.table = table, 
        this.vtableSheet = table.__vtableSheet, runtime !== TABLE_EVENT_TYPE.BEFORE_INIT && (null === (_a = table.internalProps) || void 0 === _a ? void 0 : _a.dataSource)) {
            const state = this.getSnapshotState(), sheetKey = this.getSheetKey();
            captureSnapshot(table, state, {
                formulaManager: null === (_b = this.vtableSheet) || void 0 === _b ? void 0 : _b.formulaManager,
                sheetKey: sheetKey
            }), this.setSnapshotState(state), this.lastKnownSortState = this.normalizeSortState(null === (_c = table.internalProps) || void 0 === _c ? void 0 : _c.sortState);
        }
        if (this.ensureFilterEventBindings(), runtime === TABLE_EVENT_TYPE.BEFORE_INIT) return;
        this.ensureFormulaEventBindings(), this.ensureSortEventBindings();
        const workbookReplaying = null === (_e = null === (_d = this.table) || void 0 === _d ? void 0 : _d.__vtableSheet) || void 0 === _e ? void 0 : _e.__workbookHistoryReplaying;
        if (!this.isReplaying && !workbookReplaying) {
            switch (runtime) {
              case TABLE_EVENT_TYPE.INITIALIZED:
                break;

              case TABLE_EVENT_TYPE.BEFORE_KEYDOWN:
                this.handleBeforeKeydown(eventArgs);
                break;

              case TABLE_EVENT_TYPE.CHANGE_CELL_VALUE:
                this.handleChangeCellValue(eventArgs);
                break;

              case TABLE_EVENT_TYPE.CHANGE_CELL_VALUES:
                this.handleChangeCellValues(eventArgs);
                break;

              case TABLE_EVENT_TYPE.PASTED_DATA:
                break;

              case TABLE_EVENT_TYPE.MERGE_CELLS:
                this.handleMergeCells(eventArgs);
                break;

              case TABLE_EVENT_TYPE.UNMERGE_CELLS:
                this.handleUnmergeCells(eventArgs);
                break;

              case TABLE_EVENT_TYPE.ADD_RECORD:
                this.handleAddRecord(eventArgs);
                break;

              case TABLE_EVENT_TYPE.DELETE_RECORD:
                this.handleDeleteRecord(eventArgs);
                break;

              case TABLE_EVENT_TYPE.UPDATE_RECORD:
                this.handleUpdateRecord(eventArgs);
                break;

              case TABLE_EVENT_TYPE.ADD_COLUMN:
                this.handleAddColumn(eventArgs);
                break;

              case TABLE_EVENT_TYPE.DELETE_COLUMN:
                this.handleDeleteColumn(eventArgs);
                break;

              case TABLE_EVENT_TYPE.CHANGE_HEADER_POSITION:
                this.handleChangeHeaderPosition(eventArgs);
                break;

              case TABLE_EVENT_TYPE.SORT_CLICK:
                this.handleSortClick(eventArgs);
                break;

              case TABLE_EVENT_TYPE.RESIZE_ROW:
                this.handleResizeRow(eventArgs);
                break;

              case TABLE_EVENT_TYPE.RESIZE_ROW_END:
                this.handleResizeRowEnd(eventArgs);
                break;

              case TABLE_EVENT_TYPE.RESIZE_COLUMN:
                this.handleResizeColumn(eventArgs);
                break;

              case TABLE_EVENT_TYPE.RESIZE_COLUMN_END:
                this.handleResizeColumnEnd(eventArgs);
            }
            if (this.table) {
                const state = this.getSnapshotState(), sheetKey = this.getSheetKey();
                captureSnapshot(this.table, state, {
                    formulaManager: null === (_f = this.vtableSheet) || void 0 === _f ? void 0 : _f.formulaManager,
                    sheetKey: sheetKey
                }), this.setSnapshotState(state);
            }
        }
    }
    startTransaction() {
        this.currentTransaction || (this.currentTransaction = {
            commands: []
        });
    }
    endTransaction() {
        this.currentTransaction && 0 !== this.currentTransaction.commands.length ? (this.pushTransaction(this.currentTransaction), 
        this.currentTransaction = null) : this.currentTransaction = null;
    }
    undo() {
        var _a;
        if (!this.table || 0 === this.undoStack.length) return;
        const transaction = this.undoStack.pop();
        this.isReplaying = !0;
        try {
            for (let i = transaction.commands.length - 1; i >= 0; i--) this.applyCommand(transaction.commands[i], "undo");
        } finally {
            this.isReplaying = !1;
        }
        if (this.redoStack.push(transaction), this.table) {
            const state = this.getSnapshotState(), sheetKey = this.getSheetKey();
            captureSnapshot(this.table, state, {
                formulaManager: null === (_a = this.vtableSheet) || void 0 === _a ? void 0 : _a.formulaManager,
                sheetKey: sheetKey
            }), this.setSnapshotState(state);
        }
    }
    redo() {
        var _a;
        if (!this.table || 0 === this.redoStack.length) return;
        const transaction = this.redoStack.pop();
        this.isReplaying = !0;
        try {
            for (const cmd of transaction.commands) this.applyCommand(cmd, "redo");
        } finally {
            this.isReplaying = !1;
        }
        if (this.undoStack.push(transaction), this.table) {
            const state = this.getSnapshotState(), sheetKey = this.getSheetKey();
            captureSnapshot(this.table, state, {
                formulaManager: null === (_a = this.vtableSheet) || void 0 === _a ? void 0 : _a.formulaManager,
                sheetKey: sheetKey
            }), this.setSnapshotState(state);
        }
    }
    clear() {
        this.undoStack = [], this.redoStack = [], this.currentTransaction = null;
    }
    updateOptions(options) {
        null != options.maxHistory && (this.maxHistory = options.maxHistory, this.trimHistory()), 
        null != options.enableCompression && (this.enableCompression = options.enableCompression), 
        options.onTransactionPushed && (this.onTransactionPushed = options.onTransactionPushed);
    }
    release() {
        this.clear(), this.unbindFormulaEvents(), this.unbindSortEvents(), this.unbindFilterEvents(), 
        this.table = null, this.vtableSheet = null, this.resolvedSheetKey = void 0, this.prevColumnsSnapshot = null, 
        this.prevMergeSnapshot = void 0, this.prevRecordsSnapshot = null, this.prevFormulasSnapshot = null, 
        this.prevResizedRowHeightsSnapshot = null, this.prevResizedColWidthsSnapshot = null, 
        this.lastKnownSortState = null, this.sortStartSnapshot = null, this.sortPending = !1, 
        this.sortEventBound = !1, this.sortChangeOrderListenerId = void 0, this.cellPreChangeContent.clear(), 
        this.formulaCache.clear(), this.resizeRowStartHeight.clear(), this.resizeColStartWidth.clear(), 
        this.filterEventBound = !1, this.filterUnsubscribe = null, this.filterSnapshotCache = void 0, 
        this.filterPluginId = void 0;
    }
    ensureFilterEventBindings() {
        var _a, _b, _c, _d, _e, _f, _g, _h, _j;
        if (!this.table) return;
        const pm = this.table.pluginManager, filterPlugin = null !== (_f = null !== (_d = null !== (_b = null === (_a = null == pm ? void 0 : pm.getPlugin) || void 0 === _a ? void 0 : _a.call(pm, "filter")) && void 0 !== _b ? _b : null === (_c = null == pm ? void 0 : pm.getPluginByName) || void 0 === _c ? void 0 : _c.call(pm, "Filter")) && void 0 !== _d ? _d : null === (_e = null == pm ? void 0 : pm.getPlugin) || void 0 === _e ? void 0 : _e.call(pm, "filter-plugin")) && void 0 !== _f ? _f : null === (_g = null == pm ? void 0 : pm.getPluginByName) || void 0 === _g ? void 0 : _g.call(pm, "filter-plugin");
        if (!(null == filterPlugin ? void 0 : filterPlugin.getFilterSnapshot) || !(null == filterPlugin ? void 0 : filterPlugin.applyFilterSnapshot)) return;
        this.filterEventBound || (this.filterEventBound = !0, this.filterPluginRef = filterPlugin, 
        this.filterPluginId = null !== (_h = filterPlugin.id) && void 0 !== _h ? _h : "filter", 
        this.filterSnapshotCache = cloneDeep(filterPlugin.getFilterSnapshot())), this.filterApplySnapshotOriginal || (this.filterApplySnapshotOriginal = null === (_j = filterPlugin.applyFilterSnapshot) || void 0 === _j ? void 0 : _j.bind(filterPlugin), 
        this.filterApplySnapshotOriginal && (filterPlugin.applyFilterSnapshot = snapshot => {
            var _a, _b;
            if (this.isReplaying) return this.filterApplySnapshotOriginal(snapshot);
            const oldSnapshot = null !== (_a = this.filterSnapshotCache) && void 0 !== _a ? _a : filterPlugin.getFilterSnapshot();
            this.filterApplyingSnapshot = !0;
            try {
                return this.filterApplySnapshotOriginal(snapshot);
            } finally {
                this.filterApplyingSnapshot = !1;
                const newSnapshot = filterPlugin.getFilterSnapshot();
                if (JSON.stringify(oldSnapshot) !== JSON.stringify(newSnapshot)) {
                    const cmd = {
                        type: "filter",
                        sheetKey: this.getSheetKey(),
                        pluginId: null !== (_b = this.filterPluginId) && void 0 !== _b ? _b : "filter",
                        oldSnapshot: oldSnapshot,
                        newSnapshot: newSnapshot
                    };
                    this.pushCommand(cmd), this.filterSnapshotCache = cloneDeep(newSnapshot);
                } else this.filterSnapshotCache = cloneDeep(newSnapshot);
            }
        }));
        const stateManager = null == filterPlugin ? void 0 : filterPlugin.filterStateManager;
        !this.filterUnsubscribe && (null == stateManager ? void 0 : stateManager.subscribe) && (this.filterUnsubscribe = stateManager.subscribe(((_state, action) => {
            var _a, _b;
            if (this.isReplaying) return;
            if (this.filterApplyingSnapshot) return void (this.filterSnapshotCache = cloneDeep(filterPlugin.getFilterSnapshot()));
            const t = null == action ? void 0 : action.type;
            if (t !== FilterActionType.APPLY_FILTERS && t !== FilterActionType.CLEAR_ALL_FILTERS && t !== FilterActionType.REMOVE_FILTER && t !== FilterActionType.DISABLE_FILTER) return;
            const oldSnapshot = null !== (_a = this.filterSnapshotCache) && void 0 !== _a ? _a : filterPlugin.getFilterSnapshot(), newSnapshot = filterPlugin.getFilterSnapshot();
            if (JSON.stringify(oldSnapshot) === JSON.stringify(newSnapshot)) return;
            const cmd = {
                type: "filter",
                sheetKey: this.getSheetKey(),
                pluginId: null !== (_b = this.filterPluginId) && void 0 !== _b ? _b : "filter",
                oldSnapshot: oldSnapshot,
                newSnapshot: newSnapshot
            };
            this.pushCommand(cmd), this.filterSnapshotCache = cloneDeep(newSnapshot);
        })));
    }
    unbindFilterEvents() {
        var _a;
        null === (_a = this.filterUnsubscribe) || void 0 === _a || _a.call(this), this.filterUnsubscribe = null, 
        this.filterEventBound = !1, this.filterPluginRef && this.filterApplySnapshotOriginal && (this.filterPluginRef.applyFilterSnapshot = this.filterApplySnapshotOriginal), 
        this.filterPluginRef = null, this.filterApplySnapshotOriginal = null, this.filterApplyingSnapshot = !1;
    }
    getSheetKey() {
        const resolved = resolveSheetKey({
            vtableSheet: this.vtableSheet,
            table: this.table,
            cached: this.resolvedSheetKey
        });
        return this.resolvedSheetKey = resolved.cached, resolved.sheetKey;
    }
    getSnapshotState() {
        return {
            prevColumnsSnapshot: this.prevColumnsSnapshot,
            prevMergeSnapshot: this.prevMergeSnapshot,
            prevRecordsSnapshot: this.prevRecordsSnapshot,
            prevFormulasSnapshot: this.prevFormulasSnapshot,
            prevResizedRowHeightsSnapshot: this.prevResizedRowHeightsSnapshot,
            prevResizedColWidthsSnapshot: this.prevResizedColWidthsSnapshot
        };
    }
    setSnapshotState(next) {
        var _a, _b, _c;
        this.prevColumnsSnapshot = next.prevColumnsSnapshot, this.prevMergeSnapshot = next.prevMergeSnapshot, 
        this.prevRecordsSnapshot = next.prevRecordsSnapshot, this.prevFormulasSnapshot = null !== (_a = next.prevFormulasSnapshot) && void 0 !== _a ? _a : null, 
        this.prevResizedRowHeightsSnapshot = null !== (_b = next.prevResizedRowHeightsSnapshot) && void 0 !== _b ? _b : null, 
        this.prevResizedColWidthsSnapshot = null !== (_c = next.prevResizedColWidthsSnapshot) && void 0 !== _c ? _c : null;
    }
    pushTransaction(tx) {
        var _a, _b, _c;
        if (0 !== tx.commands.length && (this.undoStack.push(tx), this.redoStack = [], this.trimHistory(), 
        this.onTransactionPushed && !this.isReplaying)) {
            (null === (_b = null === (_a = this.table) || void 0 === _a ? void 0 : _a.__vtableSheet) || void 0 === _b ? void 0 : _b.__workbookHistoryReplaying) || this.onTransactionPushed({
                tx: tx,
                sheetKey: this.getSheetKey(),
                table: null !== (_c = this.table) && void 0 !== _c ? _c : void 0
            });
        }
    }
    trimHistory() {
        this.undoStack.length > this.maxHistory && this.undoStack.splice(0, this.undoStack.length - this.maxHistory);
    }
    pushCommand(cmd) {
        this.currentTransaction ? this.currentTransaction.commands.push(cmd) : this.pushTransaction({
            commands: [ cmd ]
        });
    }
    recordExternalCommand(cmd) {
        this.isReplaying || this.pushCommand(cmd);
    }
    tryCompressCellChange(change, sheetKey) {
        if (!this.enableCompression) return !1;
        const targetTx = this.currentTransaction || this.undoStack[this.undoStack.length - 1];
        if (!targetTx || 0 === targetTx.commands.length) return !1;
        const last = targetTx.commands[targetTx.commands.length - 1];
        if (("cell" === last.type || "cells" === last.type) && last.sheetKey === sheetKey) {
            const cells = last.cells;
            if (1 === cells.length && cells[0].row === change.row && cells[0].col === change.col) return cells[0].newContent = change.newContent, 
            !0;
        }
        return !1;
    }
    handleBeforeKeydown(eventArgs) {
        var _a, _b, _c, _d, _e, _f;
        const e = eventArgs.event;
        if (!e || !this.table) return;
        if (!(e.ctrlKey || e.metaKey)) return;
        const formulaManager = null === (_a = this.vtableSheet) || void 0 === _a ? void 0 : _a.formulaManager;
        if (null == formulaManager ? void 0 : formulaManager.formulaWorkingOnCell) return;
        if (null === (_b = this.table.editorManager) || void 0 === _b ? void 0 : _b.editingEditor) return;
        const key = e.key.toLowerCase();
        if ("z" === key) {
            const workbookHistory = null === (_d = null === (_c = this.vtableSheet) || void 0 === _c ? void 0 : _c.getWorkbookHistoryManager) || void 0 === _d ? void 0 : _d.call(_c);
            e.shiftKey ? workbookHistory ? workbookHistory.redo() : this.redo() : workbookHistory ? workbookHistory.undo() : this.undo(), 
            e.preventDefault(), e.stopPropagation();
        } else if ("y" === key) {
            const workbookHistory = null === (_f = null === (_e = this.vtableSheet) || void 0 === _e ? void 0 : _e.getWorkbookHistoryManager) || void 0 === _f ? void 0 : _f.call(_e);
            workbookHistory ? workbookHistory.redo() : this.redo(), e.preventDefault(), e.stopPropagation();
        }
    }
    handleChangeCellValue(eventArgs) {
        var _a;
        captureCellPreChangeContent({
            sheetKey: this.getSheetKey(),
            row: eventArgs.row,
            col: eventArgs.col,
            currentValue: eventArgs.currentValue,
            formulaManager: null === (_a = this.vtableSheet) || void 0 === _a ? void 0 : _a.formulaManager,
            store: this.cellPreChangeContent
        });
    }
    handleChangeCellValues(eventArgs) {
        var _a;
        if (!this.table) return;
        const sheetKey = this.getSheetKey(), formulaManager = null === (_a = this.vtableSheet) || void 0 === _a ? void 0 : _a.formulaManager, cells = eventArgs.values.map((v => {
            const oldContent = popCellPreChangeContent({
                sheetKey: sheetKey,
                row: v.row,
                col: v.col,
                fallbackOldContent: v.currentValue,
                store: this.cellPreChangeContent
            }), normalizedOld = null == oldContent ? "" : oldContent, normalizedNew = null == v.changedValue ? "" : v.changedValue;
            return {
                row: v.row,
                col: v.col,
                oldContent: normalizedOld,
                newContent: normalizedNew
            };
        })).filter((c => {
            if (c.oldContent === c.newContent) return !1;
            if (!sheetKey || !(null == formulaManager ? void 0 : formulaManager.getCellFormula) || !(null == formulaManager ? void 0 : formulaManager.getCellValue)) return !0;
            if ("string" == typeof c.newContent && c.newContent.startsWith("=")) return !1;
            if (!formulaManager.getCellFormula({
                sheet: sheetKey,
                row: c.row,
                col: c.col
            })) return !0;
            const result = formulaManager.getCellValue({
                sheet: sheetKey,
                row: c.row,
                col: c.col
            }), display = (null == result ? void 0 : result.error) ? "#ERROR!" : null == result ? void 0 : result.value;
            return display !== c.newContent && (null == display || null == c.newContent || String(display) !== String(c.newContent));
        }));
        if (!cells.length) return;
        if (sheetKey && 1 === cells.length && this.tryCompressCellChange(cells[0], sheetKey)) return;
        const cmd = {
            type: "cells",
            sheetKey: sheetKey,
            cells: cells
        };
        this.pushCommand(cmd);
    }
    ensureFormulaEventBindings() {
        var _a, _b, _c, _d, _e;
        if (!this.table || !this.vtableSheet || this.formulaEventBound) return;
        const sheetKey = this.getSheetKey();
        if (!sheetKey) return;
        const worksheet = null !== (_c = null === (_b = (_a = this.vtableSheet).getWorkSheetByKey) || void 0 === _b ? void 0 : _b.call(_a, sheetKey)) && void 0 !== _c ? _c : null === (_e = null === (_d = this.vtableSheet.workSheetInstances) || void 0 === _d ? void 0 : _d.get) || void 0 === _e ? void 0 : _e.call(_d, sheetKey), eventManager = null == worksheet ? void 0 : worksheet.eventManager;
        (null == eventManager ? void 0 : eventManager.on) && (null == eventManager ? void 0 : eventManager.off) && (this.formulaEventBound = !0, 
        this.formulaEventManager = eventManager, this.onFormulaAddedHandler = event => {
            if (this.isReplaying) return;
            const cell = null == event ? void 0 : event.cell, formula = null == event ? void 0 : event.formula;
            if (!cell || "number" != typeof cell.row || "number" != typeof cell.col) return;
            if ("string" != typeof formula || !formula.length) return;
            const normalized = formula.startsWith("=") ? formula : `=${formula}`, key = `${sheetKey}:${cell.row}:${cell.col}`, prev = this.formulaCache.get(key), oldContent = popCellPreChangeContent({
                sheetKey: sheetKey,
                row: cell.row,
                col: cell.col,
                fallbackOldContent: void 0 !== prev ? prev : "",
                store: this.cellPreChangeContent
            });
            if (oldContent === normalized) return void this.formulaCache.set(key, normalized);
            const cmd = {
                type: "cells",
                sheetKey: sheetKey,
                cells: [ {
                    row: cell.row,
                    col: cell.col,
                    oldContent: oldContent,
                    newContent: normalized
                } ]
            };
            this.pushCommand(cmd), this.formulaCache.set(key, normalized);
        }, eventManager.on("formula_added", this.onFormulaAddedHandler));
    }
    ensureSortEventBindings() {
        var _a;
        if (!this.table || this.sortEventBound) return;
        const ds = null === (_a = this.table.internalProps) || void 0 === _a ? void 0 : _a.dataSource;
        (null == ds ? void 0 : ds.on) && (null == ds ? void 0 : ds.off) && (this.sortEventBound = !0, 
        this.sortChangeOrderListenerId = ds.on("change_order", (() => {
            var _a;
            if (this.isReplaying || !this.table || !this.sortPending) return;
            const sheetKey = this.getSheetKey(), oldSortState = this.sortStartSnapshot;
            this.sortStartSnapshot = null, this.sortPending = !1;
            const newSortState = this.normalizeSortState(null === (_a = this.table.internalProps) || void 0 === _a ? void 0 : _a.sortState);
            if (JSON.stringify(oldSortState) === JSON.stringify(newSortState)) return void (this.lastKnownSortState = newSortState);
            const cmd = {
                type: "sort",
                sheetKey: sheetKey,
                oldSortState: oldSortState,
                newSortState: newSortState
            };
            this.pushCommand(cmd), this.lastKnownSortState = newSortState;
        })));
    }
    unbindSortEvents() {
        var _a, _b;
        if (!this.sortEventBound) return;
        const ds = null === (_b = null === (_a = this.table) || void 0 === _a ? void 0 : _a.internalProps) || void 0 === _b ? void 0 : _b.dataSource;
        try {
            (null == ds ? void 0 : ds.off) && this.sortChangeOrderListenerId && ds.off(this.sortChangeOrderListenerId);
        } catch (_c) {} finally {
            this.sortEventBound = !1, this.sortChangeOrderListenerId = void 0, this.sortPending = !1, 
            this.sortStartSnapshot = null;
        }
    }
    unbindFormulaEvents() {
        if (this.formulaEventBound && this.formulaEventManager) try {
            this.onFormulaAddedHandler && this.formulaEventManager.off("formula_added", this.onFormulaAddedHandler);
        } catch (_a) {} finally {
            this.formulaEventBound = !1, this.formulaEventManager = void 0, this.onFormulaAddedHandler = void 0;
        }
    }
    cloneMergeConfig(input) {
        return Array.isArray(input) ? input.map((i => cloneDeep(i))) : input;
    }
    isSameMergeConfig(a, b) {
        var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m;
        const normalize = v => null == v ? [] : (Array.isArray(v), v), aa = normalize(a), bb = normalize(b);
        if (aa === bb) return !0;
        if (Array.isArray(aa) && Array.isArray(bb)) {
            if (aa.length !== bb.length) return !1;
            for (let i = 0; i < aa.length; i++) {
                const ra = null === (_a = aa[i]) || void 0 === _a ? void 0 : _a.range, rb = null === (_b = bb[i]) || void 0 === _b ? void 0 : _b.range;
                if (!ra || !rb || (null === (_c = ra.start) || void 0 === _c ? void 0 : _c.col) !== (null === (_d = rb.start) || void 0 === _d ? void 0 : _d.col) || (null === (_e = ra.start) || void 0 === _e ? void 0 : _e.row) !== (null === (_f = rb.start) || void 0 === _f ? void 0 : _f.row) || (null === (_g = ra.end) || void 0 === _g ? void 0 : _g.col) !== (null === (_h = rb.end) || void 0 === _h ? void 0 : _h.col) || (null === (_j = ra.end) || void 0 === _j ? void 0 : _j.row) !== (null === (_k = rb.end) || void 0 === _k ? void 0 : _k.row) || (null === (_l = aa[i]) || void 0 === _l ? void 0 : _l.text) !== (null === (_m = bb[i]) || void 0 === _m ? void 0 : _m.text)) return !1;
            }
            return !0;
        }
        return !1;
    }
    handleMergeCells(eventArgs) {
        this.handleMergeConfigChanged(eventArgs);
    }
    handleUnmergeCells(eventArgs) {
        this.handleMergeConfigChanged(eventArgs);
    }
    handleMergeConfigChanged(eventArgs) {
        var _a;
        if (!this.table) return;
        const {startCol: startCol, startRow: startRow, endCol: endCol, endRow: endRow} = eventArgs, before = this.cloneMergeConfig(this.prevMergeSnapshot), after = this.cloneMergeConfig(null === (_a = this.table.options) || void 0 === _a ? void 0 : _a.customMergeCell);
        if (this.isSameMergeConfig(before, after)) return;
        const cmd = {
            type: "merge_cells",
            sheetKey: this.getSheetKey(),
            startCol: startCol,
            startRow: startRow,
            endCol: endCol,
            endRow: endRow,
            oldCustomMergeCell: before,
            newCustomMergeCell: after
        };
        this.pushCommand(cmd);
    }
    handleAddRecord(eventArgs) {
        var _a, _b, _c, _d;
        const cmd = {
            type: "add_record",
            sheetKey: this.getSheetKey(),
            records: Array.isArray(eventArgs.records) ? eventArgs.records.slice() : [],
            recordIndex: eventArgs.recordIndex,
            recordCount: null !== (_a = eventArgs.recordCount) && void 0 !== _a ? _a : Array.isArray(eventArgs.records) ? eventArgs.records.length : 0
        };
        if (this.table && "number" == typeof eventArgs.recordIndex) {
            const start = eventArgs.recordIndex, count = cmd.recordCount, ds = null === (_c = null === (_b = this.table) || void 0 === _b ? void 0 : _b.internalProps) || void 0 === _c ? void 0 : _c.dataSource, rawRecords = null === (_d = null == ds ? void 0 : ds.dataSourceObj) || void 0 === _d ? void 0 : _d.records, viewRecords = this.table.records;
            if (Array.isArray(viewRecords) && viewRecords.length && (cmd.anchorBefore = start > 0 ? viewRecords[start - 1] : void 0, 
            cmd.anchorAfter = viewRecords[start + count]), Array.isArray(rawRecords) && Array.isArray(viewRecords)) {
                let rawInsertIndex = rawRecords.length;
                if (0 === viewRecords.length) rawInsertIndex = rawRecords.length; else if (start <= 0) {
                    const first = viewRecords[0], idx = rawRecords.indexOf(first);
                    rawInsertIndex = idx >= 0 ? idx : 0;
                } else if (start >= viewRecords.length) {
                    const last = viewRecords[viewRecords.length - 1], idx = rawRecords.indexOf(last);
                    rawInsertIndex = idx >= 0 ? idx + 1 : rawRecords.length;
                } else {
                    const prev = viewRecords[start - 1], idx = rawRecords.indexOf(prev);
                    rawInsertIndex = idx >= 0 ? idx + 1 : rawRecords.length;
                }
                cmd.rawInsertIndex = rawInsertIndex;
            }
        }
        this.pushCommand(cmd);
    }
    handleDeleteRecord(eventArgs) {
        var _a, _b;
        const sheetKey = this.getSheetKey(), recordIndexs = Array.isArray(eventArgs.recordIndexs) ? eventArgs.recordIndexs.slice() : [ eventArgs.recordIndexs ];
        let deletedRowHeights;
        if (this.table && this.prevResizedRowHeightsSnapshot) {
            const headerCount = this.table.transpose ? this.table.rowHeaderLevelCount : this.table.columnHeaderLevelCount;
            recordIndexs.forEach((idx => {
                var _a;
                if ("number" != typeof idx) return;
                const rowIndex = idx + (null != headerCount ? headerCount : 0), height = null === (_a = this.prevResizedRowHeightsSnapshot) || void 0 === _a ? void 0 : _a[rowIndex];
                "number" == typeof height && (deletedRowHeights || (deletedRowHeights = {}), deletedRowHeights[idx] = height);
            }));
        }
        const oldCustomMergeCell = this.cloneMergeConfig(this.prevMergeSnapshot), newCustomMergeCell = this.cloneMergeConfig(null === (_b = null === (_a = this.table) || void 0 === _a ? void 0 : _a.options) || void 0 === _b ? void 0 : _b.customMergeCell), cmd = {
            type: "delete_record",
            sheetKey: sheetKey,
            records: Array.isArray(eventArgs.records) ? eventArgs.records.slice() : [],
            recordIndexs: recordIndexs,
            deletedRowHeights: deletedRowHeights,
            oldCustomMergeCell: oldCustomMergeCell,
            newCustomMergeCell: newCustomMergeCell
        };
        this.pushCommand(cmd);
    }
    handleUpdateRecord(eventArgs) {
        if (!this.table) return;
        const sheetKey = this.getSheetKey(), recordIndexs = eventArgs.recordIndexs || [], newRecords = Array.isArray(eventArgs.records) ? eventArgs.records.slice() : [];
        if (!this.prevRecordsSnapshot || !Array.isArray(this.prevRecordsSnapshot) || !recordIndexs.length) return;
        const oldRecords = [];
        recordIndexs.forEach((idx => {
            if ("number" == typeof idx) {
                const rec = this.prevRecordsSnapshot[idx];
                oldRecords.push(void 0 !== rec ? cloneRecord(rec) : void 0);
            } else oldRecords.push(void 0);
        }));
        const cmd = {
            type: "update_record",
            sheetKey: sheetKey,
            oldRecords: oldRecords,
            newRecords: newRecords,
            recordIndexs: recordIndexs
        };
        this.pushCommand(cmd);
    }
    handleAddColumn(eventArgs) {
        if (!this.table) return;
        const sheetKey = this.getSheetKey(), {columnIndex: columnIndex, columnCount: columnCount, columns: columns} = eventArgs, cmd = {
            type: "add_column",
            sheetKey: sheetKey,
            columnIndex: columnIndex,
            columnCount: columnCount,
            columns: columns.slice(columnIndex, columnIndex + columnCount).map((c => cloneDeep(c)))
        };
        this.pushCommand(cmd);
    }
    handleDeleteColumn(eventArgs) {
        if (!this.table) return;
        const sheetKey = this.getSheetKey(), deleteColIndexs = (eventArgs.deleteColIndexs || []).slice(), deletedColumnsFromEvent = eventArgs.deletedColumns, prevColumns = this.prevColumnsSnapshot || [], deletedColumns = Array.isArray(deletedColumnsFromEvent) ? deletedColumnsFromEvent.map((c => cloneDeep(c))) : [];
        deletedColumns.length || deleteColIndexs.forEach((idx => {
            idx >= 0 && idx < prevColumns.length && deletedColumns.push(cloneDeep(prevColumns[idx]));
        }));
        let deletedFormulas, deletedColWidths, deletedRecordValues = eventArgs.deletedRecordValues;
        if (Array.isArray(deletedRecordValues) && deletedRecordValues.length || (deletedRecordValues = void 0, 
        Array.isArray(this.prevRecordsSnapshot) && this.prevRecordsSnapshot.length && (deletedRecordValues = this.prevRecordsSnapshot.map((rec => Array.isArray(rec) ? deleteColIndexs.map((idx => rec[idx])) : [])), 
        deletedRecordValues.every((v => 0 === v.length)) && (deletedRecordValues = void 0))), 
        this.prevFormulasSnapshot && deleteColIndexs.length) {
            Object.entries(this.prevFormulasSnapshot).forEach((([cellRef, formula]) => {
                const parsed = parseA1Notation(cellRef);
                parsed && deleteColIndexs.includes(parsed.col) && (deletedFormulas || (deletedFormulas = {}), 
                deletedFormulas[cellRef] = formula);
            }));
        }
        this.prevResizedColWidthsSnapshot && deleteColIndexs.length && deleteColIndexs.forEach((idx => {
            var _a;
            const w = null === (_a = this.prevResizedColWidthsSnapshot) || void 0 === _a ? void 0 : _a[idx];
            "number" == typeof w && (deletedColWidths || (deletedColWidths = {}), deletedColWidths[idx] = w);
        }));
        const cmd = {
            type: "delete_column",
            sheetKey: sheetKey,
            deleteColIndexs: deleteColIndexs,
            columns: deletedColumns,
            deletedRecordValues: deletedRecordValues,
            deletedFormulas: deletedFormulas,
            deletedColWidths: deletedColWidths
        };
        this.pushCommand(cmd);
    }
    handleChangeHeaderPosition(eventArgs) {
        var _a;
        const sheetKey = this.getSheetKey(), moving = null !== (_a = eventArgs.movingColumnOrRow) && void 0 !== _a ? _a : "column", cmd = {
            type: "change_header_position",
            sheetKey: sheetKey,
            moving: moving,
            sourceIndex: "column" === moving ? eventArgs.source.col : eventArgs.source.row,
            targetIndex: "column" === moving ? eventArgs.target.col : eventArgs.target.row
        };
        this.pushCommand(cmd);
    }
    normalizeSortState(input) {
        if (!input) return null;
        if (Array.isArray(input)) {
            const list = input.filter(Boolean).map((s => ({
                field: s.field,
                order: s.order
            }))).filter((s => void 0 !== s.field && s.order && "normal" !== s.order));
            return list.length ? list : null;
        }
        const s = input;
        return void 0 !== s.field && s.order && "normal" !== s.order ? {
            field: s.field,
            order: s.order
        } : null;
    }
    handleSortClick(_eventArgs) {
        this.table && (this.sortStartSnapshot = this.lastKnownSortState, this.sortPending = !0, 
        setTimeout((() => {
            this.sortPending && (this.sortPending = !1, this.sortStartSnapshot = null);
        }), 0));
    }
    handleResizeRow(eventArgs) {
        var _a, _b;
        if (!this.table) return;
        const row = eventArgs.row;
        if (!this.resizeRowStartHeight.has(row)) {
            const oldHeight = null === (_b = (_a = this.table).getRowHeight) || void 0 === _b ? void 0 : _b.call(_a, row);
            "number" == typeof oldHeight && this.resizeRowStartHeight.set(row, oldHeight);
        }
    }
    handleResizeRowEnd(eventArgs) {
        var _a;
        if (!this.table) return;
        const sheetKey = this.getSheetKey(), row = eventArgs.row, newHeight = eventArgs.rowHeight, oldHeight = null !== (_a = this.resizeRowStartHeight.get(row)) && void 0 !== _a ? _a : newHeight;
        if (this.resizeRowStartHeight.delete(row), "number" == typeof oldHeight && "number" == typeof newHeight && oldHeight === newHeight) return;
        const cmd = {
            type: "resize_row",
            sheetKey: sheetKey,
            row: row,
            oldHeight: oldHeight,
            newHeight: newHeight
        };
        this.pushCommand(cmd);
    }
    handleResizeColumn(eventArgs) {
        var _a, _b;
        if (!this.table) return;
        const col = eventArgs.col;
        if (!this.resizeColStartWidth.has(col)) {
            const oldWidth = null === (_b = (_a = this.table).getColWidth) || void 0 === _b ? void 0 : _b.call(_a, col);
            "number" == typeof oldWidth && this.resizeColStartWidth.set(col, oldWidth);
        }
    }
    handleResizeColumnEnd(eventArgs) {
        var _a, _b, _c, _d;
        if (!this.table) return;
        const sheetKey = this.getSheetKey(), col = eventArgs.col, newWidth = null !== (_a = (eventArgs.colWidths || [])[col]) && void 0 !== _a ? _a : null === (_c = (_b = this.table).getColWidth) || void 0 === _c ? void 0 : _c.call(_b, col), oldWidth = null !== (_d = this.resizeColStartWidth.get(col)) && void 0 !== _d ? _d : newWidth;
        if (this.resizeColStartWidth.delete(col), "number" == typeof oldWidth && "number" == typeof newWidth && oldWidth === newWidth) return;
        const cmd = {
            type: "resize_column",
            sheetKey: sheetKey,
            col: col,
            oldWidth: oldWidth,
            newWidth: newWidth
        };
        this.pushCommand(cmd);
    }
    applyCommand(cmd, direction) {
        this.table && replayCommand({
            table: this.table,
            vtableSheet: this.vtableSheet,
            cmd: cmd,
            direction: direction,
            deleteRecordsByReference: records => this.deleteRecordsByReference(records)
        });
    }
    deleteRecordsByReference(records) {
        if (!this.table || !(null == records ? void 0 : records.length)) return;
        const tableRecords = this.table.records || [], indexs = [];
        records.forEach((rec => {
            const idx = tableRecords.indexOf(rec);
            idx >= 0 && indexs.push(idx);
        })), indexs.length && this.table.deleteRecords && this.table.deleteRecords(indexs);
    }
}
//# sourceMappingURL=history-plugin.js.map