/* ===================== AUTOGENERATED SOURCE IMPORT ===================== */

import Template from "/emcJS/util/html/template/HTMLTemplate.js";

const TPL = new Template(`
    <input is="emc-i18n-input" id="input" autocomplete="off" readonly>
    <div id="scroll-container">
        <div id="options-container"></div>
        <emc-i18n-label id="empty" i18n-value="no entries"></emc-i18n-label>
    </div>
    <slot id="options-slot"></slot>
    
`);

import GlobalStyle from "/emcJS/util/html/template/CSSTemplate.js";

const STYLE = new GlobalStyle(`
    #field {
        height: 2em;
        overflow: visible;
        color: var(--input-text-color, #000000);
        background-color: transparent;
        background-image: none;
    }
    
    /* --- */
    #input {
        position: absolute;
        opacity: 0;
        pointer-events: none;
    }
    
    /* --- */
    #options-container button {
        position: relative;
        display: flex;
        align-items: center;
        justify-content: center;
        flex-shrink: 0;
        flex-grow: 1;
        height: 100%;
        padding: 0px;
        color: var(--input-button-text-color, #000000);
        background-color: var(--input-button-back-color, #ffffff);
        background-image: var(--input-button-back-image, none);
        border-style: solid;
        border-width: var(--input-border-width, 2px);
        border-color: var(--input-border-color, #222222);
        font-size: 1rem;
        font-weight: bold;
        cursor: pointer;
        appearance: none;
    }
    
    #options-container button.selected {
        color: var(--input-button-active-text-color, #ffffff);
        background-color: var(--input-button-active-back-color, #222222);
        background-image: var(--input-button-active-back-image, none);
        border-color: var(--input-button-active-border-color, #222222);
    }
    
    #options-container button:first-child {
        border-top-left-radius: var(--input-border-radius, 6px);
        border-bottom-left-radius: var(--input-border-radius, 6px);
    }
    
    #options-container button:last-child {
        border-top-right-radius: var(--input-border-radius, 6px);
        border-bottom-right-radius: var(--input-border-radius, 6px);
    }
    
    #options-container button:nth-child(n + 2) {
        margin: 0 0 0 calc(-1 * var(--input-border-width, 2px));
    }
    
    #options-container button:hover {
        color: var(--input-button-hover-text-color, #000000);
        background-color: var(--input-button-hover-back-color, #b3e5ec);
        background-image: var(--input-button-hover-back-image, none);
    }
    
    #options-container button.selected:hover {
        color: var(--input-button-active-hover-text-color, #ffffff);
        background-color: var(--input-button-active-hover-back-color, #222222);
        background-image: var(--input-button-active-hover-back-image, none);
        border-color: var(--input-button-active-hover-border-color, #222222);
        cursor: default;
    }
    
    #options-container button:focus {
        box-shadow: 0 0 2px 3px var(--input-focus-color, #06b5ff);
        outline: none;
        z-index: 1;
    }
    
    :host(:invalid) #options-container button {
        color: var(--input-invalid-text-color, #000000);
        background-color: var(--input-invalid-back-color, #ffebeb);
        background-image: var(--input-invalid-back-image, none);
        border-color: var(--input-invalid-border-color, #ff4444);
    }
    
    :host(:invalid) #options-container button:focus {
        box-shadow: 0 0 2px 3px var(--input-invalid-focus-color, #ff2500);
    }
    
    #options-container button:disabled {
        color: var(--input-button-disabled-text-color, #757575) !important;
        background-color: var(--input-button-disabled-back-color, #999999) !important;
        background-image: var(--input-button-disabled-back-image, none) !important;
        border-color: var(--input-button-disabled-border-color, #6a6a6a) !important;
        cursor: default !important;
        opacity: 1 !important;
    }
    
    #options-container button.selected:disabled {
        color: var(--input-button-active-disabled-text-color, #757575) !important;
        background-color: var(--input-button-active-disabled-back-color, #3f3f3f) !important;
        background-image: var(--input-button-active-disabled-back-image, none) !important;
        border-color: var(--input-button-active-disabled-border-color, #6a6a6a) !important;
        cursor: default !important;
        opacity: 1 !important;
    }
    
    #options-container button[readonly]:not([readonly="false"]) {
        pointer-events: none;
    }
    
    /* --- */
    #scroll-container,
    #options-container {
        position: relative;
        display: flex;
        align-items: center;
        justify-content: stretch;
        width: 100%;
        height: 100%;
    }
    
    #options-container:empty {
        display: none;
    }
    
    /* --- */
    #empty {
        display: none;
        align-items: center;
        justify-content: center;
        font-style: italic;
        height: 100%;
        width: 100%;
        padding: 5px;
        color: var(--input-text-color, #000000);
        background-color: var(--input-back-color, #ffffff);
        border-style: solid;
        border-width: var(--input-border-width, 2px);
        border-color: var(--input-border-color, #222222);
        border-radius: var(--input-border-radius, 6px);
        white-space: normal;
    }
    
    #options-container:empty + #empty {
        display: flex;
    }
    
    :host(:invalid) #empty {
        color: var(--input-invalid-text-color, #000000);
        background-color: var(--input-invalid-back-color, #ffebeb);
        background-image: var(--input-invalid-back-image, none);
        border-color: var(--input-invalid-border-color, #ff4444);
    }
    
    .hidden,
    #options-slot {
        display: none !important;
    }
    
`);

