"use strict";

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

const formula_throttle_1 = require("./formula-throttle"), formula_helper_1 = require("./formula-helper");

class FormulaRangeSelector {
    constructor(formulaManager) {
        this.formulaManager = formulaManager;
    }
    selectionsToA1Notation(selections, addressFromCoord) {
        if (!selections || 0 === selections.length) return "";
        const ranges = [];
        for (const range of selections) {
            const startAddr = addressFromCoord(range.startRow, range.startCol), endAddr = addressFromCoord(range.endRow, range.endCol);
            range.startRow === range.endRow && range.startCol === range.endCol ? ranges.push(startAddr) : ranges.push(`${startAddr}:${endAddr}`);
        }
        return ranges.join(",");
    }
    insertA1ReferenceInFunction(formulaInput, a1Notation, isCtrlAddSelection) {
        if (!this.formulaManager.inputIsParamMode.inParamMode) return;
        const currentValue = formulaInput.value, cursorPos = this.formulaManager.lastKnownCursorPosInFormulaInput, argPosition = this.findCurrentArgumentPosition(currentValue, cursorPos);
        let newValue;
        if (argPosition) if (isCtrlAddSelection) {
            newValue = currentValue.substring(argPosition.start, argPosition.end).trim() ? currentValue.slice(0, argPosition.end) + ("," === currentValue[argPosition.end - 1] ? " " : ", ") + a1Notation + currentValue.slice(argPosition.end) : currentValue.slice(0, argPosition.start) + a1Notation + currentValue.slice(argPosition.end);
        } else newValue = currentValue.slice(0, argPosition.start) + a1Notation + currentValue.slice(argPosition.end); else newValue = currentValue;
        formulaInput.value = newValue, this.formulaManager.setCellContent({
            sheet: this.formulaManager.sheet.getActiveSheet().getKey(),
            row: this.formulaManager.sheet.getActiveSheet().editingCell.row,
            col: this.formulaManager.sheet.getActiveSheet().editingCell.col
        }, newValue);
        const newCursorPos = isCtrlAddSelection ? this.formulaManager.lastKnownCursorPosInFormulaInput + (newValue.length - currentValue.length) : argPosition ? argPosition.start + a1Notation.length : cursorPos;
        formulaInput.setSelectionRange(newCursorPos, newCursorPos), setTimeout((() => {
            this.ensureCursorVisible(formulaInput, newCursorPos);
        })), this.formulaManager.inputIsParamMode.functionParamPosition = {
            start: newCursorPos,
            end: newCursorPos
        };
        const inputEvent = new Event("input", {
            bubbles: !0
        });
        Object.defineProperty(inputEvent, "isFormulaInsertion", {
            value: !0
        }), formulaInput.dispatchEvent(inputEvent);
    }
    findCurrentArgumentPosition(formula, cursorPos) {
        if (cursorPos > 1 && cursorPos <= formula.length) {
            const prevChar = formula[cursorPos - 1];
            if ([ "+", "-", "*", "/", "=", ">", "<", "&", "|", "^" ].includes(prevChar)) return {
                start: cursorPos,
                end: cursorPos
            };
        }
        if (!formula.includes("(")) return null;
        let lastOpenParenBeforeCursor = -1, nestLevel = 0, inQuote = !1;
        for (let i = 0; i < cursorPos; i++) {
            const char = formula[i];
            '"' !== char || 0 !== i && "\\" === formula[i - 1] ? inQuote || ("(" === char ? (nestLevel++, 
            lastOpenParenBeforeCursor = i) : ")" === char && nestLevel--) : inQuote = !inQuote;
        }
        if (-1 === lastOpenParenBeforeCursor || nestLevel <= 0) return formula.startsWith("=") && 1 === cursorPos ? {
            start: cursorPos,
            end: cursorPos
        } : null;
        if (cursorPos === lastOpenParenBeforeCursor + 1) return {
            start: cursorPos,
            end: cursorPos
        };
        if (cursorPos > 0 && "," === formula[cursorPos - 1]) return {
            start: cursorPos,
            end: cursorPos
        };
        let argumentStart = lastOpenParenBeforeCursor + 1, argumentEnd = cursorPos;
        nestLevel = 1, inQuote = !1;
        for (let i = argumentStart; i < cursorPos; i++) {
            const char = formula[i];
            if ('"' !== char || 0 !== i && "\\" === formula[i - 1]) {
                if (!inQuote) if ("(" === char) nestLevel++; else if (")" === char) {
                    if (nestLevel--, 0 === nestLevel) break;
                } else "," === char && 1 === nestLevel && (argumentStart = i + 1);
            } else inQuote = !inQuote;
        }
        nestLevel = 0, inQuote = !1;
        for (let i = 0; i < cursorPos; i++) '"' !== formula[i] || 0 !== i && "\\" === formula[i - 1] ? inQuote || ("(" === formula[i] && nestLevel++, 
        ")" === formula[i] && nestLevel--) : inQuote = !inQuote;
        argumentEnd = formula.length;
        for (let i = cursorPos; i < formula.length; i++) {
            const char = formula[i];
            if ('"' !== char || 0 !== i && "\\" === formula[i - 1]) {
                if (!inQuote) if ("(" === char) nestLevel++; else if (")" === char) {
                    if (nestLevel--, 0 === nestLevel) {
                        argumentEnd = i;
                        break;
                    }
                } else if ("," === char && 1 === nestLevel) {
                    argumentEnd = i;
                    break;
                }
            } else inQuote = !inQuote;
        }
        const paramContentBefore = formula.substring(argumentStart, cursorPos).trim(), paramContentAfter = formula.substring(cursorPos, argumentEnd).trim();
        return "" === paramContentBefore && cursorPos === argumentStart ? {
            start: argumentStart,
            end: argumentStart
        } : ("" === paramContentAfter && cursorPos < argumentEnd && (argumentEnd = cursorPos), 
        {
            start: argumentStart,
            end: argumentEnd
        });
    }
    handleSelectionChanged(selections, formulaInput, isCtrlAddSelection, addressFromCoord) {
        if (!this.formulaManager.inputIsParamMode.inParamMode || !selections || 0 === selections.length) return;
        const a1Notation = this.selectionsToA1Notation(selections, addressFromCoord);
        a1Notation && this.insertA1ReferenceInFunction(formulaInput, a1Notation, isCtrlAddSelection);
    }
    handleCellValueChanged(event) {
        var _a, _b, _c;
        const activeWorkSheet = this.formulaManager.sheet.getActiveSheet(), formulaManager = this.formulaManager.sheet.formulaManager;
        if (activeWorkSheet && !this.formulaManager.formulaWorkingOnCell) try {
            const newValue = event.newValue;
            if ("string" == typeof newValue && newValue.startsWith("=") && newValue.length > 1) try {
                const currentCellAddress = activeWorkSheet.addressFromCoord(event.row, event.col);
                if (new RegExp(`(^|[^A-Za-z0-9])${currentCellAddress}([^A-Za-z0-9]|$)`).test(newValue)) return null === (_a = activeWorkSheet.tableInstance) || void 0 === _a || _a.changeCellValue(event.col, event.row, "#CYCLE!", !0, !1), 
                void (this.formulaManager.formulaWorkingOnCell = null);
                formulaManager.setCellContent({
                    sheet: activeWorkSheet.getKey(),
                    row: event.row,
                    col: event.col
                }, newValue);
                const result = formulaManager.getCellValue({
                    sheet: activeWorkSheet.getKey(),
                    row: event.row,
                    col: event.col
                });
                null === (_b = activeWorkSheet.tableInstance) || void 0 === _b || _b.changeCellValue(event.col, event.row, result.value, !1, !1), 
                this.formulaManager.formulaWorkingOnCell = null;
            } catch (error) {
                null === (_c = activeWorkSheet.tableInstance) || void 0 === _c || _c.changeCellValue(event.col, event.row, "#ERROR!", !0, !1), 
                this.formulaManager.formulaWorkingOnCell = null;
            } else {
                formulaManager.setCellContent({
                    sheet: activeWorkSheet.getKey(),
                    row: event.row,
                    col: event.col
                }, newValue);
                this.hasFormulaDependents({
                    sheet: activeWorkSheet.getKey(),
                    row: event.row,
                    col: event.col
                }) && this.updateDependentCellsCascade(activeWorkSheet, formulaManager, {
                    sheet: activeWorkSheet.getKey(),
                    row: event.row,
                    col: event.col
                });
            }
            const formulaThrottle = formula_throttle_1.FormulaThrottle.getInstance();
            this.hasFormulaDependents({
                sheet: activeWorkSheet.getKey(),
                row: event.row,
                col: event.col
            }) ? (this.updateDependentCellsCascade(activeWorkSheet, formulaManager, {
                sheet: activeWorkSheet.getKey(),
                row: event.row,
                col: event.col
            }), formulaThrottle.immediateRebuildAndRecalculate(formulaManager)) : formulaThrottle.throttledRebuildAndRecalculate(formulaManager);
        } catch (error) {}
    }
    handleSelectionChangedForRangeMode(event) {
        var _a;
        const activeWorkSheet = this.formulaManager.sheet.getActiveSheet(), formulaWorkingOnCell = this.formulaManager.formulaWorkingOnCell, formulaManager = this.formulaManager.sheet.formulaManager;
        if (!activeWorkSheet || !formulaWorkingOnCell) return;
        const formulaInput = this.formulaManager.inputingElement;
        if (!formulaInput) return;
        if (this.formulaManager.inputIsParamMode = (0, formula_helper_1.detectFunctionParameterPosition)(formulaInput.value, this.formulaManager.lastKnownCursorPosInFormulaInput), 
        !this.formulaManager.inputIsParamMode.inParamMode) return;
        document.activeElement !== formulaInput && formulaInput.focus();
        const selections = activeWorkSheet.getMultipleSelections(), todoSelection = selections[selections.length - 1];
        let isCtrlAddSelection = !1;
        if ((null == selections ? void 0 : selections.length) > (null === (_a = formulaManager.lastSelectionRangesOfHandling) || void 0 === _a ? void 0 : _a.length) && (isCtrlAddSelection = !0), 
        formulaManager.lastSelectionRangesOfHandling = selections, !selections || 0 === selections.length) return;
        const editCell = formulaManager.formulaWorkingOnCell, safeSelections = this.formulaManager.sheet.excludeEditCellFromSelection(todoSelection, (null == editCell ? void 0 : editCell.row) || 0, (null == editCell ? void 0 : editCell.col) || 0);
        this.handleSelectionChanged([ safeSelections ], formulaInput, isCtrlAddSelection, ((row, col) => activeWorkSheet.addressFromCoord(row, col)));
    }
    hasFormulaDependents(cell) {
        try {
            return this.formulaManager.getCellDependents(cell).length > 0;
        } catch (error) {
            return !1;
        }
    }
    updateDependentCellsCascade(activeWorkSheet, formulaManager, changedCell) {
        var _a;
        try {
            const allDependents = this.getAllDependentsRecursive(formulaManager, changedCell);
            if (0 === allDependents.length) return;
            const sortedDependents = this.sortCellsByDependencyOrder(formulaManager, allDependents);
            for (const dependent of sortedDependents) {
                const result = formulaManager.getCellValue(dependent);
                result && activeWorkSheet && (null === (_a = activeWorkSheet.tableInstance) || void 0 === _a || _a.changeCellValue(dependent.col, dependent.row, result.value, !1, !1));
            }
        } catch (error) {}
    }
    getAllDependentsRecursive(formulaManager, cell, visited = new Set) {
        const cellKey = `${cell.sheet}!${cell.row},${cell.col}`;
        if (visited.has(cellKey)) return [];
        visited.add(cellKey);
        const directDependents = formulaManager.getCellDependents(cell);
        let allDependents = [ ...directDependents ];
        for (const dependent of directDependents) {
            const indirectDependents = this.getAllDependentsRecursive(formulaManager, dependent, visited);
            allDependents = allDependents.concat(indirectDependents);
        }
        return allDependents;
    }
    sortCellsByDependencyOrder(formulaManager, cells) {
        const dependencyGraph = new Map;
        for (const cell of cells) {
            const cellKey = `${cell.sheet}!${cell.row},${cell.col}`, precedents = formulaManager.getCellPrecedents(cell), dependencies = new Set;
            for (const precedent of precedents) {
                const precedentKey = `${precedent.sheet}!${precedent.row},${precedent.col}`;
                dependencies.add(precedentKey);
            }
            dependencyGraph.set(cellKey, dependencies);
        }
        const sorted = [], visited = new Set, visiting = new Set, visit = cell => {
            const cellKey = `${cell.sheet}!${cell.row},${cell.col}`;
            if (visited.has(cellKey)) return;
            if (visiting.has(cellKey)) return;
            visiting.add(cellKey);
            const dependencies = dependencyGraph.get(cellKey) || new Set;
            for (const depKey of Array.from(dependencies)) {
                const depCell = cells.find((c => `${c.sheet}!${c.row},${c.col}` === depKey));
                depCell && visit(depCell);
            }
            visiting.delete(cellKey), visited.add(cellKey), sorted.push(cell);
        };
        for (const cell of cells) visited.has(`${cell.sheet}!${cell.row},${cell.col}`) || visit(cell);
        return sorted;
    }
    ensureCursorVisible(input, cursorPos) {
        const tempSpan = document.createElement("span"), inputStyle = window.getComputedStyle(input);
        tempSpan.style.font = inputStyle.font, tempSpan.style.letterSpacing = inputStyle.letterSpacing, 
        tempSpan.style.position = "absolute", tempSpan.style.visibility = "hidden", tempSpan.style.whiteSpace = "pre", 
        tempSpan.textContent = input.value.substring(0, cursorPos), document.body.appendChild(tempSpan);
        const cursorOffset = tempSpan.offsetWidth;
        document.body.removeChild(tempSpan);
        const inputScrollLeft = input.scrollLeft, inputWidth = input.clientWidth, paddingLeft = parseFloat(inputStyle.paddingLeft), paddingRight = parseFloat(inputStyle.paddingRight), visibleWidth = inputWidth - paddingLeft - paddingRight;
        cursorOffset < inputScrollLeft + paddingLeft ? input.scrollLeft = Math.max(0, cursorOffset - paddingLeft) : cursorOffset > inputScrollLeft + visibleWidth && (input.scrollLeft = cursorOffset - visibleWidth + paddingRight);
    }
    release() {}
}

exports.FormulaRangeSelector = FormulaRangeSelector;
//# sourceMappingURL=formula-range-selector.js.map