import { ValidationError } from "./Errors.js";
export const SystemPhase = Object.freeze({
    Platform: "platform",
    Tasks: "tasks",
    Fixed: "fixed",
    Update: "update",
    Scene: "scene",
    Render: "render",
    Present: "present",
    Cleanup: "cleanup"
});
const phaseOrder = Object.values(SystemPhase);
export class Scheduler {
    tasks = new Map();
    add(task) {
        if (this.tasks.has(task.id))
            throw new ValidationError("DUPLICATE_TASK", `Task ${task.id} already exists.`);
        this.tasks.set(task.id, task);
    }
    remove(id) {
        this.tasks.delete(id);
    }
    getExecutionPlan() {
        const ordered = [];
        const byPhase = new Map();
        for (const task of this.tasks.values()) {
            if (task.enabled === false)
                continue;
            const list = byPhase.get(task.phase) ?? [];
            list.push(task);
            byPhase.set(task.phase, list);
        }
        for (const phase of phaseOrder) {
            ordered.push(...this.sortPhase(byPhase.get(phase) ?? []));
        }
        return Object.freeze(ordered);
    }
    async runPhase(phase, fixedStepIndex) {
        for (const task of this.getExecutionPlan()) {
            if (task.phase === phase) {
                const context = fixedStepIndex === undefined ? { phase } : { phase, fixedStepIndex };
                await task.run(context);
            }
        }
    }
    sortPhase(tasks) {
        const taskIds = new Set(tasks.map((task) => task.id));
        const visiting = new Set();
        const visited = new Set();
        const output = [];
        const taskById = new Map(tasks.map((task) => [task.id, task]));
        const visit = (task) => {
            if (visited.has(task.id))
                return;
            if (visiting.has(task.id))
                throw new ValidationError("TASK_CYCLE", `Scheduler cycle includes ${task.id}.`);
            visiting.add(task.id);
            for (const dependency of task.dependsOn ?? []) {
                const dep = this.tasks.get(dependency);
                if (!dep || dep.enabled === false)
                    throw new ValidationError("MISSING_DEPENDENCY", `Task ${task.id} depends on missing task ${dependency}.`);
                if (dep.phase !== task.phase)
                    throw new ValidationError("CROSS_PHASE_DEPENDENCY", `Task ${task.id} depends on ${dependency} in a different phase.`);
                if (!taskIds.has(dependency))
                    throw new ValidationError("MISSING_DEPENDENCY", `Task ${task.id} depends on missing phase peer ${dependency}.`);
                visit(taskById.get(dependency));
            }
            visiting.delete(task.id);
            visited.add(task.id);
            output.push(task);
        };
        for (const task of tasks)
            visit(task);
        return output;
    }
}
//# sourceMappingURL=Scheduler.js.map