const CONFIG_FIELDS = [
    {
        "type": "StringInput",
        "name": "placeholder",
        "label": "Placeholder",
        "value": ""
    },
    {
        "type": "SwitchInput",
        "name": "sorted",
        "label": "Sorted",
        "value": false
    },
    {
        "type": "KeyValueListInput",
        "name": "options",
        "label": "Options",
        "value": {}
    },
    {
        "type": "StringInput",
        "name": "optiongroup",
        "label": "Option Group",
        "value": ""
    }
]
;

/* ======================================================================= */

import AbstractFormElement from "../../AbstractFormElement.js";
import FormElementRegistry from "../../../../../data/registry/form/FormElementRegistry.js";
import BusyIndicatorManager from "../../../../../util/BusyIndicatorManager.js";
import EventTargetManager from "../../../../../util/event/EventTargetManager.js";
import EventMultiTargetManager from "../../../../../util/event/EventMultiTargetManager.js";
import i18n from "../../../../../util/I18n.js";
import {deepClone} from "../../../../../util/helper/DeepClone.js";
import {
    nodeTextComparator,
    sortChildren
} from "../../../../../util/helper/ui/NodeListSort.js";
import {debounce} from "../../../../../util/Debouncer.js";
import {registerFocusable} from "../../../../../util/helper/html/ElementFocusHelper.js";
import {safeSetAttribute} from "../../../../../util/helper/ui/NodeAttributes.js";
import MutationObserverManager from "../../../../../util/observer/manager/MutationObserverManager.js";
import I18nOption from "../../../../i18n/builtin/I18nOption.js";
import SwitchButtonManager from "./manager/SwitchButtonManager.js";
import "../../../../i18n/builtin/I18nInput.js";
import "../../../../i18n/I18nLabel.js";

const MUTATION_CONFIG = {
    attributes: true,
    characterData: true,
    attributeFilter: ["value", "label"]
};

export default class SwitchSelect extends AbstractFormElement {

    static get formConfigurationFields() {
        return [...super.formConfigurationFields, ...deepClone(CONFIG_FIELDS)];
    }

    static get changeDebounceTime() {
        return 0;
    }

    #fieldEl;

    #inputEl;

    #optionsContainerEl;

    #optionsSlotEl;

    #optionSelectEventManager = new EventMultiTargetManager();

    #i18nEventManager = new EventTargetManager(i18n, false);

