import { Float64Arena, Uint32Arena } from '@bilig/formula/program-arena';
import { cellToCsvValue, serializeCsv } from '../csv.js';
import { CycleDetector } from '../cycle-detection.js';
import { EdgeArena } from '../edge-arena.js';
import { EngineEventBus } from '../events.js';
import { FormulaTable } from '../formula-table.js';
import { createRuntimeEngineCounters } from '../perf/engine-counters.js';
import { RangeRegistry } from '../range-registry.js';
import { compareOpOrder, createLocalOpOrder, createReplicaState } from '../replica-state.js';
import { RecalcScheduler } from '../scheduler.js';
import { StringPool } from '../string-pool.js';
import { WasmKernelFacade } from '../wasm-facade.js';
import { WorkbookStore } from '../workbook-store.js';
import { hasWorkbookMetadataForSheetRename } from '../workbook-store-metadata-presence.js';
import { createEngineRuntimeState, createInitialRecalcMetrics, createInitialSelectionState, } from './runtime-state.js';
import { createEngineServiceRuntime, runEngineEffect } from './live.js';
import { EngineEvaluationTimeoutError } from './errors.js';
const EMPTY_ENGINE_WORK_BUFFER = new Uint32Array(0);
export class SpreadsheetEngineRuntimeBase {
    performanceCounters = createRuntimeEngineCounters();
    workbook;
    strings = new StringPool();
    events = new EngineEventBus();
    replicaState;
    ranges = new RangeRegistry(this.performanceCounters);
    scheduler = new RecalcScheduler(this.performanceCounters);
    wasm = new WasmKernelFacade();
    formulas;
    cycleDetector;
    edgeArena = new EdgeArena();
    programArena = new Uint32Arena();
    constantArena = new Float64Arena();
    rangeListArena = new Uint32Arena();
    reverseCellEdges = [];
    reverseRangeEdges = [];
    reverseDefinedNameEdges = new Map();
    reverseTableEdges = new Map();
    reverseSpillEdges = new Map();
    reverseAggregateColumnEdges = new Map();
    reverseExactLookupColumnEdges = new Map();
    reverseSortedLookupColumnEdges = new Map();
    pivotOutputOwners = new Map();
    batchListeners = new Set();
    selectionListeners = new Set();
    entityVersions = new Map();
    sheetDeleteVersions = new Map();
    selection = createInitialSelectionState();
    syncState = 'local-only';
    syncClientConnection = null;
    undoStack = [];
    redoStack = [];
    transactionReplayDepth = 0;
    useColumnIndexEnabled;
    evaluationTimeoutMs;
    evaluationBudgetActive = false;
    evaluationBudgetDeadlineMs = Number.POSITIVE_INFINITY;
    evaluationBudgetDepth = 0;
    evaluationBudgetStepCount = 0;
    dependencyBuildEpoch = 1;
    dependencyBuildSeen = EMPTY_ENGINE_WORK_BUFFER;
    dependencyBuildCells = EMPTY_ENGINE_WORK_BUFFER;
    dependencyBuildEntities = EMPTY_ENGINE_WORK_BUFFER;
    dependencyBuildRanges = EMPTY_ENGINE_WORK_BUFFER;
    dependencyBuildNewRanges = EMPTY_ENGINE_WORK_BUFFER;
    symbolicRefBindings = EMPTY_ENGINE_WORK_BUFFER;
    symbolicRangeBindings = EMPTY_ENGINE_WORK_BUFFER;
    wasmProgramTargets = EMPTY_ENGINE_WORK_BUFFER;
    wasmProgramOffsets = EMPTY_ENGINE_WORK_BUFFER;
    wasmProgramLengths = EMPTY_ENGINE_WORK_BUFFER;
    wasmConstantOffsets = EMPTY_ENGINE_WORK_BUFFER;
    wasmConstantLengths = EMPTY_ENGINE_WORK_BUFFER;
    wasmRangeOffsets = EMPTY_ENGINE_WORK_BUFFER;
    wasmRangeLengths = EMPTY_ENGINE_WORK_BUFFER;
    wasmRangeRowCounts = EMPTY_ENGINE_WORK_BUFFER;
    wasmRangeColCounts = EMPTY_ENGINE_WORK_BUFFER;
    topoIndegree = EMPTY_ENGINE_WORK_BUFFER;
    topoQueue = EMPTY_ENGINE_WORK_BUFFER;
    directScalarDeltaInputCellIndices = [];
    directScalarDeltaOutputCellIndicesByInput = [];
    batchMutationDepth = 0;
    wasmProgramSyncPending = false;
    lastMetrics = createInitialRecalcMetrics();
    state;
    runtime;
    constructor(options = {}) {
        this.workbook = new WorkbookStore(options.workbookName ?? 'Workbook', this.performanceCounters);
        this.formulas = new FormulaTable(this.workbook.cellStore, {
            deltaInputCellIndices: this.directScalarDeltaInputCellIndices,
            singleOutputCellIndicesByInput: this.directScalarDeltaOutputCellIndicesByInput,
            readDeltaInputCellIndex: (formula) => formula.directScalar?.kind === 'binary' && !formula.compiled.volatile && !formula.compiled.producesSpill
                ? formula.directScalar.deltaInputCellIndex
                : undefined,
        });
        this.replicaState = createReplicaState(options.replicaId ?? 'local');
        this.useColumnIndexEnabled = options.useColumnIndex ?? true;
        this.evaluationTimeoutMs = options.evaluationTimeoutMs;
        this.state = createEngineRuntimeState({
            workbook: this.workbook,
            strings: this.strings,
            events: this.events,
            ranges: this.ranges,
            scheduler: this.scheduler,
            wasm: this.wasm,
            formulas: this.formulas,
            directScalarDeltaInputCellIndices: this.directScalarDeltaInputCellIndices,
            directScalarDeltaOutputCellIndicesByInput: this.directScalarDeltaOutputCellIndicesByInput,
            replicaState: this.replicaState,
            entityVersions: this.entityVersions,
            sheetDeleteVersions: this.sheetDeleteVersions,
            batchListeners: this.batchListeners,
            selectionListeners: this.selectionListeners,
            undoStack: this.undoStack,
            redoStack: this.redoStack,
            counters: this.performanceCounters,
            trackReplicaVersions: options.trackReplicaVersions ?? true,
            getUseColumnIndex: () => this.useColumnIndexEnabled,
            setUseColumnIndex: (enabled) => {
                this.useColumnIndexEnabled = enabled;
            },
            setEvaluationTimeoutMs: (timeoutMs) => {
                this.evaluationTimeoutMs = timeoutMs;
            },
            beginEvaluationBudget: (startedAtMs) => {
                this.beginEvaluationBudget(startedAtMs);
            },
            endEvaluationBudget: () => {
                this.endEvaluationBudget();
            },
            checkEvaluationBudget: (stepCost) => {
                this.checkEvaluationBudget(stepCost);
            },
            getSelection: () => this.selection,
            setSelection: (selection) => {
                this.selection = selection;
            },
            getSyncState: () => this.syncState,
            setSyncState: (state) => {
                this.syncState = state;
            },
            getSyncClientConnection: () => this.syncClientConnection,
            setSyncClientConnection: (connection) => {
                this.syncClientConnection = connection;
            },
            getTransactionReplayDepth: () => this.transactionReplayDepth,
            setTransactionReplayDepth: (depth) => {
                this.transactionReplayDepth = depth;
            },
            getLastMetrics: () => this.lastMetrics,
            setLastMetrics: (metrics) => {
                this.lastMetrics = metrics;
            },
        });
        this.runtime = createEngineServiceRuntime({
            state: this.state,
            getCellByIndex: (cellIndex) => this.getCellByIndex(cellIndex),
            exportSnapshot: () => this.exportSnapshot(),
            importSnapshot: (snapshot) => this.importSnapshot(snapshot),
            maintenance: {
                state: this.state,
                edgeArena: this.edgeArena,
                reverseState: {
                    reverseCellEdges: this.reverseCellEdges,
                    reverseRangeEdges: this.reverseRangeEdges,
                    reverseDefinedNameEdges: this.reverseDefinedNameEdges,
                    reverseTableEdges: this.reverseTableEdges,
                    reverseSpillEdges: this.reverseSpillEdges,
                    reverseAggregateColumnEdges: this.reverseAggregateColumnEdges,
                    reverseExactLookupColumnEdges: this.reverseExactLookupColumnEdges,
                    reverseSortedLookupColumnEdges: this.reverseSortedLookupColumnEdges,
                },
                pivotOutputOwners: this.pivotOutputOwners,
                setWasmProgramSyncPending: (next) => {
                    this.wasmProgramSyncPending = next;
                },
                resetWasmState: () => {
                    this.wasm.resetStoreState();
                },
            },
            mutationSupport: {
                state: this.state,
                edgeArena: this.edgeArena,
                reverseState: {
                    reverseCellEdges: this.reverseCellEdges,
                    reverseRangeEdges: this.reverseRangeEdges,
                },
                getSelectionState: () => this.getSelectionState(),
                setSelection: (sheetName, address) => this.setSelection(sheetName, address),
            },
            formulaBinding: {
                state: this.state,
                edgeArena: this.edgeArena,
                programArena: this.programArena,
                constantArena: this.constantArena,
                rangeListArena: this.rangeListArena,
                reverseState: {
                    reverseCellEdges: this.reverseCellEdges,
                    reverseRangeEdges: this.reverseRangeEdges,
                    reverseDefinedNameEdges: this.reverseDefinedNameEdges,
                    reverseTableEdges: this.reverseTableEdges,
                    reverseSpillEdges: this.reverseSpillEdges,
                    reverseAggregateColumnEdges: this.reverseAggregateColumnEdges,
                    reverseExactLookupColumnEdges: this.reverseExactLookupColumnEdges,
                    reverseSortedLookupColumnEdges: this.reverseSortedLookupColumnEdges,
                },
                getDependencyBuildEpoch: () => this.dependencyBuildEpoch,
                setDependencyBuildEpoch: (next) => {
                    this.dependencyBuildEpoch = next;
                },
                getDependencyBuildSeen: () => this.dependencyBuildSeen,
                setDependencyBuildSeen: (next) => {
                    this.dependencyBuildSeen = next;
                },
                getDependencyBuildCells: () => this.dependencyBuildCells,
                setDependencyBuildCells: (next) => {
                    this.dependencyBuildCells = next;
                },
                getDependencyBuildEntities: () => this.dependencyBuildEntities,
                setDependencyBuildEntities: (next) => {
                    this.dependencyBuildEntities = next;
                },
                getDependencyBuildRanges: () => this.dependencyBuildRanges,
                setDependencyBuildRanges: (next) => {
                    this.dependencyBuildRanges = next;
                },
                getDependencyBuildNewRanges: () => this.dependencyBuildNewRanges,
                setDependencyBuildNewRanges: (next) => {
                    this.dependencyBuildNewRanges = next;
                },
                getSymbolicRefBindings: () => this.symbolicRefBindings,
                setSymbolicRefBindings: (next) => {
                    this.symbolicRefBindings = next;
                },
                getSymbolicRangeBindings: () => this.symbolicRangeBindings,
                setSymbolicRangeBindings: (next) => {
                    this.symbolicRangeBindings = next;
                },
            },
            formulaGraph: {
                state: this.state,
                getCycleDetector: () => (this.cycleDetector ??= new CycleDetector()),
                programArena: this.programArena,
                constantArena: this.constantArena,
                rangeListArena: this.rangeListArena,
                getTopoIndegree: () => this.topoIndegree,
                setTopoIndegree: (next) => {
                    this.topoIndegree = next;
                },
                getTopoQueue: () => this.topoQueue,
                setTopoQueue: (next) => {
                    this.topoQueue = next;
                },
                getWasmProgramTargets: () => this.wasmProgramTargets,
                setWasmProgramTargets: (next) => {
                    this.wasmProgramTargets = next;
                },
                getWasmProgramOffsets: () => this.wasmProgramOffsets,
                setWasmProgramOffsets: (next) => {
                    this.wasmProgramOffsets = next;
                },
                getWasmProgramLengths: () => this.wasmProgramLengths,
                setWasmProgramLengths: (next) => {
                    this.wasmProgramLengths = next;
                },
                getWasmConstantOffsets: () => this.wasmConstantOffsets,
                setWasmConstantOffsets: (next) => {
                    this.wasmConstantOffsets = next;
                },
                getWasmConstantLengths: () => this.wasmConstantLengths,
                setWasmConstantLengths: (next) => {
                    this.wasmConstantLengths = next;
                },
                getWasmRangeOffsets: () => this.wasmRangeOffsets,
                setWasmRangeOffsets: (next) => {
                    this.wasmRangeOffsets = next;
                },
                getWasmRangeLengths: () => this.wasmRangeLengths,
                setWasmRangeLengths: (next) => {
                    this.wasmRangeLengths = next;
                },
                getWasmRangeRowCounts: () => this.wasmRangeRowCounts,
                setWasmRangeRowCounts: (next) => {
                    this.wasmRangeRowCounts = next;
                },
                getWasmRangeColCounts: () => this.wasmRangeColCounts,
                setWasmRangeColCounts: (next) => {
                    this.wasmRangeColCounts = next;
                },
                getBatchMutationDepth: () => this.batchMutationDepth,
                getWasmProgramSyncPending: () => this.wasmProgramSyncPending,
                setWasmProgramSyncPending: (next) => {
                    this.wasmProgramSyncPending = next;
                },
            },
            traversal: {
                state: this.state,
                edgeArena: this.edgeArena,
                reverseState: {
                    reverseCellEdges: this.reverseCellEdges,
                    reverseRangeEdges: this.reverseRangeEdges,
                    reverseAggregateColumnEdges: this.reverseAggregateColumnEdges,
                    reverseExactLookupColumnEdges: this.reverseExactLookupColumnEdges,
                    reverseSortedLookupColumnEdges: this.reverseSortedLookupColumnEdges,
                },
            },
            cellToCsvValue: (cell) => cellToCsvValue(cell),
            serializeCsv: (rows) => serializeCsv(rows),
            pivotState: {
                pivotOutputOwners: this.pivotOutputOwners,
            },
            recalc: {
                state: this.state,
                getCellByIndex: (cellIndex) => this.getCellByIndex(cellIndex),
                exportSnapshot: () => this.exportSnapshot(),
                importSnapshot: (snapshot) => this.importSnapshot(snapshot),
                now: () => new Date(),
                random: () => Math.random(),
                performanceNow: () => performance.now(),
                forEachFormulaDependencyCell: (cellIndex, fn) => this.runtime.traversal.forEachFormulaDependencyCellNow(cellIndex, fn),
            },
            pivot: {
                state: {
                    workbook: this.state.workbook,
                    strings: this.state.strings,
                    formulas: this.state.formulas,
                    ranges: this.state.ranges,
                    wasm: this.state.wasm,
                    pivotOutputOwners: this.pivotOutputOwners,
                },
            },
            applyRemoteSnapshot: (snapshot) => {
                this.importSnapshot(snapshot);
            },
            operation: {
                state: this.state,
                edgeArena: this.edgeArena,
                reverseState: {
                    reverseCellEdges: this.reverseCellEdges,
                    reverseSpillEdges: this.reverseSpillEdges,
                    reverseAggregateColumnEdges: this.reverseAggregateColumnEdges,
                    reverseExactLookupColumnEdges: this.reverseExactLookupColumnEdges,
                    reverseSortedLookupColumnEdges: this.reverseSortedLookupColumnEdges,
                },
                getBatchMutationDepth: () => this.batchMutationDepth,
                setBatchMutationDepth: (next) => {
                    this.batchMutationDepth = next;
                },
                materializePivot: (pivotRecord) => this.runtime.pivot.materializePivotNow(pivotRecord),
                refreshRangeDependencies: () => {
                    return;
                },
                materializeDeferredStructuralFormulaSources: () => {
                    return;
                },
                getChangedFormulaBuffer: () => new Uint32Array(),
                repairTopoRanks: () => false,
                getEntityDependents: () => new Uint32Array(),
                getSingleEntityDependent: () => -1,
                forEachFormulaDependencyCell: (cellIndex, fn) => this.runtime.traversal.forEachFormulaDependencyCellNow(cellIndex, fn),
                collectFormulaDependents: () => new Uint32Array(),
                collectFormulaCellsForTables: () => [],
                clearSpillForCell: () => ({ changedCellIndices: [], ownerCellIndex: undefined }),
                noteExactLookupLiteralWrite: () => {
                    return;
                },
                noteAggregateLiteralWrite: () => {
                    return;
                },
                noteSortedLookupLiteralWrite: () => {
                    return;
                },
                sortedLookup: {
                    findPreparedVectorMatch: () => ({ handled: false }),
                },
                exactLookup: {
                    findPreparedVectorMatch: () => ({ handled: false }),
                },
                deferKernelSync: () => {
                    return;
                },
                invalidateExactLookupColumn: () => {
                    return;
                },
                invalidateAggregateColumn: () => {
                    return;
                },
                invalidateSortedLookupColumn: () => {
                    return;
                },
            },
        });
    }
    renameSheetMetadataOnly(oldName, newName) {
        const trimmedName = newName.trim();
        if (trimmedName.length === 0 ||
            oldName === trimmedName ||
            !this.workbook.getSheet(oldName) ||
            this.workbook.getSheet(trimmedName) ||
            this.syncClientConnection !== null ||
            this.batchListeners.size > 0 ||
            this.batchMutationDepth !== 0 ||
            this.transactionReplayDepth !== 0 ||
            this.workbook.metadata.workbookProtection?.lockStructure === true) {
            return false;
        }
        const renamedSheet = this.workbook.renameSheet(oldName, trimmedName);
        if (!renamedSheet) {
            return false;
        }
        this.recordMetadataOnlySheetRename(oldName, trimmedName);
        return true;
    }
    hasWorkbookMetadataForSheetRenameFastPath() {
        return hasWorkbookMetadataForSheetRename(this.workbook.metadata);
    }
    renameSheetMetadataOnlyById(sheetId, newName) {
        const trimmedName = newName.trim();
        const sheet = this.workbook.getSheetById(sheetId);
        if (trimmedName.length === 0 ||
            !sheet ||
            sheet.name === trimmedName ||
            this.workbook.getSheet(trimmedName) ||
            this.syncClientConnection !== null ||
            this.batchListeners.size > 0 ||
            this.batchMutationDepth !== 0 ||
            this.transactionReplayDepth !== 0 ||
            this.workbook.metadata.workbookProtection?.lockStructure === true) {
            return false;
        }
        const oldName = sheet.name;
        const renamedSheet = this.workbook.renameSheetByIdTrustedPrevalidated(sheetId, oldName, trimmedName);
        if (!renamedSheet) {
            return false;
        }
        this.recordMetadataOnlySheetRename(oldName, trimmedName);
        return true;
    }
    renameSheetMetadataOnlyByIdPrevalidated(sheetId, oldName, trimmedName) {
        if (trimmedName.length === 0 ||
            oldName === trimmedName ||
            this.workbook.getSheet(trimmedName) ||
            this.syncClientConnection !== null ||
            this.batchListeners.size > 0 ||
            this.batchMutationDepth !== 0 ||
            this.transactionReplayDepth !== 0 ||
            this.workbook.metadata.workbookProtection?.lockStructure === true) {
            return false;
        }
        const sheet = this.workbook.getSheetById(sheetId);
        if (!sheet || sheet.name !== oldName) {
            return false;
        }
        const renamedSheet = this.workbook.renameSheetByIdTrustedPrevalidated(sheetId, oldName, trimmedName);
        if (!renamedSheet) {
            return false;
        }
        this.recordMetadataOnlySheetRename(oldName, trimmedName);
        return true;
    }
    renameSheetMetadataOnlyByIdTrustedPrevalidated(sheetId, oldName, trimmedName, assumeLocalRuntimeReady = false, assumeNoWorkbookRenameMetadata = false) {
        if (!assumeLocalRuntimeReady &&
            (this.syncClientConnection !== null ||
                this.batchListeners.size > 0 ||
                this.batchMutationDepth !== 0 ||
                this.transactionReplayDepth !== 0 ||
                this.workbook.metadata.workbookProtection?.lockStructure === true)) {
            return false;
        }
        const renamedSheet = assumeNoWorkbookRenameMetadata
            ? this.renameSheetNamesOnlyByIdTrustedPrevalidated(sheetId, oldName, trimmedName)
            : this.workbook.renameSheetByIdTrustedPrevalidated(sheetId, oldName, trimmedName);
        if (!renamedSheet) {
            return false;
        }
        if (assumeNoWorkbookRenameMetadata && this.recordSimpleMetadataOnlySheetRename(oldName, trimmedName)) {
            return true;
        }
        this.recordMetadataOnlySheetRename(oldName, trimmedName);
        return true;
    }
    renameSheetSimpleMetadataOnlyByIdTrustedPrevalidated(sheetId, oldName, trimmedName, assumeLocalRuntimeReady = false, assumeNoWorkbookRenameMetadata = false) {
        if (!assumeLocalRuntimeReady &&
            (this.syncClientConnection !== null ||
                this.batchListeners.size > 0 ||
                this.batchMutationDepth !== 0 ||
                this.transactionReplayDepth !== 0 ||
                this.workbook.metadata.workbookProtection?.lockStructure === true)) {
            return false;
        }
        if (this.state.trackReplicaVersions ||
            this.redoStack.length !== 0 ||
            (!assumeNoWorkbookRenameMetadata && hasWorkbookMetadataForSheetRename(this.workbook.metadata))) {
            return false;
        }
        if (!this.renameSheetNamesOnlyByIdTrustedPrevalidated(sheetId, oldName, trimmedName)) {
            return false;
        }
        this.recordSimpleMetadataOnlySheetRenameTrusted(oldName, trimmedName);
        return true;
    }
    renameSheetNamesOnlyByIdTrustedPrevalidated(sheetId, oldName, trimmedName) {
        const sheet = this.workbook.sheetsById.get(sheetId);
        if (!sheet ||
            sheet.name !== oldName ||
            this.workbook.sheetsByName.has(trimmedName) ||
            sheet.styleRanges.length !== 0 ||
            sheet.formatRanges.length !== 0) {
            return false;
        }
        this.workbook.sheetsByName.delete(oldName);
        sheet.name = trimmedName;
        this.workbook.sheetsByName.set(trimmedName, sheet);
        return true;
    }
    recordSimpleMetadataOnlySheetRename(oldName, trimmedName) {
        if (this.state.trackReplicaVersions || this.workbook.metadata.definedNames.size > 0 || this.redoStack.length !== 0) {
            return false;
        }
        this.recordSimpleMetadataOnlySheetRenameTrusted(oldName, trimmedName);
        return true;
    }
    recordSimpleMetadataOnlySheetRenameTrusted(oldName, trimmedName) {
        if (this.selection.sheetName === oldName) {
            this.selection.sheetName = trimmedName;
        }
        this.runtime.binding.deferCellFormulasForSheetRenameNow(oldName, trimmedName);
        this.undoStack.push({
            forward: { kind: 'rename-sheet', oldName, newName: trimmedName },
            inverse: { kind: 'rename-sheet', oldName: trimmedName, newName: oldName },
        });
    }
    recordMetadataOnlySheetRename(oldName, trimmedName) {
        if (this.state.trackReplicaVersions) {
            const order = createLocalOpOrder(this.replicaState);
            this.entityVersions.set(`sheet:${oldName}`, order);
            this.entityVersions.set(`sheet:${trimmedName}`, order);
            this.sheetDeleteVersions.set(oldName, order);
            const renamedTombstone = this.sheetDeleteVersions.get(trimmedName);
            if (!renamedTombstone || compareOpOrder(order, renamedTombstone) > 0) {
                this.sheetDeleteVersions.delete(trimmedName);
            }
        }
        if (this.selection.sheetName === oldName) {
            this.selection = { ...this.selection, sheetName: trimmedName };
        }
        if (this.workbook.metadata.definedNames.size > 0) {
            runEngineEffect(this.runtime.maintenance.rewriteDefinedNamesForSheetRename(oldName, trimmedName));
        }
        this.runtime.binding.deferCellFormulasForSheetRenameNow(oldName, trimmedName);
        this.undoStack.push({
            forward: { kind: 'rename-sheet', oldName, newName: trimmedName },
            inverse: { kind: 'rename-sheet', oldName: trimmedName, newName: oldName },
        });
        if (this.redoStack.length !== 0) {
            this.redoStack.length = 0;
        }
    }
    setEvaluationTimeoutMs(timeoutMs) {
        this.state.setEvaluationTimeoutMs(timeoutMs);
    }
    beginEvaluationBudget(startedAtMs) {
        const timeoutMs = this.evaluationTimeoutMs;
        if (timeoutMs === undefined) {
            this.evaluationBudgetActive = false;
            this.evaluationBudgetDepth = 0;
            this.evaluationBudgetDeadlineMs = Number.POSITIVE_INFINITY;
            this.evaluationBudgetStepCount = 0;
            return;
        }
        if (this.evaluationBudgetActive) {
            this.evaluationBudgetDepth += 1;
            return;
        }
        this.evaluationBudgetActive = true;
        this.evaluationBudgetDepth = 1;
        this.evaluationBudgetDeadlineMs = startedAtMs + timeoutMs;
        this.evaluationBudgetStepCount = 0;
    }
    endEvaluationBudget() {
        if (!this.evaluationBudgetActive) {
            return;
        }
        this.evaluationBudgetDepth = Math.max(0, this.evaluationBudgetDepth - 1);
        if (this.evaluationBudgetDepth > 0) {
            return;
        }
        this.evaluationBudgetActive = false;
        this.evaluationBudgetDepth = 0;
        this.evaluationBudgetDeadlineMs = Number.POSITIVE_INFINITY;
        this.evaluationBudgetStepCount = 0;
    }
    checkEvaluationBudget(stepCost = 1) {
        if (!this.evaluationBudgetActive || this.evaluationTimeoutMs === undefined) {
            return;
        }
        this.evaluationBudgetStepCount += Math.max(1, stepCost);
        if (this.evaluationTimeoutMs === 0 || performance.now() >= this.evaluationBudgetDeadlineMs) {
            throw new EngineEvaluationTimeoutError(this.evaluationTimeoutMs);
        }
    }
    executeLocalTransaction(ops, potentialNewCells, options = {}) {
        if (ops.length === 0) {
            return null;
        }
        return this.runtime.mutation.executeLocalNow(ops, potentialNewCells, { returnUndoOps: false, ...options });
    }
}
//# sourceMappingURL=engine-runtime-base.js.map