import { ValidationError } from "../core/index.js";
import { Camera } from "./Camera.js";
import { DirectionalLight } from "./DirectionalLight.js";
import { Light } from "./Light.js";
import { Group, InstancedMesh, Mesh, Object3D, SkinnedMesh } from "./Object3D.js";
import { OrthographicCamera } from "./OrthographicCamera.js";
import { PerspectiveCamera } from "./PerspectiveCamera.js";
import { PointLight } from "./PointLight.js";
import { Renderable } from "./Renderable.js";
import { collectRenderables, queryScene } from "./SceneQuery.js";
import { SceneMetadata } from "./SceneMetadata.js";
import { SceneNode } from "./SceneNode.js";
import { SpotLight } from "./SpotLight.js";
export class Scene {
    root = new SceneNode({ id: "root", name: "root" });
    metadata = new SceneMetadata();
    nodesById = new Map([[this.root.id, this.root]]);
    createNode(name) {
        return this.register(new SceneNode({ name }));
    }
    createObject3D(options = {}) {
        return this.register(new Object3D(options));
    }
    createGroup(options = {}) {
        return this.register(new Group(options));
    }
    createMesh(options = {}) {
        return this.register(new Mesh(options));
    }
    createSkinnedMesh(options = {}) {
        return this.register(new SkinnedMesh(options));
    }
    createInstancedMesh(options = {}) {
        return this.register(new InstancedMesh(options));
    }
    createPerspectiveCamera(options = {}) {
        return this.register(new PerspectiveCamera(options));
    }
    createOrthographicCamera(options = {}) {
        return this.register(new OrthographicCamera(options));
    }
    createLight(kind, name) {
        const light = kind === "directional" ? new DirectionalLight(name) : kind === "point" ? new PointLight(name) : new SpotLight(name);
        return this.register(light);
    }
    addRenderable(node, renderable) {
        node.renderable = renderable;
    }
    getNodeById(id) {
        return this.nodesById.get(id);
    }
    findByName(name) {
        return this.query({ name });
    }
    query(options) {
        if (options.bounds)
            this.updateWorldBounds();
        return queryScene(this.root, options);
    }
    updateWorldTransforms() {
        this.root.updateWorldTransform();
    }
    updateWorldBounds() {
        this.updateWorldTransforms();
        this.root.updateWorldBounds();
    }
    update() {
        this.updateWorldBounds();
    }
    traverse(visitor) {
        this.root.traverse(visitor);
    }
    collectRenderables() {
        return collectRenderables(this.root);
    }
    collectCameras() {
        return this.query({ type: "camera" }).filter((node) => node instanceof Camera);
    }
    collectLights() {
        return this.query({ type: "light" }).filter((node) => node instanceof Light);
    }
    removeNode(node) {
        if (node === this.root)
            return false;
        const descendants = [];
        node.traverse((descendant) => descendants.push(descendant));
        const removed = node.removeFromParent();
        for (const descendant of descendants)
            this.nodesById.delete(descendant.id);
        return removed;
    }
    dispose() {
        const descendants = [];
        for (const child of [...this.root.children])
            child.traverse((descendant) => descendants.push(descendant));
        for (const child of [...this.root.children])
            child.dispose();
        for (const descendant of descendants)
            this.nodesById.delete(descendant.id);
        this.root.children.length = 0;
    }
    registerSubtree(node) {
        node.traverse((descendant) => {
            if (this.nodesById.has(descendant.id)) {
                throw new ValidationError("DUPLICATE_SCENE_NODE", `Duplicate scene node id: ${descendant.id}`);
            }
            this.nodesById.set(descendant.id, descendant);
        });
    }
    register(node) {
        if (this.nodesById.has(node.id))
            throw new ValidationError("DUPLICATE_SCENE_NODE", `Duplicate scene node id: ${node.id}`);
        this.nodesById.set(node.id, node);
        return node;
    }
}
//# sourceMappingURL=Scene.js.map