import { ErrorCode, ValueTag } from '@bilig/protocol';
import { addEngineCounter } from '../../perf/engine-counters.js';
import { createWrittenColumnTracker, markWrittenColumn, materializeWrittenColumns, } from '../../written-column-tracker.js';
const OP_ADD = 1;
const OP_SUB = 2;
const OP_MUL = 3;
const OP_DIV = 4;
const OP_ABS = 5;
const BATCH_REF_NONE = 0xffffffff;
export const MIN_INITIAL_NATIVE_DIRECT_SCALAR_BATCH_SIZE = 8192;
export const MAX_INITIAL_NATIVE_DIRECT_SCALAR_BATCH_SIZE = 65_536;
export function createInitialNativeDirectScalarBatch(args) {
    const targets = new Uint32Array(args.capacity);
    const operators = new Uint8Array(args.capacity);
    const leftBatchRefs = new Uint32Array(args.capacity);
    const leftTags = new Uint8Array(args.capacity);
    const leftValues = new Float64Array(args.capacity);
    const leftErrors = new Uint16Array(args.capacity);
    const rightBatchRefs = new Uint32Array(args.capacity);
    const rightTags = new Uint8Array(args.capacity);
    const rightValues = new Float64Array(args.capacity);
    const rightErrors = new Uint16Array(args.capacity);
    const resultOffsets = new Float64Array(args.capacity);
    const targetOrdinalByCellIndex = new Map();
    const columnsBySheetId = new Map();
    let count = 0;
    const noteColumn = (sheetId, col) => {
        let tracker = columnsBySheetId.get(sheetId);
        if (!tracker) {
            tracker = createWrittenColumnTracker();
            columnsBySheetId.set(sheetId, tracker);
        }
        markWrittenColumn(tracker, col);
    };
    const writeOperand = (index, operand, batchRefs, tags, values, errors) => {
        switch (operand.kind) {
            case 'cell': {
                const batchRef = targetOrdinalByCellIndex.get(operand.cellIndex);
                if (batchRef !== undefined) {
                    batchRefs[index] = batchRef;
                    tags[index] = ValueTag.Empty;
                    values[index] = 0;
                    errors[index] = ErrorCode.None;
                    return true;
                }
                const cellStore = args.state.workbook.cellStore;
                if (((cellStore.flags[operand.cellIndex] ?? 0) & 2 /* CellFlags.HasFormula */) !== 0) {
                    return false;
                }
                batchRefs[index] = BATCH_REF_NONE;
                tags[index] = cellStore.tags[operand.cellIndex] ?? ValueTag.Empty;
                values[index] = cellStore.numbers[operand.cellIndex] ?? 0;
                errors[index] = cellStore.errors[operand.cellIndex] ?? ErrorCode.None;
                return true;
            }
            case 'literal-number':
                batchRefs[index] = BATCH_REF_NONE;
                tags[index] = ValueTag.Number;
                values[index] = operand.value;
                errors[index] = ErrorCode.None;
                return true;
            case 'error':
                batchRefs[index] = BATCH_REF_NONE;
                tags[index] = ValueTag.Error;
                values[index] = 0;
                errors[index] = operand.code;
                return true;
        }
    };
    const operatorCode = (directScalar) => {
        if (directScalar.kind === 'abs') {
            return OP_ABS;
        }
        switch (directScalar.operator) {
            case '+':
                return OP_ADD;
            case '-':
                return OP_SUB;
            case '*':
                return OP_MUL;
            case '/':
                return OP_DIV;
        }
    };
    return {
        get count() {
            return count;
        },
        add(prepared, directScalar) {
            if (count >= args.capacity) {
                return false;
            }
            const index = count;
            targets[index] = prepared.cellIndex;
            operators[index] = operatorCode(directScalar);
            resultOffsets[index] = directScalar.kind === 'binary' ? (directScalar.resultOffset ?? 0) : 0;
            if (directScalar.kind === 'abs') {
                if (!writeOperand(index, directScalar.operand, leftBatchRefs, leftTags, leftValues, leftErrors)) {
                    return false;
                }
                rightBatchRefs[index] = BATCH_REF_NONE;
                rightTags[index] = ValueTag.Number;
                rightValues[index] = 0;
                rightErrors[index] = ErrorCode.None;
            }
            else {
                if (!writeOperand(index, directScalar.left, leftBatchRefs, leftTags, leftValues, leftErrors) ||
                    !writeOperand(index, directScalar.right, rightBatchRefs, rightTags, rightValues, rightErrors)) {
                    return false;
                }
            }
            targetOrdinalByCellIndex.set(prepared.cellIndex, index);
            noteColumn(prepared.sheetId, prepared.col);
            count += 1;
            return true;
        },
        evaluate() {
            if (count === 0 || !args.state.wasm.initSyncIfPossible()) {
                return undefined;
            }
            const targetView = targets.subarray(0, count);
            if (!args.state.wasm.evalDirectScalarStoreTargetBatch({
                targets: targetView,
                operators: operators.subarray(0, count),
                leftBatchRefs: leftBatchRefs.subarray(0, count),
                leftTags: leftTags.subarray(0, count),
                leftValues: leftValues.subarray(0, count),
                leftErrors: leftErrors.subarray(0, count),
                rightBatchRefs: rightBatchRefs.subarray(0, count),
                rightTags: rightTags.subarray(0, count),
                rightValues: rightValues.subarray(0, count),
                rightErrors: rightErrors.subarray(0, count),
                resultOffsets: resultOffsets.subarray(0, count),
            }, args.state.workbook.cellStore.size)) {
                return undefined;
            }
            const cellStore = args.state.workbook.cellStore;
            const kernelTags = args.state.wasm.tags;
            const kernelNumbers = args.state.wasm.numbers;
            const kernelErrors = args.state.wasm.errors;
            for (let index = 0; index < count; index += 1) {
                const cellIndex = targets[index];
                const tag = kernelTags[cellIndex] ?? ValueTag.Empty;
                cellStore.flags[cellIndex] = (cellStore.flags[cellIndex] ?? 0) & ~(64 /* CellFlags.SpillChild */ | 128 /* CellFlags.PivotOutput */);
                cellStore.tags[cellIndex] = tag;
                cellStore.errors[cellIndex] =
                    tag === ValueTag.Error ? (kernelErrors[cellIndex] ?? ErrorCode.None) : ErrorCode.None;
                cellStore.stringIds[cellIndex] = 0;
                cellStore.numbers[cellIndex] = tag === ValueTag.Number || tag === ValueTag.Boolean ? (kernelNumbers[cellIndex] ?? 0) : 0;
                cellStore.versions[cellIndex] = (cellStore.versions[cellIndex] ?? 0) + 1;
            }
            columnsBySheetId.forEach((tracker, sheetId) => {
                args.state.workbook.notifyColumnsWritten(sheetId, materializeWrittenColumns(tracker));
            });
            addEngineCounter(args.state.counters, 'nativeDirectScalarInitialEvaluations', count);
            return targetView;
        },
    };
}
//# sourceMappingURL=formula-initialization-native-direct-scalar.js.map