import { BoxHelper, Group } from 'three';
import { assert } from "../src/core/assert.js";
import { SignalBinding } from "../src/core/events/signal/SignalBinding.js";
import Mesh from "../src/engine/graphics/ecs/mesh/Mesh.js";
import { MeshEvents } from "../src/engine/graphics/ecs/mesh/MeshEvents.js";


class SelectionContainer {
    /**
     *
     * @param entity
     * @param {EntityManager} entityManager
     * @constructor
     */
    constructor(entity, entityManager) {
        assert.defined(entityManager, 'entityManager');
        assert.isNonNegativeInteger(entity, 'entity');

        this.entity = entity;
        this.entityManager = entityManager;

        this.isLinked = false;

        const boxHelper = new BoxHelper();
        boxHelper.material.depthTest = true;
        boxHelper.material.transparent = true;

        this.boxHelper = boxHelper;

        const self = this;

        function updateMesh() {
            const mesh = entityManager.dataset.getComponent(entity, Mesh);

            if (mesh !== null && mesh !== undefined && mesh.hasMesh()) {
                boxHelper.setFromObject(mesh.mesh);
                boxHelper.visible = true;
            } else {
                boxHelper.visible = false;
            }
        }

        function updateBox() {
            boxHelper.update();
        }

        function animationLoop() {
            if (!self.isLinked) {
                return;
            }
            updateBox();
            requestAnimationFrame(animationLoop);
        }

        updateMesh();

        this.handlers = {
            updateMesh,
            animationLoop
        };
    }

    link() {
        this.isLinked = true;
        this.handlers.animationLoop();

        const ecd = this.entityManager.dataset;
        ecd.addEntityEventListener(this.entity, MeshEvents.DataSet, this.handlers.updateMesh);
    }

    unlink() {
        this.isLinked = false;

        const ecd = this.entityManager.dataset;
        ecd.removeEntityEventListener(this.entity, MeshEvents.DataSet, this.handlers.updateMesh);
    }
}

export class SelectionVisualizer {
    /**
     *
     * @param {Editor} editor
     * @constructor
     */
    constructor(editor) {
        this.editor = editor;

        /**
         *
         * @returns {EntityManager}
         */
        function getEM() {
            return editor.engine.entityManager;
        }


        const group = this.group = new Group();

        const containers = [];

        function addEntity(entity) {
            //try to get a transform

            const em = getEM();

            const container = new SelectionContainer(entity, em);
            container.link();
            containers[entity] = container;

            group.add(container.boxHelper);
        }

        function removeEntity(entity) {
            const container = containers[entity];
            if (container === undefined) {
                console.error(`Failed to un-select entity ${entity}, it no container was registered.`);
                return;
            }
            container.unlink();
            delete containers[entity];

            group.remove(container.boxHelper);
        }

        this.handlers = {
            addEntity,
            removeEntity
        };

        this.bindings = [
            new SignalBinding(editor.selection.on.added, addEntity),
            new SignalBinding(editor.selection.on.removed, removeEntity)
        ]
    }

    startup() {
        this.editor.engine.graphics.scene.add(this.group);

        this.editor.selection.forEach(this.handlers.addEntity);

        this.bindings.forEach(function (b) {
            b.link();
        });
    }

    shutdown() {
        this.editor.engine.graphics.scene.remove(this.group);


        this.editor.selection.forEach(this.handlers.removeEntity);

        this.bindings.forEach(function (b) {
            b.unlink();
        });
    }
}
