/**
 * Минимальный ECS: сущность — число, компоненты — объекты по имени, системы — функции.
 * Хватает для 2D-игр: без метапрограммирования, всё видно в отладчике.
 */

export type Entity = number;

export interface System {
    /** Вызывается каждый тик фиксированного шага. dt — секунды. */
    update(world: World, dt: number): void;
}

export class World {
    private nextId = 1;
    private alive = new Set<Entity>();
    /** entity -> (имя компонента -> компонент). */
    private components = new Map<Entity, Map<string, object>>();
    private systems: System[] = [];

    createEntity(): Entity {
        const e = this.nextId++;
        this.alive.add(e);
        this.components.set(e, new Map());
        return e;
    }

    destroyEntity(e: Entity): void {
        this.alive.delete(e);
        this.components.delete(e);
    }

    isAlive(e: Entity): boolean {
        return this.alive.has(e);
    }

    addComponent<T extends object>(e: Entity, name: string, component: T): T {
        this.components.get(e)!.set(name, component);
        return component;
    }

    getComponent<T extends object>(e: Entity, name: string): T | undefined {
        return this.components.get(e)?.get(name) as T | undefined;
    }

    hasComponent(e: Entity, name: string): boolean {
        return this.components.get(e)?.has(name) ?? false;
    }

    removeComponent(e: Entity, name: string): void {
        this.components.get(e)?.delete(name);
    }

    /** Все живые сущности с указанным компонентом. */
    query(...names: string[]): Entity[] {
        const out: Entity[] = [];
        for (const e of this.alive) {
            const map = this.components.get(e)!;
            let ok = true;
            for (const n of names) {
                if (!map.has(n)) {
                    ok = false;
                    break;
                }
            }
            if (ok) out.push(e);
        }
        return out;
    }

    addSystem(system: System): void {
        this.systems.push(system);
    }

    update(dt: number): void {
        for (const s of this.systems) {
            s.update(this, dt);
        }
    }
}