import AppState from "./state.js";
import { changeLightDirection, toggleAmbientOcclusion } from "./utils/environment.js";
import { resetClipping, addClippingPlane } from "./utils/clipping.js";
import { traverseOntology } from "./ontology.js";

/**
 * @module UI
 */

const domParser = new DOMParser;

const contentMenuTabs = `
    <!-- Nav tabs -->
    <ul class="nav nav-pills" id="content-tabs" role="tablist">
        <li class="nav-item" role="presentation">
            <button class="nav-link active" id="layer-tab" data-bs-toggle="tab" data-bs-target="#layer" type="button" role="tab" aria-controls="layer" aria-selected="false">
                <i class="bi bi-boxes me-1"></i> Elementi 3D
            </button>
        </li>
        <li class="nav-item" role="presentation">
            <button class="nav-link" id="media-tab" data-bs-toggle="tab" data-bs-target="#content" type="button" role="tab" aria-controls="media" aria-selected="true">
                <i class="bi bi-diagram-3 me-1"></i> Contenuti
            </button>
        </li>
    </ul>

    <!-- Tab panes -->
    <div class="tab-content ps-4 ms-3 overflow-y-auto">
        <div class="tab-pane active p-3 ms-2" id="layer" role="tabpanel" aria-labelledby="layer-tab" tabindex="0"></div>
        <div class="tab-pane pt-3" id="content" role="tabpanel" aria-labelledby="media-tab" tabindex="0"></div>
    </div>
`;

const audioExample = `
    <button type="button" id="audio-example" class="text-left btn aton-btn fs-6 mx-2" data-bs-toggle="modal" data-bs-target="#audio1">
        <i class="bi bi-play-btn me-2"></i> Esempio audio (<em>Che fiero costume</em>)
    </button>
`;

/**
 * 
 * @param {String} triggerSelector - Usually, the close modal trigger element(s) selector
 */
export function pauseAudio(triggerSelector) {
    // What if more than one audio element is playing?
    const audio = document.querySelector('audio');

    if (audio) {
        document.querySelectorAll(triggerSelector).forEach(el => {
            el.addEventListener('click', () => audio.pause());
        });
        document.querySelector('.modal').addEventListener('blur', () => {
            audio.pause();
        });
    }
}
/**
 * Resets the UI state (essentially hides the clipper toolbar if visible...)
 * @todo Other elements to reset?? Restore inital lighting conditions and viewpoint...
 */
function reset() {
    document.querySelector('#clipper-bar')?.classList.add('d-none');
    document.querySelector('#clipper')?.classList.remove('border', 'border-2', 'border-white');
}

/**
 * 
 * @param {HTMLElement} target 
 * @param {NodeListOf<HTMLButtonElement>} btns 
 * @param {String} axis - One of 'x', 'y', 'z'
 */
function showClipping(target, btns, axis) {
    if (axis) {
        addClippingPlane(axis, -1);
        target.classList.add('border', 'border-2', 'border-warning');
        btns.forEach(btn => {
            if (btn.id !== target.id) {
                btn.classList.remove('border', 'border-2', 'border-warning');
            }
        });
    }
}
/**
 * @todo Get clipping button from state? Review logic!! 
 * @param {String} triggerSelector 
 * @param {String} targetSelector The selector for the target toolbar to be displayed
 */
function toggleClipperBar(triggerSelector, targetSelector) {
    const trigger = document.querySelector(triggerSelector);
    const toolbar = document.querySelector(targetSelector);
    const btns = toolbar.querySelectorAll('button');
    const clipTargets = {
        clipX: {axis: 'x'},
        clipY: {axis: 'y'},
        clipZ: {axis: 'z'},
    };

    if (!AppState.clipping.listeners.button) {
        trigger.addEventListener(
            'click',
            () => {
                toolbar.classList.toggle('d-none');
                const aoCurrentState = AppState.ambientOcclusion;

                if (!toolbar.classList.contains('d-none')) {
                    AppState.clipping.enabled = true;
                    toggleAmbientOcclusion(false);

                    btns.forEach(btn => {
                        btn.classList.remove('border', 'border-2', 'border-warning');
                    });

                    trigger.className += ' border border-2 border-white';
                    toolbar.addEventListener('click', event => {
                        showClipping(event.target, btns, clipTargets[event.target.id]?.axis);
                    });
                } else {
                    resetClipping();
                    let noBorder = trigger.className.replace(/ border.*$/g, '');
                    trigger.className = noBorder;
                    toggleAmbientOcclusion(aoCurrentState);
                }
            }
        );
        AppState.clipping.listeners.button = true;
    }
}
/**
 * A left side settings panel
 * @param {String} triggerId - The settings button id
 */
