import { array_set_diff_sorting } from "../../../src/core/collection/array/array_set_diff_sorting.js";
import List from "../../../src/core/collection/list/List.js";
import Signal from "../../../src/core/events/signal/Signal.js";
import { number_compare_ascending } from "../../../src/core/primitives/numbers/number_compare_ascending.js";
import Name from "../../../src/engine/ecs/name/Name.js";
import { ParentEntity } from "../../../src/engine/ecs/parent/ParentEntity.js";
import { MouseEvents } from "../../../src/engine/input/devices/events/MouseEvents.js";
import LabelView from "../../../src/view/common/LabelView.js";
import ListView from "../../../src/view/common/ListView.js";
import EmptyView from "../../../src/view/elements/EmptyView.js";
import style from "./HierarchicalEntityListView.module.scss";

export class HierarchicalEntityListView extends EmptyView {

    /**
     *
     * @type {EntityComponentDataset|null}
     */
    #dataset = null;

    #roots = new List();

    #child_map = new Map();

    #selected = new List();


    #update_roots() {
        const new_roots = [];

        const ecd = this.#dataset;

        ecd.traverseEntityIndices(id => {

            const parent = ecd.getComponent(id, ParentEntity);

            if (parent !== undefined) {
                const childMap = this.#child_map;
                let children = childMap.get(parent.entity);
                if (children === undefined) {
                    children = [];
                    childMap.set(parent.entity, children);
                }
                children.push(id);

                return;
            }

            new_roots.push(id);
        });

        // perform patch
        const diff = array_set_diff_sorting(new_roots, this.#roots.asArray(), number_compare_ascending);

        for (let i = 0; i < diff.uniqueB.length; i++) {
            const removal_id = diff.uniqueB[i];

            this.#roots.removeOneOf(removal_id);
        }


        for (let i = 0; i < diff.uniqueA.length; i++) {
            const addition_id = diff.uniqueA[i];

            this.#roots.add(addition_id);
        }
    }

    set selection(entities) {
        this.#selected.patch(entities);
    }

    set dataset(v) {
        this.#roots.reset();

        this.#dataset = v;

        this.#update_roots();
    }

    /**
     *
     * @param {number} entity
     */
    #make_entity_view(entity) {

        const ecd = this.#dataset;

        const children = this.#child_map.get(entity);

        const has_children = children !== undefined;

        const entity_view = new EmptyView({
            classList:[style.entityView],
            css: {
                position: 'relative',
                pointerEvents: "auto",
            }
        });

        let label_text = `${entity}`;

        const name = ecd.getComponent(entity, Name);

        if (name !== undefined) {
            label_text += `: ${name}`
        }

        entity_view.addChild(new LabelView(label_text, {
            css: {
                position: 'relative'
            }
        }));

        if (has_children) {

            const children_container = new EmptyView({
                css: {
                    paddingLeft: "8px"
                }
            });

            for (let i = 0; i < children.length; i++) {

                const child = children[i];

                const child_view = this.#make_entity_view(child);

                children_container.addChild(child_view);

            }

            entity_view.addChild(children_container);

        }

        entity_view.el.addEventListener(MouseEvents.Click, (evt) => {
            this.on.interact.send1(entity);

            evt.stopPropagation();
        });


        const update_selection = () => {
            const is_selected = this.#selected.contains(entity);

            entity_view.setClass("selected", is_selected);
        }

        entity_view.bindSignal(this.#selected.on.added, update_selection);
        entity_view.bindSignal(this.#selected.on.removed, update_selection);
        entity_view.on.linked.add(update_selection);

        return entity_view;
    }

    constructor() {
        super();

        const listView = new ListView(this.#roots, {
            classList: [
                style.main
            ],
            elementFactory: (entity) => {

                return this.#make_entity_view(entity);
            },
        });

        listView.css({
            display: 'flex',
            flexDirection: 'column'
        })

        this.size.onChanged.add(listView.size.set, listView.size);

        this.on.interact = new Signal();

        this.addChild(listView);
    }

    link() {
        super.link();

        this.#update_roots();
    }

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