"use strict";

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

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

class FormulaEngine {
    constructor(_config = {}) {
        this.sheets = new Map, this.reverseSheets = new Map, this.sheetData = new Map, this.formulaCells = new Map, 
        this.dependencies = new Map, this.dependents = new Map, this.nextSheetId = 0, this.activeSheetKey = null;
    }
    setActiveSheet(sheetKey) {
        this.sheets.has(sheetKey) && (this.activeSheetKey = sheetKey);
    }
    getActiveSheet() {
        return this.activeSheetKey;
    }
    addSheet(sheetKey, data) {
        if (this.sheets.has(sheetKey)) return this.sheets.get(sheetKey);
        const sheetId = this.nextSheetId++;
        this.sheets.set(sheetKey, sheetId), this.reverseSheets.set(sheetId, sheetKey);
        const sheetData = data || [ [ "" ] ];
        return this.sheetData.set(sheetId, sheetData), sheetId;
    }
    updateSheetData(sheetKey, data) {
        const sheetId = this.sheets.get(sheetKey);
        null != sheetId && this.sheetData.set(sheetId, data);
    }
    normalizeData(data) {
        if (!Array.isArray(data) || 0 === data.length) return [ [ "" ] ];
        const maxCols = Math.max(...data.map((row => Array.isArray(row) ? row.length : 0)));
        return data.map((row => {
            if (!Array.isArray(row)) return Array(maxCols).fill("");
            const normalizedRow = row.map((cell => {
                if ("string" == typeof cell) {
                    if (cell.startsWith("=")) return cell;
                    const num = Number(cell);
                    return isNaN(num) || "" === cell.trim() ? cell : num;
                }
                return null != cell ? cell : "";
            }));
            for (;normalizedRow.length < maxCols; ) normalizedRow.push("");
            return normalizedRow;
        }));
    }
    getSheetId(sheetKey) {
        const sheetId = this.sheets.get(sheetKey);
        return void 0 === sheetId ? this.addSheet(sheetKey) : sheetId;
    }
    getFormulaString(cell) {
        const cellKey = this.getCellKey(cell);
        return this.formulaCells.get(cellKey) || null;
    }
    clearDependencies(cellKey) {
        const oldDeps = this.dependencies.get(cellKey) || new Set;
        for (const dep of oldDeps) {
            const depDependents = this.dependents.get(dep) || new Set;
            depDependents.delete(cellKey), 0 === depDependents.size ? this.dependents.delete(dep) : this.dependents.set(dep, depDependents);
        }
        this.dependencies.delete(cellKey);
    }
    setCellContentWithoutDependencyUpdate(cell, value) {
        if (!cell || void 0 === cell.sheet || void 0 === cell.row || void 0 === cell.col) throw new Error("Invalid cell parameter");
        if (cell.row < 0 || cell.col < 0) throw new Error(`Cell coordinates out of bounds: row=${cell.row}, col=${cell.col}`);
        const sheetId = this.getSheetId(cell.sheet);
        this.sheetData.has(sheetId) || this.sheetData.set(sheetId, [ [ "" ] ]);
        const sheet = this.sheetData.get(sheetId);
        if (!sheet) throw new Error(`Sheet data not found for ID: ${sheetId}`);
        for (;sheet.length <= cell.row; ) sheet.push([]);
        for (;sheet[cell.row].length <= cell.col; ) sheet[cell.row].push("");
        let processedValue = value;
        if (null == processedValue && (processedValue = ""), "string" == typeof processedValue && !processedValue.startsWith("=")) {
            const numericValue = Number(processedValue);
            isNaN(numericValue) || "" === processedValue.trim() || (processedValue = numericValue);
        }
        sheet[cell.row][cell.col] = processedValue;
    }
    setCellContent(cell, value) {
        if (!cell || void 0 === cell.sheet || void 0 === cell.row || void 0 === cell.col) throw new Error("Invalid cell parameter");
        if (cell.row < 0 || cell.col < 0) throw new Error(`Cell coordinates out of bounds: row=${cell.row}, col=${cell.col}`);
        const sheetId = this.getSheetId(cell.sheet);
        this.sheetData.has(sheetId) || this.sheetData.set(sheetId, [ [ "" ] ]);
        const sheet = this.sheetData.get(sheetId);
        if (!sheet) throw new Error(`Sheet data not found for ID: ${sheetId}`);
        for (;sheet.length <= cell.row; ) sheet.push([]);
        for (;sheet[cell.row].length <= cell.col; ) sheet[cell.row].push("");
        let processedValue = value;
        if (null == processedValue && (processedValue = ""), "string" == typeof processedValue && !processedValue.startsWith("=")) {
            const numericValue = Number(processedValue);
            isNaN(numericValue) || "" === processedValue.trim() || (processedValue = numericValue);
        }
        if (sheet[cell.row][cell.col] = processedValue, "string" == typeof processedValue && processedValue.startsWith("=")) {
            const cellKey = this.getCellKey(cell), correctedFormula = this.correctFormulaCase(processedValue);
            this.formulaCells.set(cellKey, correctedFormula), this.updateDependencies(cellKey, correctedFormula), 
            sheet[cell.row][cell.col] = correctedFormula;
        }
        this.recalculateDependents(cell);
    }
    getCellKey(cell) {
        return `${cell.sheet}!${this.getA1Notation(cell.row, cell.col)}`;
    }
    getA1Notation(row, col) {
        let columnStr = "", tempCol = col;
        do {
            columnStr = String.fromCharCode(65 + tempCol % 26) + columnStr, tempCol = Math.floor(tempCol / 26) - 1;
        } while (tempCol >= 0);
        return columnStr + (row + 1);
    }
    parseA1Notation(cellRef) {
        const match = cellRef.match(/^([A-Z]+)([0-9]+)$/);
        if (!match) throw new Error(`Invalid cell reference: ${cellRef}`);
        const colLetters = match[1], rowNumber = parseInt(match[2], 10);
        let col = 0;
        for (let i = 0; i < colLetters.length; i++) col = 26 * col + (colLetters.charCodeAt(i) - 65);
        return {
            row: rowNumber - 1,
            col: col
        };
    }
    parseCellKey(cellKey) {
        const parts = cellKey.split("!");
        if (2 !== parts.length) return null;
        const [sheet, a1Notation] = parts;
        try {
            const {row: row, col: col} = this.parseA1Notation(a1Notation);
            return {
                sheet: sheet,
                row: row,
                col: col
            };
        } catch (_a) {
            return null;
        }
    }
    calculateFormula(formula) {
        try {
            if (!formula.startsWith("=")) return {
                value: formula,
                error: void 0
            };
            const expression = this.correctFormulaCase(formula).substring(1).trim();
            if (expression.includes("#REF!")) return {
                value: "#REF!",
                error: void 0
            };
            return this.parseExpression(expression);
        } catch (error) {
            return {
                value: null,
                error: error instanceof Error ? error.message : "Calculation failed"
            };
        }
    }
    correctFormulaCase(formula) {
        if (!formula.startsWith("=")) return formula;
        let corrected = "=";
        const expression = formula.substring(1);
        let inQuotes = !1, quoteChar = "", i = 0;
        for (;i < expression.length; ) {
            const char = expression[i];
            if (!('"' !== char && "'" !== char || 0 !== i && "\\" === expression[i - 1])) {
                inQuotes ? char === quoteChar && (inQuotes = !1, quoteChar = "") : (inQuotes = !0, 
                quoteChar = char), corrected += char, i++;
                continue;
            }
            if (inQuotes) {
                corrected += char, i++;
                continue;
            }
            const funcMatch = expression.substring(i).match(/^[A-Za-z][A-Za-z0-9]*\s*\(/);
            if (funcMatch) {
                const funcName = funcMatch[0].replace(/\s*\($/, "");
                for (corrected += funcName.toUpperCase() + "(", i += funcName.length + 1; i < expression.length && " " === expression[i]; ) i++;
                continue;
            }
            const sheetCellMatch = expression.substring(i).match(/^[A-Za-z0-9_]+![A-Za-z]+[0-9]+/);
            if (sheetCellMatch) {
                const fullRef = sheetCellMatch[0], [sheetName, cellRef] = fullRef.split("!"), originalSheetName = this.findOriginalSheetName(sheetName), letters = cellRef.replace(/[0-9]/g, ""), numbers = cellRef.replace(/[A-Za-z]/g, "");
                corrected += (originalSheetName || sheetName) + "!" + letters.toUpperCase() + numbers, 
                i += fullRef.length;
                continue;
            }
            const sheetRangeMatch = expression.substring(i).match(/^[A-Za-z0-9_]+![A-Za-z]+[0-9]+:[A-Za-z]+[0-9]+/);
            if (sheetRangeMatch) {
                const fullRangeRef = sheetRangeMatch[0], [sheetPart, rangePart] = fullRangeRef.split("!"), [startCell, endCell] = rangePart.split(":"), originalSheetName = this.findOriginalSheetName(sheetPart), startLetters = startCell.replace(/[0-9]/g, ""), startNumbers = startCell.replace(/[A-Za-z]/g, ""), newStartCell = startLetters.toUpperCase() + startNumbers, endLetters = endCell.replace(/[0-9]/g, ""), endNumbers = endCell.replace(/[A-Za-z]/g, "");
                corrected += (originalSheetName || sheetPart) + "!" + newStartCell + ":" + (endLetters.toUpperCase() + endNumbers), 
                i += fullRangeRef.length;
                continue;
            }
            const cellMatch = expression.substring(i).match(/^[A-Za-z]+[0-9]+/);
            if (cellMatch) {
                const cellRef = cellMatch[0], letters = cellRef.replace(/[0-9]/g, ""), numbers = cellRef.replace(/[A-Za-z]/g, "");
                corrected += letters.toUpperCase() + numbers, i += cellRef.length;
                continue;
            }
            const rangeMatch = expression.substring(i).match(/^[A-Za-z]+[0-9]+:[A-Za-z]+[0-9]+/);
            if (rangeMatch) {
                const rangeRef = rangeMatch[0], [startCell, endCell] = rangeRef.split(":"), startLetters = startCell.replace(/[0-9]/g, ""), startNumbers = startCell.replace(/[A-Za-z]/g, ""), newStartCell = startLetters.toUpperCase() + startNumbers, endLetters = endCell.replace(/[0-9]/g, ""), endNumbers = endCell.replace(/[A-Za-z]/g, "");
                corrected += newStartCell + ":" + (endLetters.toUpperCase() + endNumbers), i += rangeRef.length;
            } else corrected += char, i++;
        }
        return corrected;
    }
    findOriginalSheetName(sheetName) {
        if (this.sheets.has(sheetName)) return sheetName;
        const lowerSheetName = sheetName.toLowerCase();
        for (const [existingSheetName] of this.sheets.entries()) if (existingSheetName.toLowerCase() === lowerSheetName) return existingSheetName;
        return null;
    }
    parseExpression(expr) {
        expr = expr.trim();
        const functionResult = this.tryParseFunction(expr);
        if (functionResult) return functionResult;
        const comparisonResult = this.tryParseComparison(expr);
        return comparisonResult || this.parseArithmetic(expr);
    }
    tryParseComparison(expr) {
        const operators = [ ">=", "<=", "<>", ">", "<", "=" ];
        for (const op of operators) {
            const index = expr.indexOf(op);
            if (-1 !== index) {
                const leftExpr = expr.substring(0, index).trim(), rightExpr = expr.substring(index + op.length).trim();
                if (!leftExpr || !rightExpr) continue;
                const leftResult = this.parseExpression(leftExpr), rightResult = this.parseExpression(rightExpr);
                if (leftResult.error || rightResult.error) return {
                    value: null,
                    error: "Comparison operands error"
                };
                const leftVal = Number(leftResult.value), rightVal = Number(rightResult.value);
                let result = !1;
                if (isNaN(leftVal) || isNaN(rightVal)) {
                    const leftStr = String(leftResult.value), rightStr = String(rightResult.value);
                    switch (op) {
                      case ">=":
                        result = leftStr >= rightStr;
                        break;

                      case "<=":
                        result = leftStr <= rightStr;
                        break;

                      case "<>":
                        result = leftStr !== rightStr;
                        break;

                      case ">":
                        result = leftStr > rightStr;
                        break;

                      case "<":
                        result = leftStr < rightStr;
                        break;

                      case "=":
                        result = leftStr === rightStr;
                    }
                } else switch (op) {
                  case ">=":
                    result = leftVal >= rightVal;
                    break;

                  case "<=":
                    result = leftVal <= rightVal;
                    break;

                  case "<>":
                    result = leftVal !== rightVal;
                    break;

                  case ">":
                    result = leftVal > rightVal;
                    break;

                  case "<":
                    result = leftVal < rightVal;
                    break;

                  case "=":
                    result = leftVal === rightVal;
                }
                return {
                    value: result,
                    error: void 0
                };
            }
        }
        return null;
    }
    parseArithmetic(expr) {
        try {
            const functionResult = this.tryParseFunction(expr);
            if (functionResult) return functionResult;
            if (expr.startsWith('"') && expr.endsWith('"')) return {
                value: expr.slice(1, -1),
                error: void 0
            };
            if (/^([A-Za-z0-9_]+!)?[A-Z]+[0-9]+$/.test(expr)) return {
                value: this.getCellValueByA1(expr),
                error: void 0
            };
            if (/^-?\d+(\.\d+)?$/.test(expr)) return {
                value: Number(expr),
                error: void 0
            };
            if (/^([A-Za-z0-9_]+!)?[A-Z]+[0-9]+:[A-Z]+[0-9]+$/.test(expr)) {
                return {
                    value: this.getRangeValuesFromExpr(expr),
                    error: void 0
                };
            }
            return expr.includes("+") || expr.includes("-") || expr.includes("*") || expr.includes("/") ? this.evaluateArithmeticWithFunctions(expr) : {
                value: expr,
                error: void 0
            };
        } catch (error) {
            return {
                value: null,
                error: error instanceof Error ? error.message : "Arithmetic parsing failed"
            };
        }
    }
    tryParseFunction(expr) {
        const funcStartMatch = expr.match(/^([A-Z]+)\s*\(/i);
        if (!funcStartMatch) return null;
        const funcName = funcStartMatch[1].toUpperCase();
        let depth = 1, argsEnd = funcStartMatch[0].length - 1;
        for (let i = funcStartMatch[0].length; i < expr.length; i++) if ("(" === expr[i]) depth++; else if (")" === expr[i] && (depth--, 
        0 === depth)) {
            argsEnd = i;
            break;
        }
        if (0 !== depth) return null;
        if (argsEnd + 1 !== expr.length) return null;
        const argsStr = expr.substring(funcStartMatch[0].length, argsEnd), args = this.parseArguments(argsStr), argValues = [];
        for (const arg of args) {
            const result = this.parseExpression(arg);
            if (result.error) return {
                value: null,
                error: `Argument error in ${funcName}: ${result.error}`
            };
            argValues.push(result.value);
        }
        return this.executeFunction(funcName, argValues);
    }
    parseArguments(argsStr) {
        const args = [];
        let current = "", depth = 0, inQuotes = !1;
        for (let i = 0; i < argsStr.length; i++) {
            const char = argsStr[i];
            '"' !== char || 0 !== i && "\\" === argsStr[i - 1] || (inQuotes = !inQuotes), inQuotes || ("(" === char && depth++, 
            ")" === char && depth--, "," !== char || 0 !== depth) ? current += char : (args.push(current.trim()), 
            current = "");
        }
        if (current.trim() && args.push(current.trim()), 1 === args.length && args[0].includes("(") && args[0].includes(")")) {
            const singleArg = args[0], reArgs = [];
            let reCurrent = "", reDepth = 0, reInQuotes = !1;
            for (let i = 0; i < singleArg.length; i++) {
                const char = singleArg[i];
                '"' !== char || 0 !== i && "\\" === singleArg[i - 1] || (reInQuotes = !reInQuotes), 
                reInQuotes || ("(" === char && reDepth++, ")" === char && reDepth--, "," !== char || 0 !== reDepth) ? reCurrent += char : (reArgs.push(reCurrent.trim()), 
                reCurrent = "");
            }
            if (reCurrent.trim() && reArgs.push(reCurrent.trim()), reArgs.length > 1) return reArgs;
        }
        return args;
    }
    executeFunction(funcName, args) {
        try {
            switch (funcName) {
              case "SUM":
                return this.calculateSum(args);

              case "AVERAGE":
                return this.calculateAverage(args);

              case "MAX":
                return this.calculateMax(args);

              case "MIN":
                return this.calculateMin(args);

              case "ABS":
                return this.calculateAbs(args);

              case "ROUND":
                return this.calculateRound(args);

              case "INT":
                return this.calculateInt(args);

              case "RAND":
                return this.calculateRand(args);

              case "COUNT":
                return this.calculateCount(args);

              case "COUNTA":
                return this.calculateCountA(args);

              case "STDEV":
                return this.calculateStdev(args);

              case "VAR":
                return this.calculateVar(args);

              case "MEDIAN":
                return this.calculateMedian(args);

              case "PRODUCT":
                return this.calculateProduct(args);

              case "IF":
                return this.calculateIf(args);

              case "AND":
                return this.calculateAnd(args);

              case "OR":
                return this.calculateOr(args);

              case "NOT":
                return this.calculateNot(args);

              case "TODAY":
                return this.calculateToday(args);

              case "NOW":
                return this.calculateNow(args);

              default:
                return {
                    value: null,
                    error: `Unknown function: ${funcName}`
                };
            }
        } catch (error) {
            return {
                value: null,
                error: error instanceof Error ? error.message : "Function execution failed"
            };
        }
    }
    calculateSum(args) {
        let total = 0;
        for (const arg of this.flattenArgsWithRanges(args)) {
            const num = Number(arg);
            isNaN(num) || (total += num);
        }
        return {
            value: total,
            error: void 0
        };
    }
    calculateAverage(args) {
        const values = this.flattenArgsWithRanges(args).filter((arg => !isNaN(Number(arg))));
        if (0 === values.length) return {
            value: 0,
            error: void 0
        };
        const sum = values.reduce(((acc, val) => acc + Number(val)), 0);
        return {
            value: Number(sum) / Number(values.length),
            error: void 0
        };
    }
    calculateMax(args) {
        const values = this.flattenArgsWithRanges(args).filter((arg => !isNaN(Number(arg)))).map((arg => Number(arg)));
        return 0 === values.length ? {
            value: 0,
            error: void 0
        } : {
            value: Math.max(...values),
            error: void 0
        };
    }
    calculateMin(args) {
        const values = this.flattenArgsWithRanges(args).filter((arg => !isNaN(Number(arg)))).map((arg => Number(arg)));
        return 0 === values.length ? {
            value: 0,
            error: void 0
        } : {
            value: Math.min(...values),
            error: void 0
        };
    }
    calculateAbs(args) {
        if (1 !== args.length) return {
            value: null,
            error: "ABS requires exactly 1 argument"
        };
        const num = Number(args[0]);
        return isNaN(num) ? {
            value: null,
            error: "ABS argument must be a number"
        } : {
            value: Math.abs(num),
            error: void 0
        };
    }
    calculateRound(args) {
        if (args.length < 1 || args.length > 2) return {
            value: null,
            error: "ROUND requires 1 or 2 arguments"
        };
        const num = Number(args[0]);
        if (isNaN(num)) return {
            value: null,
            error: "ROUND argument must be a number"
        };
        const digits = 2 === args.length ? Number(args[1]) : 0;
        if (isNaN(digits)) return {
            value: null,
            error: "ROUND digits must be a number"
        };
        const factor = Math.pow(10, digits);
        return {
            value: Math.round(num * factor) / factor,
            error: void 0
        };
    }
    calculateInt(args) {
        if (1 !== args.length) return {
            value: null,
            error: "INT requires exactly 1 argument"
        };
        const num = Number(args[0]);
        return isNaN(num) ? {
            value: null,
            error: "INT argument must be a number"
        } : {
            value: Math.floor(num),
            error: void 0
        };
    }
    calculateRand(args) {
        return args.length > 0 ? {
            value: null,
            error: "RAND requires no arguments"
        } : {
            value: Math.random(),
            error: void 0
        };
    }
    calculateCount(args) {
        const values = this.flattenArgs(args);
        let count = 0;
        for (const value of values) if ("" !== value && null != value) {
            const num = Number(value);
            isNaN(num) || count++;
        }
        return {
            value: count,
            error: void 0
        };
    }
    calculateCountA(args) {
        const values = this.flattenArgs(args);
        let count = 0;
        for (const value of values) null != value && "" !== value && count++;
        return {
            value: count,
            error: void 0
        };
    }
    calculateStdev(args) {
        const values = this.flattenArgs(args).filter((arg => !isNaN(Number(arg)) && "" !== arg && null != arg)).map((arg => Number(arg)));
        if (values.length < 2) return {
            value: 0,
            error: void 0
        };
        const mean = values.reduce(((sum, val) => sum + val), 0) / values.length, variance = values.reduce(((sum, val) => sum + Math.pow(val - mean, 2)), 0) / (values.length - 1);
        return {
            value: Math.sqrt(variance),
            error: void 0
        };
    }
    calculateVar(args) {
        const values = this.flattenArgs(args).filter((arg => !isNaN(Number(arg)) && "" !== arg && null != arg)).map((arg => Number(arg)));
        if (values.length < 2) return {
            value: 0,
            error: void 0
        };
        const mean = values.reduce(((sum, val) => sum + val), 0) / values.length;
        return {
            value: values.reduce(((sum, val) => sum + Math.pow(val - mean, 2)), 0) / (values.length - 1),
            error: void 0
        };
    }
    calculateMedian(args) {
        const values = this.flattenArgs(args).filter((arg => !isNaN(Number(arg)) && "" !== arg && null != arg)).map((arg => Number(arg)));
        if (0 === values.length) return {
            value: 0,
            error: void 0
        };
        const sortedValues = values.sort(((a, b) => a - b));
        if (sortedValues.length % 2 == 0) {
            return {
                value: (sortedValues[sortedValues.length / 2 - 1] + sortedValues[sortedValues.length / 2]) / 2,
                error: void 0
            };
        }
        return {
            value: sortedValues[Math.floor(sortedValues.length / 2)],
            error: void 0
        };
    }
    calculateProduct(args) {
        let product = 1;
        const values = this.flattenArgs(args).filter((arg => !isNaN(Number(arg)) && "" !== arg && null != arg)).map((arg => Number(arg)));
        if (0 === values.length) return {
            value: 0,
            error: void 0
        };
        for (const value of values) product *= value;
        return {
            value: product,
            error: void 0
        };
    }
    calculateIf(args) {
        if (3 !== args.length) return {
            value: null,
            error: "IF requires exactly 3 arguments"
        };
        return {
            value: this.isTruthy(args[0]) ? args[1] : args[2],
            error: void 0
        };
    }
    calculateAnd(args) {
        if (args.length < 1) return {
            value: null,
            error: "AND requires at least 1 argument"
        };
        for (const arg of args) if (!this.isTruthy(arg)) return {
            value: !1,
            error: void 0
        };
        return {
            value: !0,
            error: void 0
        };
    }
    calculateOr(args) {
        if (args.length < 1) return {
            value: null,
            error: "OR requires at least 1 argument"
        };
        for (const arg of args) if (this.isTruthy(arg)) return {
            value: !0,
            error: void 0
        };
        return {
            value: !1,
            error: void 0
        };
    }
    calculateNot(args) {
        return 1 !== args.length ? {
            value: null,
            error: "NOT requires exactly 1 argument"
        } : {
            value: !this.isTruthy(args[0]),
            error: void 0
        };
    }
    calculateToday(args) {
        return args.length > 0 ? {
            value: null,
            error: "TODAY requires no arguments"
        } : {
            value: new Date,
            error: void 0
        };
    }
    calculateNow(args) {
        return args.length > 0 ? {
            value: null,
            error: "NOW requires no arguments"
        } : {
            value: new Date,
            error: void 0
        };
    }
    flattenArgs(args) {
        const result = [];
        for (const arg of args) Array.isArray(arg) ? result.push(...arg) : result.push(arg);
        return result;
    }
    flattenArgsWithRanges(args) {
        const result = [];
        for (const arg of args) if (Array.isArray(arg)) result.push(...arg); else if ("string" == typeof arg && arg.includes(":")) try {
            const rangeValues = this.getRangeValuesFromExpr(arg);
            result.push(...rangeValues);
        } catch (_a) {
            result.push(arg);
        } else result.push(arg);
        return result;
    }
    isTruthy(value) {
        if (null == value) return !1;
        if (!1 === value || 0 === value || "" === value) return !1;
        if (!0 === value) return !0;
        if ("string" == typeof value) {
            if ("false" === value.toLowerCase()) return !1;
            if ("true" === value.toLowerCase()) return !0;
        }
        const num = Number(value);
        return !!isNaN(num) || 0 !== num;
    }
    evaluateArithmeticWithFunctions(expr) {
        try {
            const functionMatches = [], functionRegex = /[A-Z]+\([^)]*\)/g;
            let match;
            for (;null !== (match = functionRegex.exec(expr)); ) functionMatches.push({
                match: match[0],
                start: match.index,
                end: match.index + match[0].length
            });
            let processedExpr = expr;
            const functionValues = [];
            for (const funcMatch of functionMatches) {
                const funcResult = this.parseExpression(funcMatch.match);
                if (funcResult.error) return {
                    value: null,
                    error: `Error in function ${funcMatch.match}: ${funcResult.error}`
                };
                functionValues.push(funcResult.value), processedExpr = processedExpr.replace(funcMatch.match, `__FUNC_${functionValues.length - 1}__`);
            }
            const cellRefs = processedExpr.match(/[A-Z]+[0-9]+/g) || [];
            for (const cellRef of cellRefs) {
                const value = this.getCellValueByA1(cellRef);
                processedExpr = processedExpr.replace(cellRef, String(value));
            }
            for (let i = 0; i < functionValues.length; i++) processedExpr = processedExpr.replace(`__FUNC_${i}__`, String(functionValues[i]));
            return {
                value: Function('"use strict"; return (' + processedExpr + ")")(),
                error: void 0
            };
        } catch (error) {
            return {
                value: null,
                error: "Basic arithmetic evaluation failed"
            };
        }
    }
    getCellValueByA1(a1Notation) {
        try {
            let sheetKey = this.activeSheetKey || this.reverseSheets.get(0) || "Sheet1", cellRef = a1Notation;
            if (a1Notation.includes("!")) {
                const parts = a1Notation.split("!");
                2 === parts.length && (sheetKey = parts[0], cellRef = parts[1]);
            }
            const {row: row, col: col} = this.parseA1Notation(cellRef), cell = {
                sheet: sheetKey,
                row: row,
                col: col
            };
            return this.getCellValue(cell).value;
        } catch (_a) {
            return 0;
        }
    }
    getRangeValuesFromExpr(expr) {
        try {
            if (!expr.includes(":")) return [ this.getCellValueByA1(expr) ];
            let sheetKey = this.activeSheetKey || this.reverseSheets.get(0) || "Sheet1", rangeExpr = expr;
            if (expr.includes("!")) {
                const parts = expr.split("!");
                2 === parts.length && (sheetKey = parts[0], rangeExpr = parts[1]);
            }
            const [start, end] = rangeExpr.split(":"), startCell = this.parseA1Notation(start.trim()), endCell = this.parseA1Notation(end.trim()), values = [];
            for (let row = startCell.row; row <= endCell.row; row++) for (let col = startCell.col; col <= endCell.col; col++) {
                const cell = {
                    sheet: sheetKey,
                    row: row,
                    col: col
                };
                values.push(this.getCellValue(cell).value);
            }
            return values;
        } catch (_a) {
            return [];
        }
    }
    getCellValue(cell) {
        try {
            const sheetId = this.sheets.get(cell.sheet);
            if (void 0 === sheetId) return {
                value: "",
                error: void 0
            };
            const sheet = this.sheetData.get(sheetId);
            if (!sheet || !sheet[cell.row] || void 0 === sheet[cell.row][cell.col] || null === sheet[cell.row][cell.col]) return {
                value: "",
                error: void 0
            };
            const value = sheet[cell.row][cell.col];
            if ("string" == typeof value && value.startsWith("=")) {
                return this.calculateFormula(value);
            }
            return value instanceof FormulaError ? {
                value: null,
                error: value.message
            } : {
                value: value,
                error: void 0
            };
        } catch (error) {
            return {
                value: null,
                error: error instanceof Error ? error.message : "Unknown error"
            };
        }
    }
    getCellFormula(cell) {
        const cellKey = this.getCellKey(cell);
        return this.formulaCells.get(cellKey);
    }
    isCellFormula(cell) {
        const cellKey = this.getCellKey(cell);
        return this.formulaCells.has(cellKey);
    }
    getCellDependents(cell) {
        const cellKey = this.getCellKey(cell), dependents = this.dependents.get(cellKey);
        if (!dependents) return [];
        const result = [];
        for (const dependent of dependents) {
            const parsed = this.parseCellKey(dependent);
            parsed && result.push(parsed);
        }
        return result;
    }
    getCellPrecedents(cell) {
        const cellKey = this.getCellKey(cell), dependencies = this.dependencies.get(cellKey);
        if (!dependencies) return [];
        const result = [];
        for (const dependency of dependencies) {
            const parsed = this.parseCellKey(dependency);
            parsed && result.push(parsed);
        }
        return result;
    }
    getAvailableFunctions() {
        return [ ...formula_helper_1.supportedFunctions ];
    }
    validateFormula(formula) {
        try {
            if (!formula.startsWith("=")) return {
                isValid: !0
            };
            const expression = formula.substring(1).trim();
            return expression ? (this.validateExpressionStructure(expression), this.validateFunctionNames(expression), 
            {
                isValid: !0
            }) : {
                isValid: !1,
                error: "Empty expression"
            };
        } catch (error) {
            return {
                isValid: !1,
                error: error instanceof Error ? error.message : "Invalid formula syntax"
            };
        }
    }
    validateFunctionNames(expr) {
        const functionPattern = /([A-Z]+)\s*\(/gi;
        let match;
        for (;null !== (match = functionPattern.exec(expr)); ) {
            const funcName = match[1].toUpperCase();
            if (!formula_helper_1.supportedFunctions.includes(funcName)) throw new Error(`Unknown function: ${funcName}`);
        }
    }
    validateExpressionStructure(expr) {
        if ((expr.match(/\(/g) || []).length !== (expr.match(/\)/g) || []).length) throw new Error("Unmatched parentheses");
        if ((expr.match(/"/g) || []).length % 2 != 0) throw new Error("Unmatched quotes");
        if (expr.match(/[+\-*/^&%<>=]$/)) throw new Error("Expression ends with operator");
        if (expr.match(/,,/)) throw new Error("Consecutive commas");
        if (expr.match(/\([^)]*,\s*\)/) || expr.match(/,\s*\)/) || expr.match(/\(\s*\)/)) throw new Error("Invalid function arguments");
        if (expr.match(/[+\-*/^&%][+\-*/^&%]/)) throw new Error("Basic arithmetic evaluation failed");
    }
    release() {
        this.sheets.clear(), this.reverseSheets.clear(), this.sheetData.clear(), this.formulaCells.clear(), 
        this.dependencies.clear(), this.dependents.clear(), this.nextSheetId = 0;
    }
    exportFormulas(sheetKey) {
        const result = {};
        if (void 0 === this.sheets.get(sheetKey)) return result;
        for (const entry of Array.from(this.formulaCells.entries())) {
            const [cellKey, formula] = entry, cell = this.parseCellKey(cellKey);
            if (cell && cell.sheet === sheetKey) {
                result[this.getA1Notation(cell.row, cell.col)] = formula;
            }
        }
        return result;
    }
    getAllSheets() {
        const result = [];
        for (const entry of Array.from(this.sheets.entries())) {
            const [sheetKey, sheetId] = entry;
            result.push({
                key: sheetKey,
                id: sheetId,
                name: sheetKey
            });
        }
        return result;
    }
    sortFormulasByDependency(sheetKey, formulas) {
        try {
            const tempDependencies = new Map, tempDependents = new Map;
            for (const [cellRef, formula] of Object.entries(formulas)) {
                const cellKey = `${sheetKey}!${cellRef}`, dependencies = this.extractCellReferences(formula, sheetKey);
                tempDependencies.set(cellKey, new Set(dependencies));
                for (const dep of dependencies) {
                    const depDependents = tempDependents.get(dep) || new Set;
                    depDependents.add(cellKey), tempDependents.set(dep, depDependents);
                }
            }
            const visited = new Set, tempVisited = new Set, result = [], visit = cellKey => {
                if (visited.has(cellKey)) return;
                if (tempVisited.has(cellKey)) return;
                tempVisited.add(cellKey);
                const deps = tempDependencies.get(cellKey) || new Set;
                for (const dep of deps) tempDependencies.has(dep) && visit(dep);
                tempVisited.delete(cellKey), visited.add(cellKey);
                const cellRef = cellKey.substring(sheetKey.length + 1), formula = formulas[cellRef];
                formula && result.push([ cellRef, formula ]);
            };
            for (const cellKey of tempDependencies.keys()) visited.has(cellKey) || visit(cellKey);
            return result;
        } catch (error) {
            return Object.entries(formulas);
        }
    }
    removeSheet(sheetKey) {
        const sheetId = this.sheets.get(sheetKey);
        if (void 0 === sheetId) return;
        if (this.sheets.size <= 1) throw new Error("Cannot remove the last sheet");
        this.sheetData.delete(sheetId), this.sheets.delete(sheetKey), this.reverseSheets.delete(sheetId);
        const keysToRemove = [];
        for (const entry of Array.from(this.formulaCells.entries())) {
            const [cellKey] = entry;
            cellKey.startsWith(`${sheetKey}!`) && keysToRemove.push(cellKey);
        }
        for (const cellKey of keysToRemove) this.formulaCells.delete(cellKey), this.dependencies.delete(cellKey), 
        this.dependents.delete(cellKey);
        for (const entry of Array.from(this.dependencies.entries())) {
            const [cellKey, deps] = entry, newDeps = new Set;
            for (const dep of deps) dep.startsWith(`${sheetKey}!`) || newDeps.add(dep);
            0 === newDeps.size ? this.dependencies.delete(cellKey) : this.dependencies.set(cellKey, newDeps);
        }
        for (const entry of Array.from(this.dependents.entries())) {
            const [cellKey, dependents] = entry, newDependents = new Set;
            for (const dep of dependents) dep.startsWith(`${sheetKey}!`) || newDependents.add(dep);
            0 === newDependents.size ? this.dependents.delete(cellKey) : this.dependents.set(cellKey, newDependents);
        }
    }
    renameSheet(oldKey, newKey) {
        const sheetId = this.sheets.get(oldKey);
        if (void 0 === sheetId) throw new Error(`Sheet not found: ${oldKey}`);
        if (this.sheets.has(newKey)) throw new Error(`Sheet already exists: ${newKey}`);
        this.sheets.delete(oldKey), this.sheets.set(newKey, sheetId), this.reverseSheets.set(sheetId, newKey);
        const formulaEntriesToUpdate = [];
        for (const entry of Array.from(this.formulaCells.entries())) {
            const [cellKey, formula] = entry;
            if (cellKey.startsWith(`${oldKey}!`)) {
                const newCellKey = cellKey.replace(`${oldKey}!`, `${newKey}!`);
                formulaEntriesToUpdate.push([ newCellKey, formula ]), this.formulaCells.delete(cellKey);
            }
        }
        for (const [newCellKey, formula] of formulaEntriesToUpdate) this.formulaCells.set(newCellKey, formula);
        const dependencyEntriesToUpdate = [];
        for (const entry of Array.from(this.dependencies.entries())) {
            const [cellKey, deps] = entry;
            let newCellKey = cellKey;
            const newDeps = new Set;
            cellKey.startsWith(`${oldKey}!`) && (newCellKey = cellKey.replace(`${oldKey}!`, `${newKey}!`));
            for (const dep of deps) dep.startsWith(`${oldKey}!`) ? newDeps.add(dep.replace(`${oldKey}!`, `${newKey}!`)) : newDeps.add(dep);
            newCellKey === cellKey && this.areSetsEqual(newDeps, deps) || (dependencyEntriesToUpdate.push([ newCellKey, newDeps ]), 
            this.dependencies.delete(cellKey));
        }
        for (const [newCellKey, newDeps] of dependencyEntriesToUpdate) this.dependencies.set(newCellKey, newDeps);
        const dependentEntriesToUpdate = [];
        for (const entry of Array.from(this.dependents.entries())) {
            const [cellKey, dependents] = entry;
            let newCellKey = cellKey;
            const newDependents = new Set;
            cellKey.startsWith(`${oldKey}!`) && (newCellKey = cellKey.replace(`${oldKey}!`, `${newKey}!`));
            for (const dep of dependents) dep.startsWith(`${oldKey}!`) ? newDependents.add(dep.replace(`${oldKey}!`, `${newKey}!`)) : newDependents.add(dep);
            newCellKey === cellKey && this.areSetsEqual(newDependents, dependents) || (dependentEntriesToUpdate.push([ newCellKey, newDependents ]), 
            this.dependents.delete(cellKey));
        }
        for (const [newCellKey, newDependents] of dependentEntriesToUpdate) this.dependents.set(newCellKey, newDependents);
    }
    areSetsEqual(set1, set2) {
        if (set1.size !== set2.size) return !1;
        for (const item of set1) if (!set2.has(item)) return !1;
        return !0;
    }
    updateDependencies(cellKey, formula) {
        var _a;
        const oldDeps = this.dependencies.get(cellKey) || new Set;
        for (const dep of oldDeps) {
            const depDependents = this.dependents.get(dep) || new Set;
            depDependents.delete(cellKey), 0 === depDependents.size ? this.dependents.delete(dep) : this.dependents.set(dep, depDependents);
        }
        const currentSheet = (null === (_a = this.parseCellKey(cellKey)) || void 0 === _a ? void 0 : _a.sheet) || "Sheet1", dependencies = this.extractCellReferences(formula, currentSheet);
        this.dependencies.set(cellKey, new Set(dependencies));
        for (const dep of dependencies) {
            const depDependents = this.dependents.get(dep) || new Set;
            depDependents.add(cellKey), this.dependents.set(dep, depDependents);
        }
    }
    extractCellReferences(formula, currentSheet = "Sheet1") {
        if (!formula || "string" != typeof formula || !formula.startsWith("=")) return [];
        const expression = formula.substring(1).trim(), references = [];
        try {
            this.extractReferencesFromExpression(expression, references, currentSheet);
        } catch (error) {}
        return [ ...new Set(references) ];
    }
    extractReferencesFromExpression(expr, references, currentSheet = "Sheet1") {
        let cleanExpr = expr.replace(/"[^"]*"/g, "");
        cleanExpr = cleanExpr.replace(/'[^']*'/g, "");
        const cellRefPattern = /(?:([A-Za-z0-9_]+)!)?([A-Z]+[0-9]+)(?::([A-Z]+[0-9]+))?/g;
        let match;
        for (;null !== (match = cellRefPattern.exec(cleanExpr)); ) {
            const sheetName = match[1] || currentSheet, startCell = match[2], endCell = match[3];
            if (endCell) {
                const expandedRefs = this.expandRangeToCells(sheetName, startCell, endCell);
                references.push(...expandedRefs);
            } else references.push(`${sheetName}!${startCell}`);
        }
        const singleMatches = cleanExpr.match(/[A-Z]+[0-9]+/g) || [];
        for (const match of singleMatches) references.some((ref => ref.endsWith(match))) || references.push(`${currentSheet}!${match}`);
    }
    expandRangeToCells(sheetName, startCell, endCell) {
        try {
            const start = this.parseA1Notation(startCell), end = this.parseA1Notation(endCell), cells = [], minRow = Math.min(start.row, end.row), maxRow = Math.max(start.row, end.row), minCol = Math.min(start.col, end.col), maxCol = Math.max(start.col, end.col);
            for (let row = minRow; row <= maxRow; row++) for (let col = minCol; col <= maxCol; col++) cells.push(`${sheetName}!${this.getA1Notation(row, col)}`);
            return cells;
        } catch (_a) {
            return [ `${sheetName}!${startCell}:${endCell}` ];
        }
    }
    recalculateDependents(changedCell) {
        this.recalculateDependentsWithTracking(changedCell, new Set);
    }
    recalculateDependentsWithTracking(changedCell, visited) {
        const cellKey = this.getCellKey(changedCell);
        if (visited.has(cellKey)) return;
        const dependents = this.dependents.get(cellKey);
        if (!dependents || 0 === dependents.size) return;
        const sortedDependents = this.sortCellsByDependency([ ...dependents ]);
        for (const dependentKey of sortedDependents) this.recalculateSingleCellWithTracking(dependentKey, visited);
    }
    recalculateSingleCellWithTracking(cellKey, visited) {
        if (visited.has(cellKey)) return;
        const formula = this.formulaCells.get(cellKey);
        if (!formula) return;
        const cell = this.parseCellKey(cellKey);
        if (!cell) return;
        const result = this.calculateFormula(formula);
        if (result.error) return;
        const sheetId = this.sheets.get(cell.sheet);
        if (void 0 === sheetId) return;
        const sheet = this.sheetData.get(sheetId);
        if (!sheet || !sheet[cell.row] || void 0 === sheet[cell.row][cell.col]) return;
        const oldValue = sheet[cell.row][cell.col], newValue = result.value;
        sheet[cell.row][cell.col] = newValue, oldValue !== newValue && (visited.add(cellKey), 
        this.recalculateDependentsWithTracking(cell, visited), visited.delete(cellKey));
    }
    sortCellsByDependency(cells) {
        const visited = new Set, tempVisited = new Set, result = [], visit = cell => {
            if (visited.has(cell)) return;
            if (tempVisited.has(cell)) return;
            tempVisited.add(cell);
            const deps = this.dependencies.get(cell) || new Set;
            for (const dep of deps) cells.includes(dep) && visit(dep);
            tempVisited.delete(cell), visited.add(cell), result.push(cell);
        };
        for (const cell of cells) visited.has(cell) || visit(cell);
        return result;
    }
    adjustFormulaReferences(sheetKey, type, dimension, index, count, totalColCount, totalRowCount) {
        try {
            const adjustedFormulas = [], movedFormulas = [], deletedCells = new Set;
            if ("delete" === type) for (let i = 0; i < count; i++) if ("row" === dimension) for (let col = 0; col < totalColCount; col++) {
                const deletedCell = {
                    sheet: sheetKey,
                    row: index + i,
                    col: col
                }, deletedCellKey = this.getCellKey(deletedCell);
                deletedCells.add(deletedCellKey);
            } else if ("column" === dimension) for (let row = 0; row < totalRowCount; row++) {
                const deletedCell = {
                    sheet: sheetKey,
                    row: row,
                    col: index + i
                }, deletedCellKey = this.getCellKey(deletedCell);
                deletedCells.add(deletedCellKey);
            }
            const entries = Array.from(this.formulaCells.entries());
            for (const [cellKey, formula] of entries) {
                const cell = this.parseCellKey(cellKey);
                if (!cell || cell.sheet !== sheetKey) continue;
                const newCell = Object.assign({}, cell);
                let cellNeedsMove = !1;
                if ("row" === dimension) {
                    if ("insert" === type && cell.row >= index) newCell.row = cell.row + count, cellNeedsMove = !0; else if ("delete" === type && cell.row > index) newCell.row = cell.row - count, 
                    cellNeedsMove = !0; else if ("delete" === type && cell.row >= index && cell.row < index + count) {
                        this.formulaCells.delete(cellKey);
                        continue;
                    }
                } else if ("column" === dimension) if ("insert" === type && cell.col >= index) newCell.col = cell.col + count, 
                cellNeedsMove = !0; else if ("delete" === type && cell.col > index) newCell.col = cell.col - count, 
                cellNeedsMove = !0; else if ("delete" === type && cell.col >= index && cell.col < index + count) {
                    this.formulaCells.delete(cellKey);
                    continue;
                }
                const newFormula = this.adjustFormulaReference(formula, type, dimension, index, count);
                cellNeedsMove ? (movedFormulas.push({
                    oldCellKey: cellKey,
                    newCell: newCell,
                    formula: newFormula
                }), this.formulaCells.delete(cellKey), this.clearDependencies(cellKey)) : newFormula !== formula && (adjustedFormulas.push({
                    cell: cell,
                    oldFormula: formula,
                    newFormula: newFormula
                }), this.clearDependencies(cellKey));
            }
            for (const {newCell: newCell, formula: formula} of movedFormulas) {
                const newCellKey = this.getCellKey(newCell);
                this.formulaCells.set(newCellKey, formula), this.updateDependencies(newCellKey, formula), 
                this.setCellContentWithoutDependencyUpdate(newCell, formula);
            }
            for (const {cell: cell, newFormula: newFormula} of adjustedFormulas) {
                const cellKey = this.getCellKey(cell);
                this.formulaCells.set(cellKey, newFormula), this.updateDependencies(cellKey, newFormula), 
                this.setCellContentWithoutDependencyUpdate(cell, newFormula);
            }
            adjustedFormulas.length, movedFormulas.length;
            const adjustedCells = adjustedFormulas.map((item => item.cell));
            return {
                adjustedCells: adjustedCells,
                movedCells: movedFormulas.map((item => item.newCell))
            };
        } catch (error) {
            return {
                adjustedCells: [],
                movedCells: []
            };
        }
    }
    adjustFormulaReference(formula, type, dimension, index, count) {
        if (!formula || !formula.startsWith("=")) return formula;
        const expression = formula.substring(1), cellRefRegex = /([A-Z]+)([0-9]+)/g, rangeRefRegex = /([A-Z]+[0-9]+):([A-Z]+[0-9]+)/g;
        let match, newExpression = expression;
        const replacements = [];
        for (;null !== (match = rangeRefRegex.exec(expression)); ) {
            const fullRangeMatch = match[0], startCell = match[1], endCell = match[2];
            if ("delete" === type) {
                if (this.rangeContainsDeletedCells(startCell, endCell, dimension, index, count)) {
                    const newRange = this.adjustRangeForDeletion(startCell, endCell, dimension, index, count);
                    newRange !== fullRangeMatch && replacements.push({
                        start: match.index,
                        end: match.index + fullRangeMatch.length,
                        replacement: newRange
                    });
                }
            }
        }
        for (cellRefRegex.lastIndex = 0; null !== (match = cellRefRegex.exec(expression)); ) {
            const fullMatch = match[0], colLetters = match[1], rowNumber = parseInt(match[2], 10), currentMatchIndex = match.index;
            if (replacements.some((replacement => currentMatchIndex >= replacement.start && currentMatchIndex < replacement.end))) continue;
            let needsAdjustment = !1, newRowNumber = rowNumber, newColLetters = colLetters;
            if ("row" === dimension) {
                const zeroBasedRowNumber = rowNumber - 1;
                "insert" === type && zeroBasedRowNumber >= index ? (newRowNumber = rowNumber + count, 
                needsAdjustment = !0) : "delete" === type && zeroBasedRowNumber >= index && (zeroBasedRowNumber >= index + count ? (newRowNumber = rowNumber - count, 
                needsAdjustment = !0) : zeroBasedRowNumber >= index && zeroBasedRowNumber < index + count && (newRowNumber = index, 
                needsAdjustment = !0));
            } else if ("column" === dimension) {
                const colIndex = this.columnLettersToIndex(colLetters);
                "insert" === type && colIndex >= index ? (newColLetters = this.indexToColumnLetters(colIndex + count), 
                needsAdjustment = !0) : "delete" === type && colIndex >= index && (colIndex >= index + count ? (newColLetters = this.indexToColumnLetters(colIndex - count), 
                needsAdjustment = !0) : colIndex >= index && colIndex < index + count && (newColLetters = this.indexToColumnLetters(index), 
                needsAdjustment = !0));
            }
            if (needsAdjustment) {
                let replacement;
                replacement = "row" === dimension && newRowNumber === index || "column" === dimension && this.indexToColumnLetters(this.columnLettersToIndex(newColLetters)) === this.indexToColumnLetters(index) ? "#REF!" : newColLetters + newRowNumber, 
                replacements.push({
                    start: match.index,
                    end: match.index + fullMatch.length,
                    replacement: replacement
                });
            }
        }
        replacements.sort(((a, b) => b.start - a.start));
        for (const {start: start, end: end, replacement: replacement} of replacements) newExpression = newExpression.substring(0, start) + replacement + newExpression.substring(end);
        return "=" + newExpression;
    }
    columnLettersToIndex(letters) {
        let index = 0;
        for (let i = 0; i < letters.length; i++) index = 26 * index + (letters.charCodeAt(i) - 64);
        return index - 1;
    }
    indexToColumnLetters(index) {
        let letters = "";
        do {
            letters = String.fromCharCode(65 + index % 26) + letters, index = Math.floor(index / 26) - 1;
        } while (index >= 0);
        return letters;
    }
    rangeContainsDeletedCells(startCell, endCell, dimension, index, count) {
        try {
            const start = this.parseA1Notation(startCell), end = this.parseA1Notation(endCell), minRow = Math.min(start.row, end.row), maxRow = Math.max(start.row, end.row), minCol = Math.min(start.col, end.col), maxCol = Math.max(start.col, end.col);
            if ("row" === dimension) {
                if (index + count - 1 >= minRow && index <= maxRow) return !0;
            } else if ("column" === dimension) {
                if (index + count - 1 >= minCol && index <= maxCol) return !0;
            }
            return !1;
        } catch (_a) {
            return !1;
        }
    }
    adjustRangeForDeletion(startCell, endCell, dimension, index, count) {
        try {
            const start = this.parseA1Notation(startCell), end = this.parseA1Notation(endCell), minRow = Math.min(start.row, end.row), maxRow = Math.max(start.row, end.row), minCol = Math.min(start.col, end.col), maxCol = Math.max(start.col, end.col);
            let newStartCell, newEndCell, newMinRow = minRow, newMaxRow = maxRow, newMinCol = minCol, newMaxCol = maxCol;
            if ("row" === dimension) {
                const deleteEndRow = index + count - 1;
                if (minRow >= index && maxRow <= deleteEndRow) return "#REF!";
                if (minRow >= index && minRow <= deleteEndRow ? newMinRow = index : minRow > deleteEndRow && (newMinRow = minRow - count, 
                newMinRow < 0 && (newMinRow = 0)), maxRow >= index && maxRow <= deleteEndRow ? newMaxRow = index > 0 ? index - 1 : 0 : maxRow > deleteEndRow && (newMaxRow = maxRow - count, 
                newMaxRow < 0 && (newMaxRow = 0)), newMinRow > newMaxRow) return "#REF!";
            } else if ("column" === dimension) {
                const deleteEndCol = index + count - 1;
                if (minCol >= index && maxCol <= deleteEndCol) return "#REF!";
                if (minCol >= index && minCol <= deleteEndCol ? newMinCol = index > 0 ? index - 1 : 0 : minCol > deleteEndCol && (newMinCol = minCol - count, 
                newMinCol < 0 && (newMinCol = 0)), maxCol >= index && maxCol <= deleteEndCol ? newMaxCol = index > 0 ? index - 1 : 0 : maxCol > deleteEndCol && (newMaxCol = maxCol - count, 
                newMaxCol < 0 && (newMaxCol = 0)), newMinCol > newMaxCol) return "#REF!";
                if (-1 === newMinCol || -1 === newMaxCol) return "#REF!";
            }
            return "row" === dimension && -1 === newMinRow && -1 === newMaxRow || "column" === dimension && -1 === newMinCol && -1 === newMaxCol ? "#REF!" : ("row" === dimension ? (newStartCell = -1 === newMinRow ? "#REF!" : this.getA1Notation(newMinRow, newMinCol), 
            newEndCell = -1 === newMaxRow ? "#REF!" : this.getA1Notation(newMaxRow, newMaxCol)) : (newStartCell = -1 === newMinCol ? "#REF!" : this.getA1Notation(newMinRow, newMinCol), 
            newEndCell = -1 === newMaxCol ? "#REF!" : this.getA1Notation(newMaxRow, newMaxCol)), 
            newStartCell === newEndCell ? newStartCell : `${newStartCell}:${newEndCell}`);
        } catch (_a) {
            return "#REF!";
        }
    }
    adjustFormulaReferencesForColumnMove(sheetKey, sourceCol, targetCol, totalColCount, totalRowCount) {
        try {
            const adjustedFormulas = [], movedFormulas = [], isMovingForward = targetCol > sourceCol, columnMapping = new Map;
            if (isMovingForward) {
                for (let col = sourceCol + 1; col <= targetCol; col++) columnMapping.set(col, col - 1);
                columnMapping.set(sourceCol, targetCol);
            } else {
                for (let col = targetCol; col < sourceCol; col++) columnMapping.set(col, col + 1);
                columnMapping.set(sourceCol, targetCol);
            }
            for (const [cellKey, formula] of Array.from(this.formulaCells.entries())) {
                const cell = this.parseCellKey(cellKey);
                if (!cell || cell.sheet !== sheetKey) continue;
                let needsCellMove = !1;
                const newCellLocation = Object.assign({}, cell);
                if (columnMapping.has(cell.col)) {
                    const mappedCol = columnMapping.get(cell.col);
                    void 0 !== mappedCol && (newCellLocation.col = mappedCol, needsCellMove = !0);
                }
                const newFormula = this.adjustFormulaWithColumnMapping(formula, columnMapping, sourceCol, targetCol);
                (needsCellMove || newFormula !== formula) && (needsCellMove ? (movedFormulas.push({
                    oldCellKey: cellKey,
                    newCell: newCellLocation,
                    formula: newFormula
                }), this.formulaCells.delete(cellKey), this.clearDependencies(cellKey)) : (adjustedFormulas.push({
                    cell: cell,
                    oldFormula: formula,
                    newFormula: newFormula
                }), this.clearDependencies(cellKey)));
            }
            for (const {newCell: newCell, formula: formula} of movedFormulas) {
                const newCellKey = this.getCellKey(newCell);
                this.formulaCells.set(newCellKey, formula), this.updateDependencies(newCellKey, formula), 
                this.setCellContentWithoutDependencyUpdate(newCell, formula);
            }
            for (const {cell: cell, newFormula: newFormula} of adjustedFormulas) {
                const cellKey = this.getCellKey(cell);
                this.formulaCells.set(cellKey, newFormula), this.updateDependencies(cellKey, newFormula), 
                this.setCellContentWithoutDependencyUpdate(cell, newFormula);
            }
            const adjustedCells = adjustedFormulas.map((item => item.cell));
            return {
                adjustedCells: adjustedCells,
                movedCells: movedFormulas.map((item => item.newCell))
            };
        } catch (error) {
            return {
                adjustedCells: [],
                movedCells: []
            };
        }
    }
    adjustFormulaWithColumnMapping(formula, columnMapping, sourceCol, targetCol) {
        if (!formula || !formula.startsWith("=")) return formula;
        let expression = formula.substring(1);
        const rangeRegex = /([A-Z]+)([0-9]+):([A-Z]+)([0-9]+)/g;
        let match;
        const rangeReplacements = [], processedRanges = [];
        for (;null !== (match = rangeRegex.exec(expression)); ) {
            const fullMatch = match[0], startCol = match[1], startRow = match[2], endCol = match[3], endRow = match[4], startColIndex = this.columnLettersToIndex(startCol), endColIndex = this.columnLettersToIndex(endCol);
            let newStartCol = startColIndex, newEndCol = endColIndex;
            if (startColIndex <= sourceCol && sourceCol <= endColIndex) newStartCol = startColIndex, 
            newEndCol = endColIndex; else {
                if (columnMapping.has(startColIndex)) {
                    const mappedStartCol = columnMapping.get(startColIndex);
                    void 0 !== mappedStartCol && (newStartCol = mappedStartCol);
                }
                if (columnMapping.has(endColIndex)) {
                    const mappedEndCol = columnMapping.get(endColIndex);
                    void 0 !== mappedEndCol && (newEndCol = mappedEndCol);
                }
            }
            newStartCol > newEndCol && ([newStartCol, newEndCol] = [ newEndCol, newStartCol ]);
            const replacement = `${this.indexToColumnLetters(newStartCol)}${startRow}:${this.indexToColumnLetters(newEndCol)}${endRow}`;
            rangeReplacements.push({
                start: match.index,
                end: match.index + fullMatch.length,
                replacement: replacement
            }), processedRanges.push({
                start: match.index,
                end: match.index + fullMatch.length
            });
        }
        for (let i = rangeReplacements.length - 1; i >= 0; i--) {
            const {start: start, end: end, replacement: replacement} = rangeReplacements[i];
            expression = expression.substring(0, start) + replacement + expression.substring(end);
        }
        const cellRegex = /([A-Z]+)([0-9]+)/g, cellReplacements = [];
        for (;null !== (match = cellRegex.exec(expression)); ) {
            const fullMatch = match[0], colLetters = match[1], row = match[2], colIndex = this.columnLettersToIndex(colLetters), matchIndex = match.index;
            if (!processedRanges.some((range => matchIndex >= range.start && matchIndex < range.end)) && columnMapping.has(colIndex)) {
                const newColIndex = columnMapping.get(colIndex);
                if (void 0 !== newColIndex) {
                    const replacement = `${this.indexToColumnLetters(newColIndex)}${row}`;
                    cellReplacements.push({
                        start: match.index,
                        end: match.index + fullMatch.length,
                        replacement: replacement
                    });
                }
            }
        }
        for (let i = cellReplacements.length - 1; i >= 0; i--) {
            const {start: start, end: end, replacement: replacement} = cellReplacements[i];
            expression = expression.substring(0, start) + replacement + expression.substring(end);
        }
        return "=" + expression;
    }
    adjustFormulaReferencesForRowMove(sheetKey, sourceRow, targetRow) {
        try {
            if (sourceRow < 0 || targetRow < 0) return {
                adjustedCells: [],
                movedCells: []
            };
            const adjustedFormulas = [], movedFormulas = [], isMovingForward = targetRow > sourceRow, rowMapping = new Map;
            if (isMovingForward) {
                for (let row = sourceRow + 1; row <= targetRow; row++) rowMapping.set(row, row - 1);
                rowMapping.set(sourceRow, targetRow);
            } else {
                for (let row = targetRow; row < sourceRow; row++) rowMapping.set(row, row + 1);
                rowMapping.set(sourceRow, targetRow);
            }
            for (const [cellKey, formula] of Array.from(this.formulaCells.entries())) {
                const cell = this.parseCellKey(cellKey);
                if (!cell || cell.sheet !== sheetKey) continue;
                let needsCellMove = !1;
                const newCellLocation = Object.assign({}, cell);
                if (rowMapping.has(cell.row)) {
                    const mappedRow = rowMapping.get(cell.row);
                    void 0 !== mappedRow && mappedRow !== cell.row && (newCellLocation.row = mappedRow, 
                    needsCellMove = !0);
                }
                const newFormula = this.adjustFormulaWithRowMapping(formula, rowMapping);
                (needsCellMove || newFormula !== formula) && (needsCellMove ? (movedFormulas.push({
                    oldCellKey: cellKey,
                    newCell: newCellLocation,
                    formula: newFormula
                }), this.formulaCells.delete(cellKey), this.clearDependencies(cellKey)) : (adjustedFormulas.push({
                    cell: cell,
                    oldFormula: formula,
                    newFormula: newFormula
                }), this.clearDependencies(cellKey)));
            }
            for (const {newCell: newCell, formula: formula} of movedFormulas) {
                const newCellKey = this.getCellKey(newCell);
                this.formulaCells.set(newCellKey, formula), this.updateDependencies(newCellKey, formula), 
                this.setCellContentWithoutDependencyUpdate(newCell, formula);
            }
            for (const {cell: cell, newFormula: newFormula} of adjustedFormulas) {
                const cellKey = this.getCellKey(cell);
                this.formulaCells.set(cellKey, newFormula), this.updateDependencies(cellKey, newFormula), 
                this.setCellContentWithoutDependencyUpdate(cell, newFormula);
            }
            const adjustedCells = adjustedFormulas.map((item => item.cell));
            return {
                adjustedCells: adjustedCells,
                movedCells: movedFormulas.map((item => item.newCell))
            };
        } catch (error) {
            return {
                adjustedCells: [],
                movedCells: []
            };
        }
    }
    adjustFormulaWithRowMapping(formula, rowMapping) {
        if (!formula || !formula.startsWith("=")) return formula;
        let expression = formula.substring(1);
        const rangeRegex = /([A-Z]+)([0-9]+):([A-Z]+)([0-9]+)/g;
        let match;
        const rangeReplacements = [], processedRanges = [];
        for (;null !== (match = rangeRegex.exec(expression)); ) {
            const fullMatch = match[0], startCol = match[1], startRow = match[2], endCol = match[3], endRow = match[4], startRowIndex = parseInt(startRow, 10) - 1, endRowIndex = parseInt(endRow, 10) - 1;
            let newStartRow = startRowIndex, newEndRow = endRowIndex;
            if (rowMapping.has(startRowIndex)) {
                const mappedStartRow = rowMapping.get(startRowIndex);
                void 0 !== mappedStartRow && (newStartRow = mappedStartRow);
            }
            if (rowMapping.has(endRowIndex)) {
                const mappedEndRow = rowMapping.get(endRowIndex);
                void 0 !== mappedEndRow && (newEndRow = mappedEndRow);
            }
            if (newStartRow !== startRowIndex || newEndRow !== endRowIndex) {
                const replacement = `${startCol}${newStartRow + 1}:${endCol}${newEndRow + 1}`;
                rangeReplacements.push({
                    start: match.index,
                    end: match.index + fullMatch.length,
                    replacement: replacement
                }), processedRanges.push({
                    start: match.index,
                    end: match.index + fullMatch.length
                });
            }
        }
        for (let i = rangeReplacements.length - 1; i >= 0; i--) {
            const {start: start, end: end, replacement: replacement} = rangeReplacements[i];
            expression = expression.substring(0, start) + replacement + expression.substring(end);
        }
        const cellRegex = /([A-Z]+)([0-9]+)/g, cellReplacements = [];
        for (;null !== (match = cellRegex.exec(expression)); ) {
            const fullMatch = match[0], colLetters = match[1], row = match[2], rowIndex = parseInt(row, 10) - 1, matchIndex = match.index;
            if (!processedRanges.some((range => matchIndex >= range.start && matchIndex < range.end)) && rowMapping.has(rowIndex)) {
                const newRowIndex = rowMapping.get(rowIndex);
                if (void 0 !== newRowIndex) {
                    const replacement = `${colLetters}${newRowIndex + 1}`;
                    cellReplacements.push({
                        start: match.index,
                        end: match.index + fullMatch.length,
                        replacement: replacement
                    });
                }
            }
        }
        for (let i = cellReplacements.length - 1; i >= 0; i--) {
            const {start: start, end: end, replacement: replacement} = cellReplacements[i];
            expression = expression.substring(0, start) + replacement + expression.substring(end);
        }
        return "=" + expression;
    }
}

exports.FormulaEngine = FormulaEngine;

class FormulaError {
    constructor(message, type = "VALUE") {
        this.message = message, this.type = type;
    }
}
//# sourceMappingURL=formula-engine.js.map