import Structure from "../Structure";
import StructureFactory, {StructureImplConfig} from "../StructureFactory";
import Input from "../../Input/Input";

export default class Tabbed extends Structure{
    public tabs: Array<{
        label: string,
        contents: string[]
    }> = [];

    constructor(params: Partial<Tabbed> = {}) {
        super(params);
        Object.assign(this, params);
    }

    public placeHtml(container: HTMLElement): void {
        const tabDiv = document.createElement("div");
        this.addStandardContainerProperties(tabDiv);
        tabDiv.classList.add("pad-tabbed-group");

        const labelContainer = document.createElement("ul");
        tabDiv.append(labelContainer);

        this.tabs.forEach((tabData, h) => {
            const label = document.createElement("li");
            const tab = document.createElement("div");
            const link = document.createElement("a");
            const id = "pad-" + Math.random().toString(36).substring(2, 15) + Math.random().toString(36).substring(2, 15);
            label.classList.add("pad-tab-label");
            label.append(link);

            link.href = "#" + id;
            link.innerText = tabData.label;

            tab.id = id;
            tab.classList.add("pad-tab-content");

            labelContainer.appendChild(label);
            tabDiv.appendChild(tab);

            tabData.contents.forEach((content) => {
                const item = this.formBuilder.registry[content];
                if (!item) return;
                item.placeHtml(tab);
                if (item instanceof Structure || item instanceof Input)
                    item.getDataNode().onValidChange.add(() => {
                        label.classList.toggle("pad-invalid", !this.checkTabValid(h));
                    });
                if (item instanceof Structure || item instanceof Input)
                    item.getDataNode().onServerSideErrorsChange.add(() => {
                        label.classList.toggle("pad-server-side-errors", !this.checkTabServerSide(h));
                    });
            });

            label.classList.toggle("pad-invalid", !this.checkTabValid(h));
        });

        if (hasJQuery(window)) window.jQuery(tabDiv).tabs();

        container.append(tabDiv);
        this.placedHtml.push(tabDiv);
    }

    public checkTabValid(tabIndex: number): boolean{
        return this.tabs[tabIndex].contents.every(c => {
            const item = this.formBuilder.registry[c];
            if (item instanceof Structure || item instanceof Input)
                return item.getDataNode().isValid;
            return true;
        });
    }

    public checkTabServerSide(tabIndex: number): boolean{
        return this.tabs[tabIndex].contents.every(c => {
            const item = this.formBuilder.registry[c];
            if (item instanceof Structure || item instanceof Input)
                return item.getDataNode().serverSideErrors.length === 0;
            return true;
        });
    }
}

function hasJQuery(object:any): object is {jQuery(element: HTMLElement):{tabs():void}}{
    return typeof object.jQuery === "function";
}

export function addTabbedToFactory(factory: StructureFactory): void {
    factory.registerConstructor('tabbedGroups', (config, params) => {
        const impl = config.impl;
        if (!isTabbedConfig(impl)) return null;
        const tabbedParams: Partial<Tabbed> = params as Partial<Tabbed>;
        tabbedParams.tabs = impl.tabs;
        return new Tabbed(tabbedParams);
    })
}

function isTabbedConfig(config: StructureImplConfig): config is TabbedConfig{
    return config.type === 'tabbedGroups';
}

interface TabbedConfig extends StructureImplConfig{
    type: 'tabbedGroups',
    tabs: Array<{
        label: string,
        contents: string[]
    }>
}