import { ValidationError } from "../core/index.js";
import {} from "./System.js";
import {} from "./World.js";
const phaseOrder = ["fixed", "update", "late"];
export class SystemScheduler {
    systems = new Map();
    add(system, world) {
        if (this.systems.has(system.name))
            throw new ValidationError("DUPLICATE_SYSTEM", `Duplicate system name: ${system.name}`);
        this.systems.set(system.name, system);
        system.onAdd?.(world);
    }
    remove(name, world) {
        const system = this.systems.get(name);
        if (!system)
            return false;
        system.onRemove?.(world);
        return this.systems.delete(name);
    }
    order(phase) {
        const systems = [...this.systems.values()].filter((system) => (system.phase ?? "update") === (phase ?? system.phase ?? "update"));
        return sortSystems(systems);
    }
    update(world, context) {
        for (const phase of phaseOrder) {
            for (const system of this.order(phase))
                system.update(world, context);
        }
    }
}
function sortSystems(systems) {
    const byName = new Map(systems.map((system) => [system.name, system]));
    const graph = new Map();
    const indegree = new Map();
    const insertion = new Map(systems.map((system, index) => [system.name, index]));
    for (const system of systems) {
        graph.set(system.name, new Set());
        indegree.set(system.name, 0);
    }
    for (const system of systems) {
        for (const dependency of system.after ?? [])
            addEdge(dependency, system.name);
        for (const dependent of system.before ?? [])
            addEdge(system.name, dependent);
    }
    const ready = systems
        .filter((system) => indegree.get(system.name) === 0)
        .sort(compareSystems);
    const result = [];
    while (ready.length > 0) {
        const system = ready.shift();
        result.push(system);
        for (const dependent of graph.get(system.name) ?? []) {
            indegree.set(dependent, (indegree.get(dependent) ?? 0) - 1);
            if (indegree.get(dependent) === 0) {
                ready.push(byName.get(dependent));
                ready.sort(compareSystems);
            }
        }
    }
    if (result.length !== systems.length)
        throw new ValidationError("ECS_SYSTEM_CYCLE", "ECS system dependency cycle rejected.");
    return result;
    function addEdge(from, to) {
        if (!byName.has(from))
            throw new ValidationError("MISSING_ECS_SYSTEM_DEPENDENCY", `Missing ECS system dependency: ${from}`);
        if (!byName.has(to))
            throw new ValidationError("MISSING_ECS_SYSTEM_DEPENDENCY", `Missing ECS system dependency: ${to}`);
        const edges = graph.get(from);
        if (!edges.has(to)) {
            edges.add(to);
            indegree.set(to, (indegree.get(to) ?? 0) + 1);
        }
    }
    function compareSystems(a, b) {
        return (a.priority ?? 0) - (b.priority ?? 0) || (insertion.get(a.name) ?? 0) - (insertion.get(b.name) ?? 0);
    }
}
//# sourceMappingURL=SystemScheduler.js.map