import List from '../../../src/core/collection/list/List.js';
import ObservedValue from '../../../src/core/model/ObservedValue.js';
import { isDefined } from "../../../src/core/process/matcher/isDefined.js";
import { EntityManager } from "../../../src/engine/ecs/EntityManager.js";
import { EventType } from "../../../src/engine/ecs/EventType.js";
import LabelView from '../../../src/view/common/LabelView.js';
import dom from "../../../src/view/DOM.js";
import ButtonView from '../../../src/view/elements/button/ButtonView.js';
import DropDownSelectionView from '../../../src/view/elements/DropDownSelectionView.js';
import EmptyView from "../../../src/view/elements/EmptyView.js";
import View from "../../../src/view/View.js";
import ComponentAddAction from "../../actions/concrete/ComponentAddAction.js";
import ComponentRemoveAction from "../../actions/concrete/ComponentRemoveAction.js";
import { buildObjectEditorFromRegistry } from "../../ecs/component/createObjectEditor.js";
import { ComponentControlView } from "./ComponentControlView.js";
import { LineView } from "./components/common/LineView.js";


class EntityEditor extends View {
    /**
     *
     * @param {ComponentControlFactory} componentControlFactory
     * @param {Editor} editor
     * @constructor
     */
    constructor(componentControlFactory, editor) {

        super();


        const dRoot = dom('div');

        dRoot.addClass('entity-editor-view');

        this.el = dRoot.el;
        const self = this;

        this.model = new ObservedValue(null);
        /**
         * @type {ObservedValue<EntityManager>}
         */
        this.entityManager = new ObservedValue(null);
        this.components = new List();

        const vComponentList = new EmptyView({ classList: ['component-list'] });

        /**
         *
         * @type {Map<any, TypeEditor>}
         */
        const registry = editor.type_editor_registry;

        /**
         *
         * @type {Map<Object, ComponentControlView>}
         */
        this.componentControllers = new Map();

        /**
         *
         * @returns {EntityComponentDataset}
         */
        function get_dataset() {
            return self.entityManager.get().dataset;
        }

        function addComponent(event) {
            if (event.instance === undefined) {
                return;
            }

            self.components.add(event.instance);
        }

        function removeComponent(event) {
            // console.log('removeComponent.Event',event, self.components);

            if (event.instance === undefined) {
                return;
            }

            try {
                self.components.removeOneOf(event.instance);
            } catch (e) {
                console.error(e);
            }
        }

        function watchEntity(entity) {

            const dataset = get_dataset();

            if (!dataset.entityExists(entity)) {
                //doesn't exist, nothing to do
                return;
            }

            dataset.addEntityEventListener(entity, EventType.ComponentAdded, addComponent);
            dataset.addEntityEventListener(entity, EventType.ComponentRemoved, removeComponent);
            dataset.addEntityEventListener(entity, EventType.EntityRemoved, unwatchEntity);
        }

        function unwatchEntity(entity) {

            const dataset = get_dataset();

            if (!dataset.entityExists(entity)) {
                //doesn't exist, nothing to do
                return;
            }

            dataset.removeEntityEventListener(entity, EventType.ComponentAdded, addComponent);
            dataset.removeEntityEventListener(entity, EventType.ComponentRemoved, removeComponent);
            dataset.removeEntityEventListener(entity, EventType.EntityRemoved, unwatchEntity);
        }

        this.model.onChanged.add(function (entity, oldEntity) {
            if (oldEntity !== undefined && oldEntity !== null) {
                unwatchEntity(oldEntity);
            }
            watchEntity(entity);

            self.components.reset();


            /**
             *
             * @type {EntityComponentDataset}
             */
            const dataset = get_dataset();

            unattachedTypes.reset();
            unattachedTypes.addAll(dataset.getComponentTypeMap().map(a => a.typeName).filter(isDefined));

            const components = dataset.getAllComponents(entity);
            components.forEach(function (c) {
                self.components.add(c);
            });
        });

        this.vLabelEntity = new LabelView(this.model, {
            classList: ['id', 'label']
        });

        this.addChild(vComponentList);


        const unattachedTypes = new List();

        /**
         * @template T
         * @param {T} component
         */
        function handleComponentAdded(component) {
            let view;

            try {
                view = buildObjectEditorFromRegistry(component, registry);
            } catch (e) {
                view = new LabelView(`ERROR: ${e.message}\n${e.stack}`);

                console.warn('Failed to build editor', e);
            }

            if (!view) {
                view = new LabelView('EMPTY');
            }

            /**
             *
             * @type {EntityManager}
             */
            const entityManager = self.entityManager.getValue();

            const entityId = self.model.getValue();

            const vBody = new EmptyView({
                classList: ['body']
            });
            vBody.addChild(view);

            const controlView = new ComponentControlView(entityId, component, entityManager, vBody, editor.engine);

            const Klass = component.constructor;

            controlView.signal.remove.add(function () {

                editor.actions.mark('Remove Component');
                editor.actions.do(new ComponentRemoveAction(entityId, Klass));
            });


            vComponentList.addChild(controlView);
            self.componentControllers.set(component, controlView);

            unattachedTypes.removeOneOf(Klass.typeName);

        }

        function handleComponentRemoved(c) {
            // console.log("handleComponentRemoved",c);
            if (self.componentControllers.has(c)) {
                const controlView = self.componentControllers.get(c);
                self.componentControllers.delete(c);
                vComponentList.removeChild(controlView);

                const Klass = c.constructor;

                unattachedTypes.add(Klass.typeName);
            }
        }

        this.components.on.added.add(handleComponentAdded);
        this.components.on.removed.add(handleComponentRemoved);


        const typeSelection = new DropDownSelectionView(unattachedTypes);

        const buttonView = new ButtonView({
            classList: ['add-component'],
            name: "Add",
            action: function () {
                const selectedValue = typeSelection.getSelectedValue();
                const em = self.entityManager.get();
                const ComponentClass = em.getComponentClassByName(selectedValue);
                const component = new ComponentClass();

                const entityIndex = self.model.get();

                editor.actions.mark('Add Component');
                editor.actions.do(new ComponentAddAction(entityIndex, component));
            }
        });

        this.addChild(new LineView({
            elements: [this.vLabelEntity, buttonView, typeSelection],
            classList: ['component-adder']
        }));

        this.handlers = {};
    }

    link() {
        super.link()


    }

    unlink() {
        super.unlink();
    }
}


export default EntityEditor;
