import { BaseEntity } from './entity';
import { EventBus } from '../event-bus';
import { Query } from './query';
import { System } from './system';
export class World {
    constructor() {
        this.nextEntityID = 0;
        this.nextComponentID = 0;
        this.nextQueryID = 0;
        this.entities = new Array();
        this.systems = new Set();
        this.components = new Map();
        this.componentNamesToIDs = new Map();
        this.queryCache = new Array();
        this.eventBus = new EventBus();
    }
    run() {
        this.systems.forEach((system) => {
            system.execute(this);
        });
    }
    createSystem(systemExecutor) {
        const newSystem = new System(systemExecutor);
        this.systems.add(newSystem);
    }
    addSystem(system) {
        this.systems.add(system);
    }
    addEntity(entity) {
        this.entities.push(entity);
        this.markQueriesDirty();
    }
    removeEntity(entityToRemove) {
        this.entities = this.entities.filter((entity) => entity !== entityToRemove);
        this.markQueriesDirty();
    }
    createEntity(components) {
        const newEntity = new BaseEntity();
        newEntity.id = this.nextEntityID;
        this.nextEntityID++;
        components.forEach((component) => {
            if (this.componentNamesToIDs.has(component.name)) {
                component.id = this.componentNamesToIDs.get(component.name);
            }
            else {
                this.componentNamesToIDs.set(component.name, this.nextComponentID);
                component.id = this.nextComponentID;
                this.nextComponentID++;
            }
            newEntity.addComponent(component);
        });
        this.entities.push(newEntity);
        this.markQueriesDirty();
        return newEntity;
    }
    extendEntity(entity, components) {
        const toClone = this.entities.find((found) => entity.name === found.constructor.name);
        const cloned = new BaseEntity();
        cloned.id = this.nextEntityID;
        this.nextEntityID++;
        toClone.components.forEach((component) => {
            cloned.addComponent(this.components.get(component.id));
        });
        components.forEach((component) => {
            if (this.componentNamesToIDs.has(component.name)) {
                component.id = this.componentNamesToIDs.get(component.name);
            }
            else {
                this.componentNamesToIDs.set(component.name, this.nextComponentID);
                component.id = this.nextComponentID;
                this.nextComponentID++;
            }
            cloned.addComponent(component);
        });
        this.entities.push(cloned);
        return cloned;
    }
    createComponent(component) {
        const newComponent = component;
        newComponent.id = this.nextComponentID;
        this.nextComponentID++;
        this.components.set(newComponent.id, newComponent);
        this.componentNamesToIDs.set(component.name, component.id);
        return newComponent;
    }
    query(include, exclude) {
        const query = new Query(include, exclude, this);
        const cache = this.queryCache.find((item) => item.include == include && item.exclude == exclude);
        if (cache) {
            return cache.execute();
        }
        this.queryCache.push(query);
        return query.execute();
    }
    createQuery(include, exclude) {
        const newQuery = new Query(include, exclude, this);
        newQuery.id = this.nextQueryID;
        this.nextQueryID++;
        this.queryCache.push(newQuery);
        return newQuery;
    }
    markQueriesDirty() {
        this.queryCache.forEach((query) => (query.isDirty = true));
    }
}