function toggleSettingsPanel(triggerId) {
    const btn = document.querySelector(`#${triggerId}`);
    const lightHeading = document.createElement('h2');
    lightHeading.className = 'fs-5 ms-2 mb-3 mt-3';
    lightHeading.innerHTML = '<i class="bi bi-lightbulb me-1"></i> Direzione luce';
    const envHeading = document.createElement('h2');
    envHeading.className = 'fs-5 ms-2 mb-3 mt-3';
    envHeading.innerHTML = '<i class="bi bi-brightness-high me-1"></i> Ambiente';

    btn.addEventListener('click', () => {
        ATON.UI.setSidePanelLeft();
        ATON.UI.showSidePanel({header: '<i class="bi bi-gear-fill me-1"></i> Impostazioni'});
        ATON.UI.elSidePanel.appendChild(lightHeading);

        const lightSliderX = createLightSlider('x', 'Asse X', [-2, 2], 0.1);
        const lightSliderY = createLightSlider('y', 'Asse Y', [-2, 2], 0.1);
        const lightSliderZ = createLightSlider('z', 'Asse Z', [-2, 2], 0.1);

        ATON.UI.elSidePanel.appendChild(lightSliderX);
        ATON.UI.elSidePanel.appendChild(lightSliderY);
        ATON.UI.elSidePanel.appendChild(lightSliderZ);

        ATON.UI.elSidePanel.appendChild(envHeading);

        const ambientOcclSwitch = document.createElement('div');
        ambientOcclSwitch.className = 'form-check form-switch ms-4 mt-2';
        ambientOcclSwitch.innerHTML = `
            <input class="form-check-input" type="checkbox" role="switch" id="aoSwitch" title="Abilita / disabilita ambient occlusion">
            <label class="form-check-label" for="aoSwitch"><em>Ambient occlusion</em> <i class="bi bi-info-circle ms-2 c-hand"></i></label>
        `;

        const shadowsSwitch = document.createElement('div');
        shadowsSwitch.className = 'form-check form-switch ms-4 mt-2';
        shadowsSwitch.innerHTML = `
            <input class="form-check-input" type="checkbox" role="switch" id="shadowsSwitch" title="Abilita / disabilita ombre">
            <label class="form-check-label" for="shadowsSwitch">Ombre <i class="bi bi-info-circle ms-2 c-hand" title=""></i></label>
        `;

        shadowsSwitch.querySelector('input[type="checkbox"]').checked = AppState.shadows;
        ambientOcclSwitch.querySelector('input[type="checkbox"]').checked = AppState.ambientOcclusion;

        ATON.UI.elSidePanel.appendChild(ambientOcclSwitch);
        ATON.UI.elSidePanel.appendChild(shadowsSwitch);

        // TODO: move somewhere else...
        document.querySelector('#aoSwitch').addEventListener(
            'change',
            event => {
                toggleAmbientOcclusion(event.target.checked);
                AppState.ambientOcclusion = event.target.checked;
            }
        );
        document.querySelector('#shadowsSwitch').addEventListener(
            'change',
            event => {
                const checked = event.target.checked;
                ATON.toggleShadows(checked);
                AppState.shadows = checked;
            }
        );
    });
}
/**
 * 
 * @param {String} direction - The axis direction, one of 'x','y','z'
 * @param {String} label - The slider label
 * @param {Number[]} range - The slider's range
 * @param {Number} step - The slider's step
 */