    #mutationObserver = new MutationObserverManager(MUTATION_CONFIG, () => {
        this.#onSlotChange();
    });

    #switchButtonManager;

    constructor() {
        super();
        this.#fieldEl = this.shadowRoot.getElementById("field");
        this.#fieldEl.append(TPL.generate());
        STYLE.apply(this.shadowRoot);
        /* --- */
        this.#optionSelectEventManager.set("click", (event) => {
            const targetEl = event.currentTarget;
            this.value = targetEl.value;
            event.preventDefault();
            event.stopPropagation();
        });
        /* --- */
        this.#inputEl = this.shadowRoot.getElementById("input");
        this.#optionsContainerEl = this.shadowRoot.getElementById("options-container");
        this.#optionsSlotEl = this.shadowRoot.getElementById("options-slot");
        this.#optionsSlotEl.addEventListener("slotchange", () => {
            this.#onSlotChange();
        });
        /* --- */
        this.#switchButtonManager = new SwitchButtonManager(this.#optionsContainerEl, this.#optionSelectEventManager);
        this.#switchButtonManager.addEventListener("afterrender", () => {
            this.renderValue(this.value);
        });
        /* --- */
        this.#i18nEventManager.set("language", () => {
            this.#sort();
        });
        this.#i18nEventManager.set("translation", () => {
            this.#sort();
        });
    }

    formDisabledCallback(disabled) {
        super.formDisabledCallback(disabled);
        this.#inputEl.disabled = disabled;
        const children = this.#optionsContainerEl.children;
        for (const child of children) {
            child.disabled = disabled;
        }
    }

    focus(options) {
        const children = this.#optionsContainerEl.children;
        const child = children[0];
        if (child != null) {
            child.focus(options);
        }
    }

    set sorted(value) {
        this.setBooleanAttribute("sorted", value);
    }

    get sorted() {
        return this.getBooleanAttribute("sorted");
    }

    static get observedAttributes() {
        return [...super.observedAttributes, "readonly", "sorted"];
    }

    attributeChangedCallback(name, oldValue, newValue) {
        super.attributeChangedCallback(name, oldValue, newValue);
        switch (name) {
            case "readonly": {
                if (oldValue != newValue) {
                    const optionEls = this.#optionsContainerEl.children;
                    for (const optionEl of optionEls) {
                        safeSetAttribute(optionEl, "readonly", newValue);
                    }
                }
            } break;
            case "sorted": {
                if (oldValue != newValue) {
                    const sorted = this.sorted;
                    this.#i18nEventManager.active = sorted;
                    if (sorted) {
                        this.#switchButtonManager.registerSortFunction(this.#sortByNameFunction);
                    } else {
                        this.#switchButtonManager.registerSortFunction();
                    }
                }
            } break;
        }
    }

    renderValue(value) {
        const oldSelectedEl = this.#optionsContainerEl.querySelector(".selected");
        if (oldSelectedEl != null) {
            oldSelectedEl.classList.remove("selected");
        }
        if (value != null) {
            const selectedEl = this.#optionsContainerEl.querySelector(`[value="${value}"]`);
            if (selectedEl != null) {
                selectedEl.classList.add("selected");
                if (selectedEl.label != null) {
                    this.#inputEl.i18nValue = selectedEl.label;
                } else {
                    this.#inputEl.i18nValue = "";
                    this.#inputEl.innerText = selectedEl.innerText;
                }
            } else {
                this.#inputEl.i18nValue = value;
            }
        } else {
            this.#inputEl.i18nValue = "";
        }
    }

    #sort = debounce(() => {
        sortChildren(this.#optionsContainerEl);
    });

    async #resolveSlottedElements() {
        await BusyIndicatorManager.busy();
        const data = [];
        const optionNodeList = this.#optionsSlotEl.assignedElements({flatten: true}).filter((el) => el.matches("option"));
        /* --- */
        const oldNodes = new Set(this.#mutationObserver.getObservedNodes());
        const newNodes = new Set();
        for (const el of optionNodeList) {
            data.push({
                key: el.value || el.innerText,
                label: el.i18nValue || el.label || el.innerText,
                readonly: this.readonly
            });
            /* --- */
            if (oldNodes.has(el)) {
                oldNodes.delete(el);
            } else {
                newNodes.add(el);
            }
        }
        for (const node of oldNodes) {
            this.#mutationObserver.unobserve(node);
        }
        for (const node of newNodes) {
            this.#mutationObserver.observe(node);
        }
        /* --- */
        this.#switchButtonManager.manage(data);
        /* --- */
        await BusyIndicatorManager.unbusy();
    }

    #onSlotChange = debounce(() => {
        this.#resolveSlottedElements();
    });

    #sortByNameFunction(entry0, entry1) {
        const {element: el0} = entry0;
        const {element: el1} = entry1;
        return nodeTextComparator(el0, el1);
    }

    static fromConfig(config) {
        const selectEl = new SwitchSelect();
        const {
            options = {}, ...params
        } = config;

        for (const value in options) {
            const optionEl = I18nOption.create();
            optionEl.value = value;
            const label = options[value];
            if (typeof label === "string" && label !== "") {
                optionEl.i18nValue = label;
            } else if (value !== "") {
                optionEl.i18nValue = value;
            }
            selectEl.append(optionEl);
        }

        for (const name in params) {
            const value = params[name];
            safeSetAttribute(selectEl, name, value);
        }

        return selectEl;
    }

}

FormElementRegistry.register("SwitchSelect", SwitchSelect);
customElements.define("emc-select-switch", SwitchSelect);
registerFocusable("emc-select-switch");
