import { EventBus } from '../core/EventBus.js';

/* ────────────────────────────────────────────────────────────────
   ECS — Entity-Component-System architecture
──────────────────────────────────────────────────────────────── */
/* ────────────────────────────────────────────────────────────────
   COMPONENT DEFAULTS
──────────────────────────────────────────────────────────────── */
const WHITE = { r: 1, g: 1, b: 1, a: 1 };
const BLACK = { r: 0, g: 0, b: 0, a: 1 };
const ZERO = { x: 0, y: 0 };
const ONE = { x: 1, y: 1 };
function createDefaultComponent(type, entityId) {
    const base = { entityId, enabled: true };
    switch (type) {
        case 'Transform':
            return { ...base, type: 'Transform', position: { ...ZERO }, rotation: 0, scale: { ...ONE }, prevPosition: { ...ZERO }, prevRotation: 0 };
        case 'Sprite':
            return { ...base, type: 'Sprite', assetId: null, srcRect: null, width: 32, height: 32, anchor: { x: 0.5, y: 0.5 }, tint: { ...WHITE }, opacity: 1, flipX: false, flipY: false, layer: 0, visible: true };
        case 'Shape':
            // as unknown as: TS can't narrow generic K in switch; Shape.shape conflicts with Collider2D.shape
            return { ...base, type: 'Shape', shape: 'rect', width: 32, height: 32, radius: 16, points: [], lineStart: { ...ZERO }, lineEnd: { x: 32, y: 0 }, fillColor: { ...WHITE }, strokeColor: { ...BLACK }, strokeWidth: 0, layer: 0, visible: true };
        case 'Collider2D':
            return { ...base, type: 'Collider2D', shape: 'aabb', width: 32, height: 32, radius: 16, points: [], offset: { ...ZERO }, isTrigger: false, layer: 1, mask: 0xFFFF };
        case 'RigidBody2D':
            return { ...base, type: 'RigidBody2D', bodyType: 'dynamic', velocity: { ...ZERO }, acceleration: { ...ZERO }, angularVelocity: 0, mass: 1, drag: 0, angularDrag: 0, restitution: 0.3, friction: 0.5, gravityScale: 1, fixedRotation: false };
        case 'Text':
            return { ...base, type: 'Text', text: '', fontSize: 16, fontFamily: 'sans-serif', color: { ...WHITE }, align: 'center', baseline: 'middle', layer: 10, visible: true };
        case 'Animator':
            return { ...base, type: 'Animator', currentAnimation: '', animations: {}, frame: 0, elapsed: 0, playing: false, loop: true };
        case 'Script':
            return { ...base, type: 'Script', scriptId: '', variables: {} };
        case 'AudioSource':
            return { ...base, type: 'AudioSource', soundId: '', volume: 1, loop: false, spatial: false, maxDistance: 500, autoPlay: false, playing: false };
        case 'ParticleEmitter':
            return { ...base, type: 'ParticleEmitter', emitting: false, rate: 10, lifetime: [0.5, 1.5], speed: [50, 150], angle: [0, 360], size: [2, 6], sizeEnd: [0, 0], color: { ...WHITE }, colorEnd: { r: 1, g: 1, b: 1, a: 0 }, gravity: { x: 0, y: 100 }, maxParticles: 100 };
        case 'CameraTarget':
            return { ...base, type: 'CameraTarget', priority: 0, smoothing: 0.1, deadzone: { x: 50, y: 30 }, lookahead: { ...ZERO } };
        default:
            throw new Error(`Unknown component type: ${type}`);
    }
}
/* ────────────────────────────────────────────────────────────────
   SYSTEM — base class for game systems
──────────────────────────────────────────────────────────────── */
class System {
    constructor(priority = 0) {
        this.enabled = true;
        this.priority = priority;
    }
    /** Called once when added to world. */
    init(_world) { }
    /** Called every frame. */
    update(_world, _dt) { }
    /** Called at fixed timestep (physics). */
    fixedUpdate(_world, _fixedDt) { }
    /** Called when removed from world. */
    destroy(_world) { }
    /** Alias for destroy() — unified cleanup pattern. */
    dispose(world) { this.destroy(world); }
}
/* ────────────────────────────────────────────────────────────────
   WORLD — Container for entities, components, and systems
──────────────────────────────────────────────────────────────── */
class World {
    constructor() {
        this.events = new EventBus();
        this._nextId = 1;
        this.entities = new Map();
        this.components = new Map();
        this.systems = [];
        this.systemsSorted = false;
    }
    /* ── Entity management ──────────────────────────────────────── */
    createEntity(name = 'Entity') {
        const id = this._nextId++;
        const entity = {
            id,
            name,
            tags: new Set(),
            enabled: true,
            parent: null,
            children: [],
        };
        this.entities.set(id, entity);
        this.events.emit('entity:created', id);
        return entity;
    }
    destroyEntity(id) {
        const entity = this.entities.get(id);
        if (!entity) {
            if (typeof console !== 'undefined')
                console.warn(`[World] destroyEntity: entity ${id} not found`);
            return;
        }
        // Destroy children recursively
        for (const childId of [...entity.children]) {
            this.destroyEntity(childId);
        }
        // Remove from parent
        if (entity.parent != null) {
            const parent = this.entities.get(entity.parent);
            if (parent) {
                parent.children = parent.children.filter(c => c !== id);
            }
        }
        // Remove all components
        for (const [, compMap] of this.components) {
            compMap.delete(id);
        }
        this.entities.delete(id);
        this.events.emit('entity:destroyed', id);
    }
    getEntity(id) {
        return this.entities.get(id);
    }
    /** Get all entities (alive). */
    allEntities() {
        return Array.from(this.entities.values());
    }
    /** Find first entity with given tag. */
    findByTag(tag) {
        for (const e of this.entities.values()) {
            if (e.tags.has(tag))
                return e;
        }
        return undefined;
    }
    /** Find all entities with given tag. */
    findAllByTag(tag) {
        const result = [];
        for (const e of this.entities.values()) {
            if (e.tags.has(tag))
                result.push(e);
        }
        return result;
    }
    /** Find first entity with given name. */
    findByName(name) {
        for (const e of this.entities.values()) {
            if (e.name === name)
                return e;
        }
        return undefined;
    }
    get entityCount() {
        return this.entities.size;
    }
    /* ── Parenting ──────────────────────────────────────────────── */
    setParent(childId, parentId) {
        const child = this.entities.get(childId);
        if (!child) {
            if (typeof console !== 'undefined')
                console.warn(`[World] setParent: entity ${childId} not found`);
            return;
        }
        // Remove from old parent
        if (child.parent != null) {
            const oldParent = this.entities.get(child.parent);
            if (oldParent) {
                oldParent.children = oldParent.children.filter(c => c !== childId);
            }
        }
        child.parent = parentId;
        if (parentId != null) {
            const parent = this.entities.get(parentId);
            if (parent && !parent.children.includes(childId)) {
                parent.children.push(childId);
            }
        }
    }
    /* ── Component management ───────────────────────────────────── */
    addComponent(entityId, type, overrides) {
        const comp = createDefaultComponent(type, entityId);
        if (overrides)
            Object.assign(comp, overrides, { type, entityId });
        let map = this.components.get(type);
        if (!map) {
            map = new Map();
            this.components.set(type, map);
        }
        map.set(entityId, comp);
        this.events.emit('component:added', { entityId, type });
        return comp;
    }
    removeComponent(entityId, type) {
        const map = this.components.get(type);
        if (map) {
            map.delete(entityId);
            this.events.emit('component:removed', { entityId, type });
        }
    }
    getComponent(entityId, type) {
        const map = this.components.get(type);
        return map === null || map === void 0 ? void 0 : map.get(entityId);
    }
    hasComponent(entityId, type) {
        var _a;
        const map = this.components.get(type);
        return (_a = map === null || map === void 0 ? void 0 : map.has(entityId)) !== null && _a !== void 0 ? _a : false;
    }
    /** Query all entities that have ALL specified components. */
    query(...types) {
        if (types.length === 0)
            return [];
        const maps = types.map(t => this.components.get(t));
        if (maps.some(m => !m))
            return [];
        // Start with smallest set for efficiency
        const sorted = maps.slice().sort((a, b) => { var _a, _b; return ((_a = a === null || a === void 0 ? void 0 : a.size) !== null && _a !== void 0 ? _a : 0) - ((_b = b === null || b === void 0 ? void 0 : b.size) !== null && _b !== void 0 ? _b : 0); });
        const smallest = sorted[0];
        const rest = sorted.slice(1);
        const result = [];
        for (const id of smallest.keys()) {
            if (rest.every(m => m.has(id))) {
                const entity = this.entities.get(id);
                if (entity === null || entity === void 0 ? void 0 : entity.enabled)
                    result.push(id);
            }
        }
        return result;
    }
    /** Get all components of a type (as a Map). */
    getComponentsOfType(type) {
        var _a;
        return ((_a = this.components.get(type)) !== null && _a !== void 0 ? _a : new Map());
    }
    /* ── System management ──────────────────────────────────────── */
    addSystem(system) {
        this.systems.push(system);
        this.systemsSorted = false;
        system.init(this);
        this.events.emit('system:added', system);
    }
    removeSystem(system) {
        const idx = this.systems.indexOf(system);
        if (idx >= 0) {
            system.destroy(this);
            this.systems.splice(idx, 1);
        }
    }
    ensureSorted() {
        if (!this.systemsSorted) {
            this.systems.sort((a, b) => a.priority - b.priority);
            this.systemsSorted = true;
        }
    }
    /* ── Update ─────────────────────────────────────────────────── */
    update(dt) {
        this.ensureSorted();
        for (const sys of this.systems) {
            if (sys.enabled)
                sys.update(this, dt);
        }
    }
    fixedUpdate(fixedDt) {
        this.ensureSorted();
        for (const sys of this.systems) {
            if (sys.enabled)
                sys.fixedUpdate(this, fixedDt);
        }
    }
    /* ── Serialization ──────────────────────────────────────────── */
    /** Clear all entities, components, and systems. */
    clear() {
        for (const sys of this.systems)
            sys.destroy(this);
        this.systems.length = 0;
        this.entities.clear();
        this.components.clear();
        this._nextId = 1;
    }
}

export { System, World, createDefaultComponent };
//# sourceMappingURL=World.js.map
