import * as VTable from "@visactor/vtable";

import { includesRecordIndex, findRecordIndexPosition } from "./types";

import { getInternalProps, getRecordByRowIndex, getOriginalRowHeight } from "./utils";

import { ConfigManager } from "./config";

import { EventManager } from "./events";

import { SubTableManager } from "./subtable";

import { TableAPIExtensions } from "./table-api-extensions";

import { bindMasterDetailCheckboxChange } from "./checkbox";

export class MasterDetailPlugin {
    constructor(pluginOptions = {}) {
        this.id = "Master Detail Plugin", this.name = "Master Detail Plugin", this.runTime = [ VTable.TABLE_EVENT_TYPE.BEFORE_INIT, VTable.TABLE_EVENT_TYPE.INITIALIZED, VTable.TABLE_EVENT_TYPE.SORT_CLICK, VTable.TABLE_EVENT_TYPE.AFTER_SORT, VTable.TABLE_EVENT_TYPE.AFTER_UPDATE_CELL_CONTENT_WIDTH, VTable.TABLE_EVENT_TYPE.AFTER_UPDATE_SELECT_BORDER_HEIGHT ], 
        this.pluginOptions = pluginOptions;
    }
    run(...args) {
        const eventArgs = args[0], runTime = args[1], table = args[2];
        if (runTime === VTable.TABLE_EVENT_TYPE.BEFORE_INIT) this.table = table, this.initializeManagers(), 
        this.configManager.injectMasterDetailOptions(eventArgs.options); else if (runTime === VTable.TABLE_EVENT_TYPE.INITIALIZED) this.setupMasterDetailFeatures(); else {
            if (runTime === VTable.TABLE_EVENT_TYPE.SORT_CLICK) return this.eventManager.executeMasterDetailBeforeSort(), 
            !0;
            runTime === VTable.TABLE_EVENT_TYPE.AFTER_SORT ? this.eventManager.executeMasterDetailAfterSort() : runTime === VTable.TABLE_EVENT_TYPE.AFTER_UPDATE_CELL_CONTENT_WIDTH ? this.eventManager.handleAfterUpdateCellContentWidth(eventArgs) : runTime === VTable.TABLE_EVENT_TYPE.AFTER_UPDATE_SELECT_BORDER_HEIGHT && this.eventManager.handleAfterUpdateSelectBorderHeight(eventArgs);
        }
    }
    initializeManagers() {
        var _a;
        this.configManager = new ConfigManager(this.pluginOptions, this.table), this.eventManager = new EventManager(this.table);
        const enableCheckboxCascade = null === (_a = this.pluginOptions.enableCheckboxCascade) || void 0 === _a || _a;
        this.subTableManager = new SubTableManager(this.table, enableCheckboxCascade), this.configManager.setRowExpandedChecker((row => this.eventManager.isRowExpanded(row))), 
        this.eventManager.setCallbacks({
            onUpdateSubTablePositions: () => this.subTableManager.recalculateAllSubTablePositions(),
            onUpdateSubTablePositionsForRow: () => this.subTableManager.updateSubTablePositionsForRowResize(),
            onExpandRow: (rowIndex, colIndex) => this.expandRow(rowIndex, colIndex),
            onCollapseRow: (rowIndex, colIndex) => this.collapseRow(rowIndex, colIndex),
            onCollapseRowToNoRealRecordIndex: rowIndex => this.collapseRowToNoRealRecordIndex(rowIndex),
            onToggleRowExpand: (rowIndex, colIndex) => this.toggleRowExpand(rowIndex, colIndex),
            getOriginalRowHeight: bodyRowIndex => getOriginalRowHeight(this.table, bodyRowIndex)
        }), this.subTableManager.setCallbacks({
            getDetailConfigForRecord: (record, bodyRowIndex) => this.configManager.getDetailConfigForRecord(record, bodyRowIndex)
        });
    }
    setupMasterDetailFeatures() {
        this.initInternalProps(), this.eventManager.bindEventHandlers(), this.setupUnifiedSelectionManagement(), 
        this.extendTableAPI(), this.initCheckboxCascade();
    }
    setupUnifiedSelectionManagement() {
        this.mainTableClickHandler = () => {
            this.clearAllSubTableVisibleSelections();
        }, this.table.on("click_cell", this.mainTableClickHandler);
    }
    initCheckboxCascade() {
        var _a;
        (null === (_a = this.pluginOptions.enableCheckboxCascade) || void 0 === _a || _a) && (this.checkboxCascadeCleanup = bindMasterDetailCheckboxChange(this.table, this.eventManager));
    }
    initInternalProps() {
        const internalProps = getInternalProps(this.table);
        internalProps.expandedRecordIndices = [], internalProps.subTableInstances = new Map, 
        internalProps.originalRowHeights = new Map, internalProps.subTableCheckboxStates = new Map;
    }
    extendTableAPI() {
        this.tableAPIExtensions = new TableAPIExtensions(this.table, this.configManager, this.eventManager, {
            addUnderlineToCell: cellGroup => this.addUnderlineToCell(cellGroup),
            updateOriginalHeightsAfterAdaptive: expandedRowsInfo => this.updateOriginalHeightsAfterAdaptive(expandedRowsInfo),
            collapseRowToNoRealRecordIndex: rowIndex => this.collapseRowToNoRealRecordIndex(rowIndex),
            expandRow: rowIndex => this.expandRow(rowIndex),
            restoreExpandedStatesAfter: () => this.restoreExpandedStatesAfter(),
            collapseRow: rowIndex => this.collapseRow(rowIndex),
            updateSubTablePositions: () => this.subTableManager.recalculateAllSubTablePositions(),
            updateRowHeightForExpand: (rowIndex, deltaHeight) => this.updateRowHeightForExpand(rowIndex, deltaHeight)
        }), this.tableAPIExtensions.extendTableAPI();
    }
    updateOriginalHeightsAfterAdaptive(expandedRowsInfo) {
        const internalProps = getInternalProps(this.table);
        for (const [rowIndex, info] of expandedRowsInfo) {
            const bodyRowIndex = rowIndex - this.table.columnHeaderLevelCount;
            internalProps.originalRowHeights && internalProps.originalRowHeights.set(bodyRowIndex, info.baseHeight);
        }
    }
    restoreExpandedStatesAfter() {
        const internalProps = getInternalProps(this.table);
        if (!internalProps.expandedRecordIndices || 0 === internalProps.expandedRecordIndices.length) return;
        this.table.dataSource._currentPagerIndexedData && internalProps.expandedRecordIndices.forEach((recordIndex => {
            try {
                const bodyRowIndex = this.table.getBodyRowIndexByRecordIndex(recordIndex);
                if (bodyRowIndex >= 0) {
                    const targetRowIndex = bodyRowIndex + this.table.columnHeaderLevelCount;
                    this.expandRow(targetRowIndex);
                }
            } catch (error) {}
        }));
    }
    expandRow(rowIndex, colIndex) {
        var _a;
        const bodyRowIndex = rowIndex - this.table.columnHeaderLevelCount, internalProps = getInternalProps(this.table);
        if (this.eventManager.isRowExpanded(rowIndex)) return;
        const realRecordIndex = this.table.getRecordIndexByCell(0, rowIndex);
        if (null == realRecordIndex) return;
        internalProps.expandedRecordIndices && (includesRecordIndex(internalProps.expandedRecordIndices, realRecordIndex) || internalProps.expandedRecordIndices.push(realRecordIndex)), 
        this.eventManager.addExpandedRow(rowIndex);
        const originalHeight = this.table.getRowHeight(rowIndex);
        internalProps.originalRowHeights && internalProps.originalRowHeights.set(bodyRowIndex, originalHeight);
        const record = getRecordByRowIndex(this.table, bodyRowIndex), detailConfig = this.configManager.getDetailConfigForRecord(record, bodyRowIndex), height = (null === (_a = null == detailConfig ? void 0 : detailConfig.style) || void 0 === _a ? void 0 : _a.height) || 300, childrenData = Array.isArray(record.children) ? record.children : [], deltaHeight = height;
        this.updateRowHeightForExpand(rowIndex, deltaHeight), this.table.scenegraph.updateContainerHeight(rowIndex, deltaHeight), 
        internalProps._heightResizedRowMap.add(rowIndex), this.subTableManager.renderSubTable(bodyRowIndex, childrenData, ((record, bodyRowIndex) => this.configManager.getDetailConfigForRecord(record, bodyRowIndex))), 
        this.subTableManager.recalculateAllSubTablePositions(bodyRowIndex + 1, void 0, ((record, bodyRowIndex) => this.configManager.getDetailConfigForRecord(record, bodyRowIndex))), 
        rowIndex !== this.table.rowCount - 1 && this.drawUnderlineForRow(rowIndex), this.refreshRowIcon(rowIndex, colIndex), 
        "adaptive" === this.table.heightMode && this.table.scenegraph.dealHeightMode();
    }
    collapseRow(rowIndex, colIndex) {
        const bodyRowIndex = rowIndex - this.table.columnHeaderLevelCount, internalProps = getInternalProps(this.table);
        if (!this.eventManager.isRowExpanded(rowIndex)) return;
        const realRecordIndex = this.table.getRecordIndexByCell(0, rowIndex);
        if (null == realRecordIndex) return;
        if (this.subTableManager.removeSubTable(bodyRowIndex), internalProps.expandedRecordIndices) {
            const index = findRecordIndexPosition(internalProps.expandedRecordIndices, realRecordIndex);
            index > -1 && internalProps.expandedRecordIndices.splice(index, 1);
        }
        this.eventManager.removeExpandedRow(rowIndex);
        const deltaHeight = this.table.getRowHeight(rowIndex) - getOriginalRowHeight(this.table, bodyRowIndex);
        this.updateRowHeightForExpand(rowIndex, -deltaHeight), internalProps._heightResizedRowMap.delete(rowIndex), 
        this.table.scenegraph.updateContainerHeight(rowIndex, -deltaHeight), internalProps.originalRowHeights && internalProps.originalRowHeights.delete(bodyRowIndex), 
        rowIndex !== this.table.rowCount - 1 && this.removeUnderlineFromRow(rowIndex), this.subTableManager.recalculateAllSubTablePositions(bodyRowIndex + 1, void 0, ((record, bodyRowIndex) => this.configManager.getDetailConfigForRecord(record, bodyRowIndex))), 
        this.refreshRowIcon(rowIndex, colIndex), "adaptive" === this.table.heightMode && this.table.scenegraph.dealHeightMode();
    }
    collapseRowToNoRealRecordIndex(rowIndex) {
        const bodyRowIndex = rowIndex - this.table.columnHeaderLevelCount, internalProps = getInternalProps(this.table);
        if (!this.eventManager.isRowExpanded(rowIndex)) return;
        this.subTableManager.removeSubTable(bodyRowIndex), this.eventManager.removeExpandedRow(rowIndex);
        const deltaHeight = this.table.getRowHeight(rowIndex) - getOriginalRowHeight(this.table, bodyRowIndex);
        this.updateRowHeightForExpand(rowIndex, -deltaHeight), internalProps._heightResizedRowMap.delete(rowIndex), 
        this.table.scenegraph.updateContainerHeight(rowIndex, -deltaHeight), internalProps.originalRowHeights && internalProps.originalRowHeights.delete(bodyRowIndex), 
        rowIndex !== this.table.rowCount - 1 && this.removeUnderlineFromRow(rowIndex), this.subTableManager.recalculateAllSubTablePositions(bodyRowIndex + 1, void 0, ((record, bodyRowIndex) => this.configManager.getDetailConfigForRecord(record, bodyRowIndex))), 
        this.refreshRowIcon(rowIndex), "adaptive" === this.table.heightMode && this.table.scenegraph.dealHeightMode();
    }
    toggleRowExpand(rowIndex, colIndex) {
        this.eventManager.isRowExpanded(rowIndex) ? this.collapseRow(rowIndex, colIndex) : this.expandRow(rowIndex, colIndex);
    }
    updateRowHeightForExpand(rowIndex, deltaHeight) {
        this.table._setRowHeight(rowIndex, this.table.getRowHeight(rowIndex) + deltaHeight, !0);
        const rowStart = rowIndex + 1;
        let rowEnd = 0;
        rowEnd = rowIndex < this.table.frozenRowCount ? this.table.frozenRowCount - 1 : rowIndex >= this.table.rowCount - this.table.bottomFrozenRowCount ? this.table.rowCount - 1 : Math.min(this.table.scenegraph.proxy.rowEnd, this.table.rowCount - this.table.bottomFrozenRowCount - 1);
        for (let colIndex = 0; colIndex < this.table.colCount; colIndex++) for (let rowIdx = rowStart; rowIdx <= rowEnd; rowIdx++) {
            const cellGroup = this.table.scenegraph.highPerformanceGetCell(colIndex, rowIdx);
            "cell" === cellGroup.role && cellGroup.setAttribute("y", cellGroup.attribute.y + deltaHeight);
        }
    }
    drawUnderlineForRow(rowIndex) {
        if (!this.table.scenegraph) return;
        const rowCells = this.getRowCells(rowIndex);
        0 !== rowCells.length && (rowCells.forEach(((cellGroup, index) => {
            cellGroup && cellGroup.attribute && this.addUnderlineToCell(cellGroup);
        })), this.table.scenegraph.updateNextFrame());
    }
    getRowCells(rowIndex) {
        const cells = [];
        for (let col = 0; col < this.table.colCount; col++) {
            const cellGroup = this.table.scenegraph.getCell(col, rowIndex);
            cellGroup && "cell" === cellGroup.role && cells.push(cellGroup);
        }
        return cells;
    }
    addUnderlineToCell(cellGroup) {
        var _a;
        const currentAttr = cellGroup.attribute, currentStrokeArrayWidth = currentAttr.strokeArrayWidth || (currentAttr.lineWidth ? [ currentAttr.lineWidth, currentAttr.lineWidth, currentAttr.lineWidth, currentAttr.lineWidth ] : [ 1, 1, 1, 1 ]), currentStrokeArrayColor = currentAttr.strokeArrayColor || (currentAttr.stroke ? [ currentAttr.stroke, currentAttr.stroke, currentAttr.stroke, currentAttr.stroke ] : [ "transparent", "transparent", "transparent", "transparent" ]);
        cellGroup._hasUnderline || (cellGroup._originalStrokeArrayWidth = [ ...currentStrokeArrayWidth ], 
        cellGroup._originalStrokeArrayColor = [ ...currentStrokeArrayColor ], cellGroup._hasUnderline = !0);
        const originalStrokeArrayWidth = cellGroup._originalStrokeArrayWidth || currentStrokeArrayWidth, originalStrokeArrayColor = cellGroup._originalStrokeArrayColor || currentStrokeArrayColor, enhancedStrokeArrayWidth = [ ...originalStrokeArrayWidth ], enhancedStrokeArrayColor = [ ...originalStrokeArrayColor ], enhancedWidth = 2 * (.75 * (originalStrokeArrayWidth[2] || 1) + .75 * (originalStrokeArrayWidth[0] || 1));
        if (enhancedStrokeArrayWidth[2] = enhancedWidth, "transparent" !== originalStrokeArrayColor[2] && originalStrokeArrayColor[2]) enhancedStrokeArrayColor[2] = originalStrokeArrayColor[2]; else {
            const theme = this.table.theme;
            enhancedStrokeArrayColor[2] = (null === (_a = null == theme ? void 0 : theme.bodyStyle) || void 0 === _a ? void 0 : _a.borderColor) || "#e1e4e8";
        }
        cellGroup.setAttributes({
            strokeArrayWidth: enhancedStrokeArrayWidth,
            strokeArrayColor: enhancedStrokeArrayColor,
            stroke: !0
        });
    }
    removeUnderlineFromRow(rowIndex) {
        this.getRowCells(rowIndex).forEach(((cellGroup, index) => {
            cellGroup && cellGroup._hasUnderline && this.removeUnderlineFromCell(cellGroup);
        })), this.table.scenegraph.updateNextFrame();
    }
    removeUnderlineFromCell(cellGroup) {
        cellGroup._hasUnderline && (cellGroup._originalStrokeArrayWidth && cellGroup._originalStrokeArrayColor && (cellGroup.setAttributes({
            strokeArrayWidth: cellGroup._originalStrokeArrayWidth,
            strokeArrayColor: cellGroup._originalStrokeArrayColor
        }), delete cellGroup._originalStrokeArrayWidth, delete cellGroup._originalStrokeArrayColor), 
        cellGroup._hasUnderline = !1);
    }
    refreshRowIcon(rowIndex, colIndex) {
        let targetColumnIndex;
        if ("number" == typeof colIndex) targetColumnIndex = colIndex; else {
            targetColumnIndex = !!this.table.options.rowSeriesNumber ? 1 : 0;
        }
        const record = this.table.getCellOriginRecord(targetColumnIndex, rowIndex);
        if (record && "object" == typeof record) {
            const recordObj = record, HierarchyState = VTable.TYPES.HierarchyState, isExpanded = this.eventManager.isRowExpanded(rowIndex);
            recordObj.hierarchyState = isExpanded ? HierarchyState.expand : HierarchyState.collapse, 
            this.table.scenegraph.updateHierarchyIcon(targetColumnIndex, rowIndex);
        }
    }
    update() {
        this.table && this.subTableManager.recalculateAllSubTablePositions(0, void 0, ((record, bodyRowIndex) => this.configManager.getDetailConfigForRecord(record, bodyRowIndex)));
    }
    setRecordChildren(children, col, row) {
        const recordIndex = this.table.getRecordIndexByCell(col, row);
        if (null == recordIndex) return;
        const realRecordIndex = "number" == typeof recordIndex ? recordIndex : recordIndex[0], record = this.table.dataSource.get(realRecordIndex);
        record && (record.children = children, this.expandRow(row, col), this.table.scenegraph.updateCellContent(col, row));
    }
    release() {
        var _a, _b, _c, _d, _e;
        const internalProps = getInternalProps(this.table);
        internalProps && (internalProps._isReleasing = !0);
        try {
            if (this.resizeObserver && (this.resizeObserver.disconnect(), this.resizeObserver = void 0), 
            this.checkboxCascadeCleanup) {
                try {
                    this.checkboxCascadeCleanup();
                } catch (error) {}
                this.checkboxCascadeCleanup = void 0;
            }
            if (this.eventManager) try {
                this.eventManager.cleanup();
            } catch (error) {}
            if (this.subTableManager) try {
                this.subTableManager.cleanup();
            } catch (error) {}
            if (this.configManager) try {
                this.configManager.release();
            } catch (error) {}
            if (this.cleanupMasterDetailFeatures(), this.table && internalProps) try {
                null === (_a = internalProps.expandedRecordIndices) || void 0 === _a || _a.splice(0), 
                null === (_b = internalProps.subTableInstances) || void 0 === _b || _b.clear(), 
                null === (_c = internalProps.originalRowHeights) || void 0 === _c || _c.clear(), 
                null === (_d = internalProps.subTableCheckboxStates) || void 0 === _d || _d.clear(), 
                null === (_e = internalProps._heightResizedRowMap) || void 0 === _e || _e.clear();
            } catch (error) {}
        } catch (error) {} finally {
            try {
                this.configManager = null, this.eventManager = null, this.subTableManager = null, 
                this.tableAPIExtensions = null, this.table = null, this.pluginOptions = null;
            } catch (error) {}
        }
    }
    clearAllSubTableVisibleSelections() {
        var _a;
        null === (_a = getInternalProps(this.table).subTableInstances) || void 0 === _a || _a.forEach((subTable => {
            subTable && "function" == typeof subTable.clearSelected && subTable.clearSelected();
        }));
    }
    getAllSubTableInstances() {
        return this.subTableManager.getAllSubTableInstances();
    }
    getSubTableByRowIndex(rowIndex) {
        const bodyRowIndex = rowIndex - this.table.columnHeaderLevelCount;
        return this.subTableManager.getSubTableInstance(bodyRowIndex);
    }
    getSubTableByBodyRowIndex(bodyRowIndex) {
        return this.subTableManager.getSubTableInstance(bodyRowIndex);
    }
    filterSubTables(predicate) {
        const result = [], allSubTables = this.subTableManager.getAllSubTableInstances();
        if (!allSubTables) return result;
        for (const [bodyRowIndex, subTable] of allSubTables) try {
            predicate(bodyRowIndex, subTable, getRecordByRowIndex(this.table, bodyRowIndex)) && result.push({
                bodyRowIndex: bodyRowIndex,
                subTable: subTable
            });
        } catch (error) {}
        return result;
    }
    cleanupMasterDetailFeatures() {
        this.mainTableClickHandler && (this.table.off("click_cell", this.mainTableClickHandler), 
        this.mainTableClickHandler = void 0), this.tableAPIExtensions && (this.tableAPIExtensions.cleanup(), 
        this.tableAPIExtensions = void 0);
    }
}