"use strict";

Object.defineProperty(exports, "__esModule", {
    value: !0
}), exports.AutoFillManager = void 0;

const types_1 = require("./types"), vtable_1 = require("@visactor/vtable"), auto_fill_helper_1 = require("./auto-fill-helper"), ts_types_1 = require("@visactor/vtable/es/ts-types"), formula_integration_1 = require("./formula-integration"), auto_fill_services_1 = require("./auto-fill-services"), rules_1 = require("./rules"), fill_1 = require("./utils/fill");

class AutoFillManager {
    constructor(options) {
        this.sourceData = [], this.options = options, this.autoFillService = new auto_fill_services_1.AutoFillService, 
        this.formulaAdapter = new formula_integration_1.DefaultFormulaAdapter({});
    }
    setTable(table) {
        var _a, _b, _c;
        this.headers = {
            row: new Set,
            col: new Set
        }, this.tableInstance = table, this.formulaAdapter = (0, formula_integration_1.createFormulaAdapter)(table, null === (_a = this.options) || void 0 === _a ? void 0 : _a.isFormulaCell, null === (_b = this.options) || void 0 === _b ? void 0 : _b.getCellFormula, null === (_c = this.options) || void 0 === _c ? void 0 : _c.setCellFormula), 
        table.on(vtable_1.TABLE_EVENT_TYPE.DROPDOWN_MENU_CLICK, (args => {
            "复制填充" === args.text ? this.fillData(types_1.APPLY_TYPE.COPY) : "序列填充" === args.text && this.fillData(types_1.APPLY_TYPE.SERIES);
        }));
        const rowHeaderCells = this.tableInstance.getAllRowHeaderCells()[0];
        rowHeaderCells && rowHeaderCells.forEach((cell => {
            this.headers.col.add(cell.col);
        }));
        const colHeaderCells = this.tableInstance.getAllColumnHeaderCells()[0];
        colHeaderCells && colHeaderCells.forEach((cell => {
            this.headers.row.add(cell.row);
        }));
    }
    handleStartDrag(selectedRange) {
        this.sourceRange = (0, auto_fill_helper_1.getSelectedRangeArray)(selectedRange), 
        this.sourceRange.cols = this.sourceRange.cols.filter((col => !this.headers.col.has(col))), 
        this.sourceRange.rows = this.sourceRange.rows.filter((row => !this.headers.row.has(row)));
    }
    handleEndDrag(endSelectCellRange, direction) {
        var _a;
        this.direction = direction;
        const selectedRange = (0, auto_fill_helper_1.getSelectedRangeArray)(endSelectCellRange);
        this.targetRange = (0, auto_fill_helper_1.getTargetRange)(this.direction, this.sourceRange, selectedRange), 
        this.targetRange.cols = this.targetRange.cols.filter((col => !this.headers.col.has(col))), 
        this.targetRange.rows = this.targetRange.rows.filter((row => !this.headers.row.has(row))), 
        (null === (_a = this.options) || void 0 === _a ? void 0 : _a.fillMode) ? this.fillData(this.options.fillMode) : (0, 
        auto_fill_helper_1.openAutoFillMenu)(this.tableInstance, Math.max(...selectedRange.cols), Math.max(...selectedRange.rows));
    }
    handleDbClick() {
        var _a;
        if (!this.sourceRange) return;
        this.direction = types_1.Direction.DOWN;
        const detectFillRange = (0, auto_fill_helper_1.getSelectedRangeArray)(this._detectFillRange());
        if (this.targetRange = (0, auto_fill_helper_1.getTargetRange)(this.direction, this.sourceRange, detectFillRange), 
        this.targetRange.cols = this.targetRange.cols.filter((col => !this.headers.col.has(col))), 
        this.targetRange.rows = this.targetRange.rows.filter((row => !this.headers.row.has(row))), 
        0 === this.targetRange.cols.length || 0 === this.targetRange.rows.length) return;
        const startCol = this.sourceRange.cols[0], startRow = this.sourceRange.rows[0], endCol = this.targetRange.cols[this.targetRange.cols.length - 1], endRow = this.targetRange.rows[this.targetRange.rows.length - 1], stateManager = this.tableInstance.stateManager;
        stateManager.updateSelectPos(startCol, startRow, !1, !1, !1, !0, !0), stateManager.updateInteractionState(ts_types_1.InteractionState.grabing), 
        stateManager.updateSelectPos(endCol, endRow, !1, !1, !1, !0, !0), stateManager.endSelectCells(!1, !1), 
        stateManager.updateInteractionState(ts_types_1.InteractionState.default), (null === (_a = this.options) || void 0 === _a ? void 0 : _a.fastFillMode) ? this.fillData(this.options.fastFillMode) : (0, 
        auto_fill_helper_1.openAutoFillMenu)(this.tableInstance, endCol, endRow);
    }
    fillData(applyType) {
        this.sourceData = this.getSourceData(this.sourceRange, this.direction);
        const location = {
            source: this.sourceRange,
            target: this.targetRange
        };
        this._fillData(location, this.direction, applyType);
    }
    getSourceData(source, direction) {
        const rules = this.autoFillService.getRules(), sourceData = [], isVertical = direction === types_1.Direction.DOWN || direction === types_1.Direction.UP;
        let aArray, bArray;
        isVertical ? (aArray = source.cols, bArray = source.rows) : (aArray = source.rows, 
        bArray = source.cols);
        const matrix = (0, auto_fill_helper_1.getCellMatrix)(this.tableInstance);
        return aArray.forEach((a => {
            const sourceDataPiece = this.getEmptySourceDataPiece(), prevData = {
                type: void 0,
                cellData: void 0
            };
            bArray.forEach((b => {
                let data;
                const col = isVertical ? a : b, row = isVertical ? b : a;
                if (data = isVertical ? matrix.getValue(b, a) : matrix.getValue(a, b), data && this.formulaAdapter.isFormulaCell(col, row)) {
                    const formula = this.formulaAdapter.getCellFormula(col, row);
                    formula && (data = Object.assign(Object.assign({}, data), {
                        v: formula
                    }));
                }
                const ruleMatch = rules.find((r => r.match(data, null))) || rules_1.otherRule, {type: type, isContinue: isContinue} = ruleMatch;
                if (isContinue(prevData, data)) {
                    const typeInfo = sourceDataPiece[type], last = typeInfo[typeInfo.length - 1];
                    last.data.push(data), last.index.push(b - bArray[0]);
                } else {
                    const typeInfo = sourceDataPiece[type];
                    typeInfo ? typeInfo.push({
                        data: [ data ],
                        index: [ b - bArray[0] ]
                    }) : sourceDataPiece[type] = [ {
                        data: [ data ],
                        index: [ b - bArray[0] ]
                    } ];
                }
                prevData.type = type, prevData.cellData = data;
            })), sourceData.push(sourceDataPiece);
        })), sourceData;
    }
    getEmptySourceDataPiece() {
        const sourceDataPiece = {};
        return this.autoFillService.getRules().forEach((r => {
            sourceDataPiece[r.type] = [];
        })), sourceDataPiece;
    }
    _detectFillRange() {
        var _a, _b, _c, _d, _e;
        const start = {
            row: Math.min(...this.sourceRange.rows),
            col: Math.min(...this.sourceRange.cols)
        }, end = {
            row: Math.max(...this.sourceRange.rows),
            col: Math.max(...this.sourceRange.cols)
        }, matrix = (0, auto_fill_helper_1.getCellMatrix)(this.tableInstance), maxRow = matrix.getMaxRows(), maxColumn = matrix.getMaxColumns();
        let detectEndRow = end.row;
        if (start.col > 0 && (null === (_a = matrix.getValue(start.row, start.col - 1)) || void 0 === _a ? void 0 : _a.v)) {
            let cur = start.row;
            for (;(null === (_b = matrix.getValue(cur, start.col - 1)) || void 0 === _b ? void 0 : _b.v) && cur <= maxRow; ) cur += 1;
            detectEndRow = cur - 1;
        } else if (end.col < maxColumn && (null === (_c = matrix.getValue(end.row, end.col + 1)) || void 0 === _c ? void 0 : _c.v)) {
            let cur = start.row;
            for (;(null === (_d = matrix.getValue(cur, end.col + 1)) || void 0 === _d ? void 0 : _d.v) && cur <= maxRow; ) cur += 1;
            detectEndRow = cur - 1;
        }
        for (let i = end.row + 1; i <= detectEndRow; i++) for (let j = start.col; j <= end.col; j++) if (null === (_e = matrix.getValue(i, j)) || void 0 === _e ? void 0 : _e.v) {
            detectEndRow = i - 1;
            break;
        }
        return {
            start: {
                row: start.row,
                col: start.col
            },
            end: {
                row: detectEndRow,
                col: end.col
            }
        };
    }
    _fillData(location, direction, applyType) {
        const {source: source, target: target} = location;
        if (!source || !target || null == direction) return;
        const {cols: targetCols, rows: targetRows} = target, {cols: sourceCols, rows: sourceRows} = source, sourceData = this.sourceData;
        let csLen;
        csLen = direction === types_1.Direction.DOWN || direction === types_1.Direction.UP ? sourceRows.length : sourceCols.length;
        const applyDatas = [];
        if (direction === types_1.Direction.DOWN || direction === types_1.Direction.UP) {
            const asLen = targetRows.length, untransformedApplyDatas = [];
            targetCols.forEach(((_, i) => {
                const copyD = sourceData[i], applyData = this.getApplyData(copyD, csLen, asLen, direction, applyType, location);
                untransformedApplyDatas.push(applyData);
            }));
            for (let i = 0; i < untransformedApplyDatas[0].length; i++) {
                const row = [];
                for (let j = 0; j < untransformedApplyDatas.length; j++) row.push(untransformedApplyDatas[j][i]);
                applyDatas.push(row);
            }
        } else {
            const asLen = targetCols.length;
            targetRows.forEach(((_, i) => {
                const copyD = sourceData[i], applyData = this.getApplyData(copyD, csLen, asLen, direction, applyType, location), row = [];
                for (let j = 0; j < applyData.length; j++) row.push(applyData[j]);
                applyDatas.push(row);
            }));
        }
        const values = [], formulaUpdates = [];
        targetRows.forEach(((row, rowIndex) => {
            const rowValues = [];
            targetCols.forEach(((col, colIndex) => {
                const range = (0, auto_fill_helper_1.isMergeCell)(this.tableInstance, col, row);
                if (range && this.tableInstance.unmergeCells(range.start.col, range.start.row, range.end.col, range.end.row), 
                applyDatas[rowIndex][colIndex]) {
                    const cellData = applyDatas[rowIndex][colIndex], cellValue = (null == cellData ? void 0 : cellData.v) + "";
                    cellData && "string" == typeof cellData.v && cellData.v.startsWith("=") ? (formulaUpdates.push({
                        col: col,
                        row: row,
                        formula: cellData.v
                    }), rowValues.push(cellValue)) : rowValues.push(cellValue);
                }
            })), values.push(rowValues);
        }));
        const minRow = Math.min(...targetRows), minCol = Math.min(...targetCols);
        this.tableInstance.changeCellValues(minCol, minRow, values), formulaUpdates.length > 0 && (formulaUpdates.forEach((({col: col, row: row, formula: formula}) => {
            this.formulaAdapter.setCellFormula(col, row, formula);
        })), this.formulaAdapter.hasFormulaEngine() && this.formulaAdapter.refreshFormulas());
    }
    getApplyData(sourceDataPiece, csLen, asLen, direction, applyType, location) {
        const applyData = [], num = Math.floor(asLen / csLen), rsd = asLen % csLen, rules = this.autoFillService.getRules(), applyDataInTypes = {};
        rules.forEach((r => {
            applyDataInTypes[r.type] = [];
        })), rules.forEach((r => {
            const {type: type, applyFunctions: customApplyFunctions = {}} = r, copyDataInType = sourceDataPiece[type];
            copyDataInType && copyDataInType.forEach((copySquad => {
                const s = (0, fill_1.getLenS)(copySquad.index, rsd), len = copySquad.index.length * num + s, arrData = this.applyFunctions(copySquad, len, direction, applyType, customApplyFunctions, sourceDataPiece, location), arrIndex = (0, 
                fill_1.getDataIndex)(csLen, asLen, copySquad.index);
                applyDataInTypes[type].push({
                    data: arrData,
                    index: arrIndex
                });
            }));
        }));
        for (let x = 0; x < asLen; x++) rules.forEach((r => {
            const {type: type} = r, applyDataInType = applyDataInTypes[type];
            for (let y = 0; y < applyDataInType.length; y++) x in applyDataInType[y].index && applyData.push(applyDataInType[y].data[applyDataInType[y].index[x]]);
        }));
        return applyData;
    }
    applyFunctions(copySquad, len, direction, applyType, customApplyFunctions, sourceDataPiece, location) {
        const {data: data} = copySquad, isReverse = direction === types_1.Direction.UP || direction === types_1.Direction.LEFT;
        if (applyType === types_1.APPLY_TYPE.COPY) {
            const custom = null == customApplyFunctions ? void 0 : customApplyFunctions[types_1.APPLY_TYPE.COPY];
            return custom ? custom(copySquad, len, direction, sourceDataPiece, location) : (isReverse && data.reverse(), 
            (0, fill_1.fillCopy)(data, len));
        }
        if (applyType === types_1.APPLY_TYPE.SERIES) {
            const custom = null == customApplyFunctions ? void 0 : customApplyFunctions[types_1.APPLY_TYPE.SERIES];
            return custom ? custom(copySquad, len, direction, sourceDataPiece) : (isReverse && data.reverse(), 
            (null == customApplyFunctions ? void 0 : customApplyFunctions[types_1.APPLY_TYPE.COPY]) ? customApplyFunctions[types_1.APPLY_TYPE.COPY](copySquad, len, direction, sourceDataPiece, location) : (0, 
            fill_1.fillCopy)(data, len));
        }
        return [];
    }
}

exports.AutoFillManager = AutoFillManager;
//# sourceMappingURL=auto-fill-manager.js.map