import { get, set } from '../matrix.js';
import { isFormulaValue } from './formula.js';
import { PointGraph } from './point-graph.js';
import { PointSet } from './point-set.js';

class Model {
    data;
    evaluatedData;
    referenceGraph;
    createFormulaParser;
    constructor(createFormulaParser, data, referenceGraph, evaluatedData) {
        this.createFormulaParser = createFormulaParser;
        this.data = data;
        this.referenceGraph = referenceGraph || createReferenceGraph();
        this.evaluatedData =
            evaluatedData || createEvaluatedData(data, this.referenceGraph);
    }
}
function updateCellValue(model, point, cell) {
    const nextData = set(point, cell, model.data);
    const nextReferenceGraph = isFormulaValue(cell.value)
        ? updateReferenceGraph(model.referenceGraph)
        : model.referenceGraph;
    const nextEvaluatedData = evaluateCell(model.evaluatedData, nextData, nextReferenceGraph, point, cell);
    return new Model(model.createFormulaParser, nextData, nextReferenceGraph, nextEvaluatedData);
}
function updateReferenceGraph(referenceGraph) {
    return referenceGraph;
}
function evaluateCell(prevEvaluatedData, data, referenceGraph, point, cell) {
    if (referenceGraph.hasCircularDependency(point)) {
        let visited = PointSet.from([point]);
        let nextEvaluatedData = set(point, { ...cell, value: 'TODO' }, // TODO For Formula Evaluation
        prevEvaluatedData);
        for (const referrer of referenceGraph.getBackwardsRecursive(point)) {
            if (visited.has(referrer)) {
                break;
            }
            visited = visited.add(referrer);
            const referrerCell = get(referrer, data);
            if (!referrerCell) {
                continue;
            }
            nextEvaluatedData = set(referrer, { ...referrerCell, value: 'TODO' }, // TODO For Formula Evaluation
            nextEvaluatedData);
        }
        return nextEvaluatedData;
    }
    let nextEvaluatedData = prevEvaluatedData;
    const evaluatedValue = isFormulaValue(cell.value)
        ? getFormulaComputedValue()
        : cell.value;
    const evaluatedCell = { ...cell, value: evaluatedValue };
    nextEvaluatedData = set(point, evaluatedCell, nextEvaluatedData);
    // for every formula cell that references the cell re-evaluate (recursive)
    for (const referrer of referenceGraph.getBackwardsRecursive(point)) {
        const referrerCell = get(referrer, data);
        if (!referrerCell) {
            continue;
        }
        const evaluatedValue = isFormulaValue(referrerCell.value)
            ? getFormulaComputedValue()
            : referrerCell.value;
        const evaluatedCell = { ...referrerCell, value: evaluatedValue };
        nextEvaluatedData = set(referrer, evaluatedCell, nextEvaluatedData);
    }
    return nextEvaluatedData;
}
function createReferenceGraph() {
    const entries = [];
    return PointGraph.from(entries);
}
function createEvaluatedData(data, referenceGraph) {
    let evaluatedData = data;
    // Iterate over the points in the reference graph, starting from the leaves
    for (const point of referenceGraph.traverseBFSBackwards()) {
        // Get the cell at the current point in the data Matrix
        const cell = get(point, data);
        if (!cell) {
            continue;
        }
        // If the cell is a formula cell, evaluate it
        if (isFormulaValue(cell.value)) {
            const evaluatedValue = getFormulaComputedValue();
            evaluatedData = set(point, { ...cell, value: evaluatedValue }, evaluatedData);
        }
    }
    return evaluatedData;
}
/** Get the computed value of a formula cell */
//TODO Function need to be Evaluate
function getFormulaComputedValue() {
    return 'TODO'; // TODO For Formula Evaluation
}

export { Model, createEvaluatedData, createReferenceGraph, getFormulaComputedValue, updateCellValue };
//# sourceMappingURL=engine.js.map
