//injectConfig library by @theripper93
//License: MIT
//Documentation: https://github.com/theripper93/injectConfig

let moduleId = "";
let object = null;
let hookSet = false;

export const injectConfig = {

    _processData: function _processData(data) {
        if (data.tab?.subTabs) {
            for (const [k, v] of Object.entries(data)) {
                if (k === "moduleId" || k === "inject" || k === "tab") continue;
                for (const [k2, v2] of Object.entries(v)) {
                    if(k2 === "tabIcon" || k2 === "tabLabel" || k2 === "tabNotes") continue;
                    this._processInputData(k2, v2);
                }
            }
        } else {
            for (const [k, v] of Object.entries(data)) {
                if (k === "moduleId" || k === "inject" || k === "tab") continue;
                this._processInputData(k, v);
            }
        }
    },

    _processInputData: function _processInputData(k, v) {
        //if (k === "moduleId" || k === "inject" || k === "tab") continue;
        const elemData = v;
        v.name = "flags." + moduleId + "." + (k || "");
        v.value = object?.getFlag(moduleId, k) ?? elemData.default ?? getDefaultFlag(k);
        v.label = v.units ? v.label + `<span class="units"> (${v.units})</span>` : v.label;
        if (v.type.includes("filepicker")) {
            const split = v.type.split(".");
            v.fpType = split[1] ?? "imagevideo";
            if (v.fpTypes) {
                v.fpTypes = v.fpTypes.join(",");
                setFilePickerHook();
            }
            v.type = "filepicker";
        }

        function getDefaultFlag(inputType){
            switch(inputType){
                case "number":
                    return 0;
                case "checkbox":
                    return false;
            }
            return "";
        }
    },

    _getInjectionPoint: function _getInjectionPoint(app, html, data, object) {
        let injectPoint
        if(typeof data.inject === "string"){
            injectPoint = html.querySelector(data.inject).closest(".form-group");
        }else{
            injectPoint = data.inject;
        }
        injectPoint = injectPoint ?? (data.tab ? [...html.querySelectorAll("form > .tab")].at(-1) : [...html.querySelectorAll(".form-group").at(-1)]);
        return injectPoint;
    },

    _createTab: function _createTab(html, name,label,icon){
        const tabs = [...html.querySelector(".sheet-tabs").querySelectorAll(".item")].at(-1);
        const tab = document.createElement("a");
        tab.classList.add("item");
        tab.dataset.tab = name;
        tab.innerHTML = `<i class="${icon}"></i> ${label}`;
        tabs.after(tab);
        const tabContainer = document.createElement("div");
        tabContainer.classList.add("tab");
        tabContainer.dataset.tab = name;
        return tabContainer;
    },

    _generateInnerHtml: function _generateInnerHtml(app, data, tabSize) {
        if (data.tab?.subTabs) {
            const container = document.createElement("div");
            const subTabNav = document.createElement("nav");
            subTabNav.classList.add("tabs", "sheet-tabs", "secondary-tabs");
            subTabNav.dataset.group = data.tab.name;
            container.append(subTabNav);
            for (const [k, v] of Object.entries(data)) {
                if (k === "tab" || k === "moduleId" || k === "inject") continue;
                const a = document.createElement("a");
                a.classList.add("item");
                a.dataset.tab = k;
                a.dataset.group = data.tab.name;
                a.innerHTML = `<i class="${v.tabIcon}"></i> ${v.tabLabel}`;
                a.addEventListener("click", (e) => {
                    const tab = e.currentTarget.dataset.tab;
                    container.querySelectorAll(".item").forEach((i) => i.classList.remove("active"));
                    e.currentTarget.classList.add("active");
                    container.querySelectorAll(".tab").forEach((i) => i.classList.remove("active"));
                    container.querySelector(`.tab[data-tab="${tab}"]`).classList.add("active");
                    app.setPosition({"height" : "auto", "width" : data.tab ? app.options.width + tabSize : "auto"});
                });
                subTabNav.append(a);
                const tabContents = document.createElement("div");
                tabContents.classList.add("tab");
                tabContents.dataset.tab = k;
                tabContents.dataset.group = data.tab.name;
                container.append(tabContents);
                const renderedContent = getRenderedTemplate({inputs: v});
                if (v.tabNotes) {
                    const notes = document.createElement("p");
                    notes.classList.add("notes");
                    notes.innerHTML = v.tabNotes;
                    tabContents.append(notes);
                }
                Array.from(renderedContent.children).forEach((e) => tabContents.append(e));
            }
            //set first tab to active
            subTabNav.querySelector(".item").classList.add("active");
            container.querySelector(".tab").classList.add("active");
            return container;
        } else {
            return getRenderedTemplate({inputs: data});
        }
    },

    inject: function injectConfig(app, html, data, _object) {
        moduleId = data.moduleId;
        object = _object || app.object;
        html = html[0] || html;
        html = html.closest(".app") ?? html;
        
        this._processData(data);

        const injectPoint = this._getInjectionPoint(app, html, data, object);

        const isTabs = !!html.querySelector(".sheet-tabs");
        this._generateTabStruct(app, html, data, object);
        
        const tabSize = data.tab?.width ?? 100;
        
        let injectHtml = this._generateInnerHtml(app, data, tabSize);
        
        try {
            app.activateListeners($(injectHtml));
        } catch (error) {}

        if (data.tab) {
            const injectTab = this._createTab(html, data.tab.name, data.tab.label, data.tab.icon)
            injectTab.append(injectHtml);
            (injectPoint ?? this._getInjectionPoint(app, html, data, object)).after(injectTab);
            if(app._activeTab) html.querySelector(`.item[data-tab="${app._activeTab}"]`)?.click();
        } else {
            injectPoint.after(injectHtml);
        }

        html.querySelectorAll(".tabs .item").forEach((item) => {
            item.addEventListener("click", (e) => app._activeTab = e.currentTarget.dataset.tab);
        });

        if (!isTabs) {
            html.querySelectorAll(".item").forEach((item) => {
                item.addEventListener("click", (e) => {
                    html.querySelectorAll(".item").forEach((i) => i.classList.remove("active"));
                    e.currentTarget.classList.add("active");
                    html.querySelectorAll(".tab").forEach((i) => i.classList.remove("active"));
                    html.querySelector(`.tab[data-tab="${e.currentTarget.dataset.tab}"]`).classList.add("active");
                    app.setPosition({"height" : "auto", "width" : data.tab ? app.options.width + tabSize : "auto"});
                });
            });
        }

        
        app?.setPosition({"height" : "auto", "width" : data.tab ? app.options.width + tabSize : "auto"});
        return $(injectHtml);

    },
    quickInject: function quickInject(injectData, data){
        injectData = Array.isArray(injectData) ? injectData : [injectData];
        for(const doc of injectData){
            let newData = data
            if(doc.inject){
                newData = JSON.parse(JSON.stringify(data))
                data.inject = doc.inject;
            }
            Hooks.on(`render${doc.documentName}Config`, (app,html)=>{ injectConfig.inject(app,html,newData) });
        }

    },
    _generateTabStruct : function _generateTabStruct(app,html,data,object){
        const isTabs = !!html.querySelector(".sheet-tabs");
        const useTabs = data.tab
        if(isTabs || !useTabs) return;
        const tabSize = data.tab?.width || 100;
        const layer = app?.object?.layer?.options?.name
        const icon = $(".main-controls").find(`li[data-canvas-layer="${layer}"]`).find("i").attr("class")

        const tabsInner = `<nav class="sheet-tabs tabs">
        <a class="item active" data-tab="basic"><i class="${icon}"></i> ${game.i18n.localize("LIGHT.HeaderBasic")}</a>
        </nav>
        <div class="tab active" data-tab="basic"></div>`;
        const container = document.createElement("div");
        container.innerHTML = tabsInner;
        const inputsContainer = container.querySelector(".tab");
        //move all content of form into tab
        const form = html.querySelector("form");
        Array.from(form.children).forEach((e)=>{
            inputsContainer.append(e);
        });
        
        Array.from(container.children).forEach((e)=>{
            form.append(e);
        });

        const submitButton = html.querySelector("button[type='submit']");
        form.append(submitButton);
    }
}