function createLightSlider(direction, label, range, step) {
    const currentVal = ATON.getMainLightDirection()[direction];
    const lightSlider = ATON.UI.createSlider({
        range,
        label,
        value: Number.parseFloat(currentVal).toPrecision(1),
        oninput: val => {
            const lightDir = ATON.getMainLightDirection();
            // Keep existing direction values for the other axes
            lightDir[direction] = Number.parseFloat(val);
            changeLightDirection(lightDir);
        },
    });

    lightSlider.classList.add('ms-4');
    lightSlider.querySelector('input').step = step;

    return lightSlider;
}
/**
 * Right-side main menu panel
 * @param {String} triggerId - The menu button id
 * @param {String} ontologyJsonPath
 */
function toggleContentMenu(triggerId, ontologyJsonPath) {
    const btn = document.querySelector(`#${triggerId}`);

    btn.addEventListener('click', async () => {
        ATON.UI.setSidePanelRight();
        ATON.UI.showSidePanel({header: 'Menu'});
        // Append tabs, then tab panes
        const tabs = domParser.parseFromString(contentMenuTabs, 'text/html');
        ATON.UI.elSidePanel.appendChild(tabs.querySelector('#content-tabs'));
        ATON.UI.elSidePanel.appendChild(tabs.querySelector('.tab-content'));
        buildLayersMenu(AppState.normalizedNodes, ATON.UI.elSidePanel.querySelector('#layer'));
        buildOntologyMenu(await traverseOntology(ontologyJsonPath), ATON.UI.elSidePanel.querySelector('#content'));
    });
}
/**
 * @todo Don't rebuild it every time the side panel is shown...
 * @param {Array} nodes The normalized scene nodes (IDs and status)
 * @param {HTMLElement} sidePanel ATON's side panel element
 */
function buildLayersMenu(nodes, sidePanel) {
    for(let node of nodes) {
        const menuItem = document.createElement('div');
        menuItem.className = `form-check form-switch ms-${node.depth} ps-${node.depth} mt-2`;
        const checkbox = document.createElement('input');
        checkbox.type = 'checkbox';
        checkbox.className = "form-check-input";
        checkbox.checked = node.active;
        checkbox.role = 'switch';
        checkbox.title = "Mostra / nascondi layer";

        menuItem.appendChild(checkbox);

        const label = document.createElement('label');
        label.className = "form-check-label";
        label.textContent = node.id;
        
        menuItem.appendChild(label);

        sidePanel.appendChild(menuItem);
        // Will this ever work??
        menuItem.addEventListener('change', event => toggleNode(node.id, event.target.checked));
    }

    /**
     * This is terrible...
     * @param {String} id 
     * @param {Boolean} status 
     */
    const toggleNode = (id, status) => {
        ATON.getSceneNode(id).toggle(status);
        AppState.normalizedNodes.find(n => n.id === id).active = status;
    }
}

/**
 * @see traverseOntology
 * @param {Object} ontology The traversed ontology object (temp)
 * @param {HTMLElement} sidePanel ATON's side panel element
 */
function buildOntologyMenu(ontology, sidePanel) {
    const list = document.createElement('ul');
    list.className = 'list-group';
    const mainNode = document.createElement('li');
    mainNode.className = 'list-group-item';
    mainNode.textContent = ontology.ontology;
    
    const domainList = document.createElement('ul');
    domainList.className = 'list-group';

    for(let domain of ontology.domains) {
        const domainItem = document.createElement('li');
        domainItem.textContent = domain.label;
        domainItem.className = 'list-group-item';
        domainList.appendChild(domainItem);
    }

    mainNode.appendChild(domainList);
    list.appendChild(mainNode);
    sidePanel.appendChild(list);
}
/**
 * Initialize required components for scene UI
 * @param {String} ontologyJsonPath
 */
export async function initUI(ontologyJsonPath) {
    toggleSettingsPanel('settings');
    toggleContentMenu('menu', ontologyJsonPath);
    toggleClipperBar('#clipper', '#clipper-bar');
}