// Global ATON
import { Controller } from "@hotwired/stimulus";
import AppState from "../state.js";
import { loadOntology, traverseOntology, Ontology } from "../ontology.js";

const html = String.raw;
const domParser = new DOMParser;
// TODO: hard-coded, but follows a convention...
const ontologyJsonPath = '/a/scaenae/scenes/BaroqueTheatreOntology_BT_v1_8_ATON_by_domain_with_instances.json';

export default class extends Controller {
    static targets = ['trigger', 'layers', 'ontology'];

    async connect() {
        console.log('#menu controller connected');
        this.ontologyObject = await loadOntology(ontologyJsonPath);
    }
    /**
     * Open settings panel
     */
    async toggleMenu() {
        ATON.UI.setSidePanelRight();
        ATON.UI.showSidePanel({header: 'Menu'});
        this.#buildMenuPanel(ATON.UI.elSidePanel);
        this.#buildLayersMenu(AppState.treeNodes, this.layersTarget);
        this.#buildOntologyMenu(await traverseOntology(this.ontologyObject), this.ontologyTarget);
    }
    /**
     * @param {Event} event 
     */
    toggleNode(event) {

        console.debug(AppState.treeNodes);

        /**
         * The node's id
         * @type {string}
         */
        const id = event?.params.node;
        const status = event?.target?.checked;
        const node = AppState.normalizedNodes.find(n => n.id === id);
        /**
         * @type {HTMLElement|null}
         */
        const eye = event.target.parentElement.querySelector('i');

        if (eye) {
            eye.classList.toggle('bi-eye');
            eye.classList.toggle('bi-eye-slash');
        }

        if (node.children.length > 0) {
            this.#toggleGroup(node, status);
            this.#syncGroupCheckboxes(node, status, this.layersTarget);
        } else {
            ATON.getSceneNode(id).toggle(status);
            node.active = status;
        }
    }
    /**
     * Recursively toggle children in a nodes group
     * @param {Object} groupNode
     * @param {Boolean} status
     */
    #toggleGroup(groupNode, status) {
        for (const child of groupNode.children) {
            child.active = status;
            if (child.model) {
                ATON.getSceneNode(child.id).toggle(status);
            }
            if (child.children.length > 0) {
                this.#toggleGroup(child, status);
            }
        }
    }
    /**
     * Toggles checkboxes in a group based on status
     * @param {Object} groupNode
     * @param {Boolean} status
     * @param {HTMLElement} container
     */
    #syncGroupCheckboxes(groupNode, status, container) {
        for (const child of groupNode.children) {
            const checkbox = container.querySelector(
                `[data-menu-node-param="${child.id}"]`
            );

            if (checkbox) checkbox.checked = status;
            
            if (child.children.length > 0) {
                this.#syncGroupCheckboxes(child, status, container);
            }
        }
    }
    /**
     * Clone a <template> by id
     * @param {String} id
     * @returns {DocumentFragment}
     */
    #cloneTemplate(id) {
        return document.getElementById(id)?.content?.cloneNode(true);
    }
    /**
     * Create the left-side settings panel
     * content
     * @param {Element} panel 
     */
    #buildMenuPanel(panel) {
        const fragment = this.#cloneTemplate('tmpl-menu-tabs');

        panel.appendChild(fragment);
    }
    /**
     * @todo Don't rebuild it every time, use caching, return a container with checkboxes
     * @param {Array<import("../state.js").NormalizedSceneNode>} tree The normalized scene nodes tree
     * @param {HTMLElement} tab Tab content element
     */
    #buildLayersMenu(tree, tab) {
        const heading = document.createElement('h1');
        heading.classList.add('fs-5', 'fw-bold', 'ms-0');
        heading.textContent = 'Teatro'; // Hard-coded!!

        tab.appendChild(heading);

        for (const node of tree) {
            tab.appendChild(
                node.children.length > 0
                    ? this.#createLayerGroup(node)
                    : this.#createLayerToggle(node)
            );
        }
    }
    /**
     * @param {import("../state.js").NormalizedSceneNode} node 
     * @param {boolean} isGroup
     * @returns {HTMLDivElement}
     */
    #createLayerToggle(node, isGroup = false) {
        // This should be calculated somehow!
        const labelIndent = node.depth < 3 ? '1.5rem' : '1rem';
        const labelText = isGroup ? '<i class="bi bi-eye"></i>' : node.id;
        const toggle = html`
            <label class="toggle-control ms-${node.depth} ps-${node.depth} mt-1 mb-1">
                <input id="${node.id}" type="checkbox" ${node.active ? 'checked' : ''} role="switch"
                    data-menu-node-param="${node.id}"
                    data-action="change->menu#toggleNode">
                <span class="control"></span>
                <span class="ps-2 fs-6" title="Mostra / nascondi ${isGroup ? 'gruppo' : 'layer'}" style="margin-left: ${labelIndent}">
                    ${labelText}
                </span>
            </label>
        `;

        return domParser.parseFromString(toggle, 'text/html').querySelector('label');
    }
    /**
     * @param {import("../state.js").NormalizedSceneNode} node 
     * @returns {HTMLDetailsElement}
     */
    #createLayerGroup(node) {
        const { trigger, collapseDiv } = this.#createNodeCollapse(node);
        const wrapper = document.createElement('div');
        wrapper.classList.add('w-max-content', 'ps-1', 'mb-1');
            
        collapseDiv.appendChild(this.#createLayerToggle(node, true));

        for (const child of node.children) {
            collapseDiv.appendChild(
                child.children.length > 0
                    ? this.#createLayerGroup(child)
                    : this.#createLayerToggle(child)
            );
        }

        wrapper.appendChild(trigger);
        wrapper.appendChild(collapseDiv);
        wrapper.classList.add('border-start', 'border-bottom', 'rounded', `ms-${node.depth}`);

        return wrapper;
    }
    /**
     * 
     * @param {import("../state.js").NormalizedSceneNode} node 
     * @returns {{trigger: HTMLButtonElement, collapseDiv: HTMLDivElement}}
     */
    #createNodeCollapse(node) {
        const cleanId = node.id.replace(/[\s\/\-]+/g, '');
        const trigger = document.createElement('button');
        trigger.className = 'btn btn-link p-0 fs-6 fw-bold text-decoration-none text-reset';
        trigger.setAttribute('data-bs-toggle', 'collapse');
        trigger.setAttribute('data-bs-target', `#group-${cleanId}`);
        trigger.setAttribute('data-action', 'menu#toggleChevron');
        trigger.innerHTML = html`
            <i class="bi bi-chevron-down me-1"></i>${node.id}
        `;
        // Add color "swatch" only for first level groups
        /*
        if (node.depth === 2) {
            trigger.innerHTML += html`
                <div class="d-inline-block border rounded ms-2"
                    style="background-color: ${node.color}; height: 15px; width:15px">
                </div>
            `;
        }
        */

        const collapseDiv = document.createElement('div');
        collapseDiv.className = 'collapse';
        collapseDiv.id = `group-${cleanId}`;

        return {trigger, collapseDiv};
    }
    /**
     * 
     * @param {Event} event 
     */
    toggleChevron(event) {
        /**
         * @type {HTMLButtonElement} collapse
         */
        const collapse = event.target;
        const icon = collapse.querySelector('i');
        icon.classList.toggle('bi-chevron-down');
        icon.classList.toggle('bi-chevron-up');
    }
    /**
     * Temporary implementation to show domains only
     * @todo Don't rebuild it every time, use caching, return a container
     * @param {Array<{id: string, tree: Array<Object>}>} domains The traversed ontology domains
     * @param {HTMLElement} tab Tab content element
     */
    #buildOntologyMenu(domains, tab) {
        const ontology = new Ontology;
        ontology.data = this.ontologyObject; 

        const tabs = this.#createOntologyTabs().tabs;
        const content = this.#createOntologyTabs().content;

        tab.appendChild(tabs);
        tab.appendChild(content);

        const classesTab = content.querySelector('#classes');
        const instancesTab = content.querySelector('#instances');

        for (let domain of domains) {
            const heading = document.createElement('span');
            heading.className ="fs-5 fw-bold";
            heading.textContent = domain.id;
            classesTab.appendChild(heading);

            let domainList = document.createElement('div');

            // Traverse class trees for each domain
            for (let item of domain.tree) {
                const domainItem = document.createElement('div');
                domainItem.className = 'border-start border-bottom rounded border-1 border-light-subtle';
                domainItem.textContent = item.label.it ?? item.label.en;
                // Indentation...
                domainItem.classList.add(`ms-${item.depth}`, `ps-${item.depth}`, 'py-2');

                if (item.depth === 1) domainItem.classList.add('fw-bold');

                domainList.appendChild(domainItem);
            }

            classesTab.appendChild(domainList);
        }

        const sortedItLabels = ontology.getInstanceLabels().map(l => l.it).sort();

        for (let label of sortedItLabels) {
            const instanceItem = document.createElement('div');
            instanceItem.className = 'border-bottom ps-3 py-2';
            instanceItem.textContent = label;

            instancesTab.appendChild(instanceItem);
        }

        console.debug(ontology.getInstanceById('ex:SSGJP_StructuralBackdrop'), ontology.getInstanceLabels());
    }

    /**
     * @returns {{tabs: HTMLUListElement, content: HTMLDivElement}}
     */
    #createOntologyTabs() {
        const tabs = html`
            <ul class="nav nav-pills" role="tablist" id="ontoTabs">
                <li class="nav-item" role="presentation">
                    <button class="nav-link active" id="classes-tab" data-bs-toggle="tab" data-bs-target="#classes" type="button"
                        role="tab" aria-controls="classes-tab-pane" aria-selected="true">Classi</button>
                </li>
                <li class="nav-item" role="presentation">
                    <button class="nav-link" id="instances-tab" data-bs-toggle="tab" data-bs-target="#instances" type="button"
                        role="tab" aria-controls="instances-tab-pane" aria-selected="true">Istanze</button>
                </li>
            </ul>
        `;
        const content = html`
            <div class="tab-content" id="ontoTabsContent">
                <div class="tab-pane fade p-3 show active" id="classes" role="tabpanel" aria-labelledby="classes-tab" tabindex="0"></div>
                <div class="tab-pane fade" id="instances" role="tabpanel" aria-labelledby="instances-tab" tabindex="0"></div>
            </div>
        `;

        return {
            tabs: domParser.parseFromString(tabs, 'text/html').querySelector('ul'),
            content: domParser.parseFromString(content, 'text/html').querySelector('div'), 
        };
    }
}