const _template = `

<div>
    {{#each inputs as |input|}}
    {{#if (eq input.type 'custom')}}
    {{{input.html}}}
    {{else}}
    <div class="form-group">
        
            <label for="{{input.name}}">{{{input.label}}}</label>
        
        {{#if (eq input.type 'text')}}
            <input type="text" name="{{input.name}}" placeholder="{{input.placeholder}}" value="{{input.value}}">
        {{/if}}

        {{#if (eq input.type 'textarea')}}
            <textarea type="text" name="{{input.name}}" placeholder="{{input.placeholder}}"">{{input.value}}</textarea>
        {{/if}}

        {{#if (eq input.type 'number')}}
            <input type="number" min="{{input.min}}" max="{{input.max}}" step="{{input.step}}" name="{{input.name}}" placeholder="{{input.placeholder}}" value="{{input.value}}">
        {{/if}}

        {{#if (eq input.type 'checkbox')}}
            <input type="checkbox" name="{{input.name}}" {{#if input.value}}checked{{/if}}>
        {{/if}}

        {{#if (eq input.type 'select')}}
            <select name="{{input.name}}">
            {{#each input.options as |option|}}
                {{#if option.optgroup}}
                    {{#if option.optgroup.start}}
                        <optgroup label="{{option.optgroup.label}}">
                    {{else}}
                        </optgroup>
                    {{/if}}
                {{else}}
                <option value="{{@key}}" {{#if (eq @key input.value)}}selected{{/if}}>{{option}}</option>
                {{/if}}
            {{/each}}
            </select>
        {{/if}}

        {{#if (eq input.type 'range')}}
            <div class="form-fields">
                <input type="range" min="{{input.min}}" max="{{input.max}}" step="{{input.step}}" name="{{input.name}}" value="{{input.value}}">
                <span class="range-value">{{input.value}}</span>
            </div>
        {{/if}}

        {{#if (eq input.type 'color')}}
            <div class="form-fields">
                {{colorPicker name=input.name value=input.value default="#000000"}}
            </div>
        {{/if}}

        {{#if (eq input.type 'filepicker')}}
                <div class="form-fields">     
                    <button type="button" class="file-picker" data-extras="{{input.fpTypes}}" data-type="{{input.fpType}}" data-target="{{input.name}}" title="Browse Files" tabindex="-1">
                        <i class="fas fa-file-import fa-fw"></i>
                    </button>
                    <input class="image" type="text" name="{{input.name}}" placeholder="{{input.placeholder}}" value="{{input.value}}">
                </div>
        {{/if}}

        {{#if input.notes}}
        <p class="notes">{{input.notes}}</p>
        {{/if}}
    </div>
    {{/if}}
    {{/each}}
</div>

`

let _compiledTemplate = null;

function getRenderedTemplate(data) {
    data.inputs = {...data.inputs}
    delete data.inputs.tab;
    delete data.inputs.moduleId;
    delete data.inputs.inject;
    const compiledTemplate = _compiledTemplate ?? Handlebars.compile(_template);
    _compiledTemplate = compiledTemplate;
    const innerHtml = compiledTemplate(data, {
        allowProtoMethodsByDefault: true,
        allowProtoPropertiesByDefault: true,
    });
    const el = document.createElement("div");
    el.innerHTML = innerHtml;
    return el.children[0];
}

function setFilePickerHook() {
    if (hookSet) return;
    hookSet = true;
    Hooks.on("renderFilePicker", (app, html, data) => {
        const button = app.button;
        if (!button || app._customExtensionsAdded) return;
        const ext = button[0].dataset.extras;
        if (!ext) return;
        const extraExt = ext.split(",");
        app.extensions ? app.extensions.push(...extraExt) : app.extensions = extraExt;
        app._customExtensionsAdded = true;
        app.render(true);
    });
}