import { ErrorCode, ValueTag } from '@bilig/protocol';
import { parseArithmeticScalarText } from '@bilig/formula';
import { directScalarCellNumber, directScalarValueNumber } from './direct-scalar-helpers.js';
export function createOperationDirectFormulaValues(args) {
    const workbookDateSystem = () => args.state.workbook.getCalculationSettings().dateSystem ?? '1900';
    const readDirectScalarCellNumber = (cellIndex) => directScalarValueNumber(args.state.workbook.cellStore.getValue(cellIndex, (id) => args.state.strings.get(id)), workbookDateSystem());
    const directScalarCellNumericValue = (cellIndex) => directScalarCellNumber(args.state.workbook.cellStore, cellIndex);
    const readCellTextNumber = (cellIndex) => {
        const stringId = args.state.workbook.cellStore.stringIds[cellIndex] ?? 0;
        return parseArithmeticScalarText(args.state.strings.get(stringId), workbookDateSystem());
    };
    const directScalarCurrentResultMatchesCell = (cellIndex, result) => {
        const cellStore = args.state.workbook.cellStore;
        const currentTag = cellStore.tags[cellIndex] ?? ValueTag.Empty;
        if (result.kind === 'number') {
            return currentTag === ValueTag.Number && Object.is(cellStore.numbers[cellIndex] ?? 0, result.value);
        }
        return currentTag === ValueTag.Error && (cellStore.errors[cellIndex] ?? ErrorCode.None) === result.code;
    };
    const directScalarNumericResultMatchesCell = (cellIndex, result) => {
        const cellStore = args.state.workbook.cellStore;
        return cellStore.tags[cellIndex] === ValueTag.Number && Object.is(cellStore.numbers[cellIndex] ?? 0, result);
    };
    const applyDirectFormulaCurrentResult = (cellIndex, result) => {
        const cellStore = args.state.workbook.cellStore;
        const beforeTag = cellStore.tags[cellIndex] ?? ValueTag.Empty;
        const beforeNumber = cellStore.numbers[cellIndex] ?? 0;
        const beforeError = cellStore.errors[cellIndex] ?? ErrorCode.None;
        cellStore.flags[cellIndex] = (cellStore.flags[cellIndex] ?? 0) & ~(64 /* CellFlags.SpillChild */ | 128 /* CellFlags.PivotOutput */);
        cellStore.versions[cellIndex] = (cellStore.versions[cellIndex] ?? 0) + 1;
        cellStore.stringIds[cellIndex] = 0;
        if (result.kind === 'number') {
            cellStore.tags[cellIndex] = ValueTag.Number;
            cellStore.numbers[cellIndex] = result.value;
            cellStore.errors[cellIndex] = ErrorCode.None;
            if (cellStore.onSetValue) {
                cellStore.onSetValue(cellIndex);
            }
            else if (beforeTag !== ValueTag.Number || !Object.is(beforeNumber, result.value)) {
                args.state.workbook.notifyCellValueWritten(cellIndex);
            }
            return true;
        }
        cellStore.tags[cellIndex] = ValueTag.Error;
        cellStore.numbers[cellIndex] = 0;
        cellStore.errors[cellIndex] = result.code;
        if (cellStore.onSetValue) {
            cellStore.onSetValue(cellIndex);
        }
        else if (beforeTag !== ValueTag.Error || beforeError !== result.code) {
            args.state.workbook.notifyCellValueWritten(cellIndex);
        }
        return true;
    };
    const applyDirectFormulaNumericResult = (cellIndex, value) => {
        const cellStore = args.state.workbook.cellStore;
        cellStore.flags[cellIndex] = (cellStore.flags[cellIndex] ?? 0) & ~(64 /* CellFlags.SpillChild */ | 128 /* CellFlags.PivotOutput */);
        cellStore.versions[cellIndex] = (cellStore.versions[cellIndex] ?? 0) + 1;
        cellStore.stringIds[cellIndex] = 0;
        cellStore.tags[cellIndex] = ValueTag.Number;
        cellStore.numbers[cellIndex] = value;
        cellStore.errors[cellIndex] = ErrorCode.None;
        if (cellStore.onSetValue) {
            cellStore.onSetValue(cellIndex);
        }
        else {
            args.state.workbook.notifyCellValueWritten(cellIndex);
        }
    };
    const applyTerminalDirectFormulaNumericResult = (cellIndex, value) => {
        const cellStore = args.state.workbook.cellStore;
        cellStore.flags[cellIndex] = (cellStore.flags[cellIndex] ?? 0) & ~(64 /* CellFlags.SpillChild */ | 128 /* CellFlags.PivotOutput */);
        cellStore.versions[cellIndex] = (cellStore.versions[cellIndex] ?? 0) + 1;
        cellStore.stringIds[cellIndex] = 0;
        cellStore.tags[cellIndex] = ValueTag.Number;
        cellStore.numbers[cellIndex] = value;
        cellStore.errors[cellIndex] = ErrorCode.None;
    };
    const applyTerminalDirectFormulaNumericResults = (cellIndices, values, count = cellIndices.length) => {
        const cellStore = args.state.workbook.cellStore;
        const flags = cellStore.flags;
        const versions = cellStore.versions;
        const stringIds = cellStore.stringIds;
        const tags = cellStore.tags;
        const numbers = cellStore.numbers;
        const errors = cellStore.errors;
        const formulaOutputFlags = 64 /* CellFlags.SpillChild */ | 128 /* CellFlags.PivotOutput */;
        const clearFormulaOutputFlags = ~formulaOutputFlags;
        for (let index = 0; index < count; index += 1) {
            const cellIndex = cellIndices[index];
            const currentFlags = flags[cellIndex] ?? 0;
            if ((currentFlags & formulaOutputFlags) !== 0) {
                flags[cellIndex] = currentFlags & clearFormulaOutputFlags;
            }
            versions[cellIndex] = (versions[cellIndex] ?? 0) + 1;
            stringIds[cellIndex] = 0;
            tags[cellIndex] = ValueTag.Number;
            numbers[cellIndex] = values[index];
            errors[cellIndex] = ErrorCode.None;
        }
    };
    const applyTerminalDirectFormulaAffineNumericResults = (cellIndices, values, scale, offset, count = cellIndices.length) => {
        const cellStore = args.state.workbook.cellStore;
        const flags = cellStore.flags;
        const versions = cellStore.versions;
        const stringIds = cellStore.stringIds;
        const tags = cellStore.tags;
        const numbers = cellStore.numbers;
        const errors = cellStore.errors;
        const formulaOutputFlags = 64 /* CellFlags.SpillChild */ | 128 /* CellFlags.PivotOutput */;
        const clearFormulaOutputFlags = ~formulaOutputFlags;
        for (let index = 0; index < count; index += 1) {
            const cellIndex = cellIndices[index];
            const currentFlags = flags[cellIndex] ?? 0;
            if ((currentFlags & formulaOutputFlags) !== 0) {
                flags[cellIndex] = currentFlags & clearFormulaOutputFlags;
            }
            versions[cellIndex] = (versions[cellIndex] ?? 0) + 1;
            stringIds[cellIndex] = 0;
            tags[cellIndex] = ValueTag.Number;
            numbers[cellIndex] = values[index] * scale + offset;
            errors[cellIndex] = ErrorCode.None;
        }
    };
    const applyTerminalDirectFormulaAffineNumericResultsFromRefs = (cellIndices, refs, scale, offset, count = cellIndices.length) => {
        const cellStore = args.state.workbook.cellStore;
        const flags = cellStore.flags;
        const versions = cellStore.versions;
        const stringIds = cellStore.stringIds;
        const tags = cellStore.tags;
        const numbers = cellStore.numbers;
        const errors = cellStore.errors;
        const formulaOutputFlags = 64 /* CellFlags.SpillChild */ | 128 /* CellFlags.PivotOutput */;
        const clearFormulaOutputFlags = ~formulaOutputFlags;
        for (let index = 0; index < count; index += 1) {
            const cellIndex = cellIndices[index];
            const mutation = refs[index].mutation;
            if (mutation.kind !== 'setCellValue' || typeof mutation.value !== 'number') {
                throw new Error('Expected affine direct scalar batch to contain numeric literal writes');
            }
            const currentFlags = flags[cellIndex] ?? 0;
            if ((currentFlags & formulaOutputFlags) !== 0) {
                flags[cellIndex] = currentFlags & clearFormulaOutputFlags;
            }
            versions[cellIndex] = (versions[cellIndex] ?? 0) + 1;
            stringIds[cellIndex] = 0;
            tags[cellIndex] = ValueTag.Number;
            numbers[cellIndex] = mutation.value * scale + offset;
            errors[cellIndex] = ErrorCode.None;
        }
    };
    const writeNumericLiteralToCellStore = (cellIndex, value) => {
        const cellStore = args.state.workbook.cellStore;
        cellStore.flags[cellIndex] = (cellStore.flags[cellIndex] ?? 0) & ~(64 /* CellFlags.SpillChild */ | 128 /* CellFlags.PivotOutput */);
        cellStore.versions[cellIndex] = (cellStore.versions[cellIndex] ?? 0) + 1;
        cellStore.stringIds[cellIndex] = 0;
        cellStore.tags[cellIndex] = ValueTag.Number;
        cellStore.numbers[cellIndex] = value;
        cellStore.errors[cellIndex] = ErrorCode.None;
    };
    const readDirectScalarCurrentOperand = (operand) => {
        switch (operand.kind) {
            case 'literal-number':
                return { kind: 'number', value: operand.value };
            case 'error':
                return { kind: 'error', code: operand.code };
            case 'cell': {
                const cellStore = args.state.workbook.cellStore;
                const tag = cellStore.tags[operand.cellIndex] ?? ValueTag.Empty;
                switch (tag) {
                    case ValueTag.Number:
                        return { kind: 'number', value: cellStore.numbers[operand.cellIndex] ?? 0 };
                    case ValueTag.Boolean:
                        return { kind: 'number', value: (cellStore.numbers[operand.cellIndex] ?? 0) !== 0 ? 1 : 0 };
                    case ValueTag.Empty:
                        return { kind: 'number', value: 0 };
                    case ValueTag.Error:
                        return { kind: 'error', code: cellStore.errors[operand.cellIndex] ?? ErrorCode.None };
                    case ValueTag.String: {
                        const numeric = readCellTextNumber(operand.cellIndex);
                        return numeric === undefined ? { kind: 'error', code: ErrorCode.Value } : { kind: 'number', value: numeric };
                    }
                }
            }
        }
    };
    const evaluateDirectScalarCurrentValue = (directScalar) => {
        if (directScalar.kind === 'abs') {
            const operand = readDirectScalarCurrentOperand(directScalar.operand);
            return operand?.kind === 'number' ? { kind: 'number', value: Math.abs(operand.value) } : operand;
        }
        const left = readDirectScalarCurrentOperand(directScalar.left);
        const right = readDirectScalarCurrentOperand(directScalar.right);
        if (!left || !right) {
            return undefined;
        }
        if (left.kind === 'error') {
            return left;
        }
        if (right.kind === 'error') {
            return right;
        }
        let result;
        switch (directScalar.operator) {
            case '+':
                result = left.value + right.value;
                break;
            case '-':
                result = left.value - right.value;
                break;
            case '*':
                result = left.value * right.value;
                break;
            case '/':
                if (right.value === 0) {
                    return { kind: 'error', code: ErrorCode.Div0 };
                }
                result = left.value / right.value;
                break;
        }
        return { kind: 'number', value: result + (directScalar.resultOffset ?? 0) };
    };
    const applyDirectScalarCurrentValue = (cellIndex, directScalar) => {
        const result = evaluateDirectScalarCurrentValue(directScalar);
        if (!result) {
            return false;
        }
        return applyDirectFormulaCurrentResult(cellIndex, result);
    };
    const tryEvaluateDirectScalarWithPendingNumbers = (directScalar, pendingNumbers) => {
        const readOperand = (operand) => {
            switch (operand.kind) {
                case 'literal-number':
                    return { kind: 'number', value: operand.value };
                case 'error':
                    return { kind: 'error', code: operand.code };
                case 'cell': {
                    const pending = pendingNumbers.get(operand.cellIndex);
                    if (pending !== undefined) {
                        return { kind: 'number', value: pending };
                    }
                    const cellStore = args.state.workbook.cellStore;
                    const tag = cellStore.tags[operand.cellIndex] ?? ValueTag.Empty;
                    switch (tag) {
                        case ValueTag.Number:
                            return { kind: 'number', value: cellStore.numbers[operand.cellIndex] ?? 0 };
                        case ValueTag.Boolean:
                            return { kind: 'number', value: (cellStore.numbers[operand.cellIndex] ?? 0) !== 0 ? 1 : 0 };
                        case ValueTag.Empty:
                            return { kind: 'number', value: 0 };
                        case ValueTag.Error:
                            return { kind: 'error', code: cellStore.errors[operand.cellIndex] ?? ErrorCode.None };
                        case ValueTag.String: {
                            const numeric = readCellTextNumber(operand.cellIndex);
                            return numeric === undefined ? { kind: 'error', code: ErrorCode.Value } : { kind: 'number', value: numeric };
                        }
                    }
                }
            }
        };
        if (directScalar.kind === 'abs') {
            const operand = readOperand(directScalar.operand);
            return operand?.kind === 'number' ? { kind: 'number', value: Math.abs(operand.value) } : operand;
        }
        const left = readOperand(directScalar.left);
        const right = readOperand(directScalar.right);
        if (!left || !right) {
            return undefined;
        }
        if (left.kind === 'error') {
            return left;
        }
        if (right.kind === 'error') {
            return right;
        }
        let result;
        switch (directScalar.operator) {
            case '+':
                result = left.value + right.value;
                break;
            case '-':
                result = left.value - right.value;
                break;
            case '*':
                result = left.value * right.value;
                break;
            case '/':
                if (right.value === 0) {
                    return { kind: 'error', code: ErrorCode.Div0 };
                }
                result = left.value / right.value;
                break;
        }
        return { kind: 'number', value: result + (directScalar.resultOffset ?? 0) };
    };
    const tryEvaluateDirectScalarNumericWithPendingNumbers = (directScalar, pendingNumbers) => {
        const readOperand = (operand) => {
            switch (operand.kind) {
                case 'literal-number':
                    return operand.value;
                case 'error':
                    return undefined;
                case 'cell': {
                    const pending = pendingNumbers.get(operand.cellIndex);
                    if (pending !== undefined) {
                        return pending;
                    }
                    const cellStore = args.state.workbook.cellStore;
                    switch (cellStore.tags[operand.cellIndex] ?? ValueTag.Empty) {
                        case ValueTag.Number:
                            return cellStore.numbers[operand.cellIndex] ?? 0;
                        case ValueTag.Boolean:
                            return (cellStore.numbers[operand.cellIndex] ?? 0) !== 0 ? 1 : 0;
                        case ValueTag.Empty:
                            return 0;
                        case ValueTag.Error:
                        case ValueTag.String:
                            return undefined;
                    }
                }
            }
        };
        if (directScalar.kind === 'abs') {
            const operand = readOperand(directScalar.operand);
            return operand === undefined ? undefined : Math.abs(operand);
        }
        const left = readOperand(directScalar.left);
        const right = readOperand(directScalar.right);
        if (left === undefined || right === undefined) {
            return undefined;
        }
        let result;
        switch (directScalar.operator) {
            case '+':
                result = left + right;
                break;
            case '-':
                result = left - right;
                break;
            case '*':
                result = left * right;
                break;
            case '/':
                if (right === 0) {
                    return undefined;
                }
                result = left / right;
                break;
        }
        return result + (directScalar.resultOffset ?? 0);
    };
    return {
        readDirectScalarCellNumber,
        directScalarCellNumericValue,
        directScalarCurrentResultMatchesCell,
        directScalarNumericResultMatchesCell,
        applyDirectFormulaCurrentResult,
        applyDirectFormulaNumericResult,
        applyTerminalDirectFormulaNumericResult,
        applyTerminalDirectFormulaNumericResults,
        applyTerminalDirectFormulaAffineNumericResults,
        applyTerminalDirectFormulaAffineNumericResultsFromRefs,
        writeNumericLiteralToCellStore,
        evaluateDirectScalarCurrentValue,
        applyDirectScalarCurrentValue,
        tryEvaluateDirectScalarWithPendingNumbers,
        tryEvaluateDirectScalarNumericWithPendingNumbers,
    };
}
//# sourceMappingURL=operation-direct-formula-values.js.map