import { Tabbing } from ".";

import { MainStateManager } from "../../../MainStateManager";
import { IMainStateFactory } from "../../../Types";
import { ElementFactory } from "../ElementFactory";
import { IFocusAbleGridEditFactory } from "./GridEditTabbing";
import { validationTabbing } from "./ValidationTabbing";


export interface IFocusAbleElementFactory {
    mainStateManager: MainStateManager;
    tabIndex: number;
    element: HTMLElement;

    disabled: boolean;
    hidden: boolean;
    mainStateFactory: IMainStateFactory,
    factoryFieldName: string,
}

class MergeElementAndGridEdit {
    constructor(
        public element: HTMLElement,
        public tabIndex: number,
        public factory: IFocusAbleElementFactory | IFocusAbleGridEditFactory,
    ) {

    }
}

export class ElementTabbing {
    public isEmpty = (): boolean => {
        if (!this.getFocusAbleElements) {
            return true;
        } else if (this.getFocusAbleElements.length <= 0) {
            return true;
        }
        return false;
    }

    public focusAbleElements: IFocusAbleElementFactory[];

    constructor(
        public tabbing: Tabbing,
    ) {
        this.focusAbleElements = [];
    }


    public getFocusAbleElements = (): MergeElementAndGridEdit[] => {
        const elements = this.focusAbleElements.filter(i =>
            !i.disabled
            && !i.hidden
            && i.element
            && i.element!.tabIndex >= 0
        ).map(i => new MergeElementAndGridEdit(i.element, i.tabIndex, i));

        const grids = this.tabbing.gridEditTabbing.focusAbleGridEdit.filter(i =>
            typeof i.tabbing.tabIndex === 'number'
            && i.tabbing.refElement
            && i.tabbing.refElement.current
            && i.tabbing.refElement.current.tabIndex >= 0
        ).map(i => new MergeElementAndGridEdit(i.tabbing.refElement!.current!, i.tabbing.tabIndex!, i));

        return [...elements, ...grids].sort((a, b) => a.tabIndex - b.tabIndex);
    }

    public focus = () => {
        let result = false;
        result = this.focusToActiveElementTabIndex();

        if (result) {
            return;
        }

        result = this.focusToZeroTabIndexElement();

        if (result) {
            return;
        }

        result = this.focusToFirstTabIndexElement();

        if (result) {
            return;
        }

        this.tabbing.toolboxTabbing.focus(false);
    }


    public isThereAnyElement(element: EventTarget) {
        const find = this.getFocusAbleElements().find(item => item.element === element);
        return find;
    }

    public focusToActiveElementTabIndex = (): boolean => {
        const find = this.getFocusAbleElements().find(i => i.tabIndex === this.tabbing.activeElementIndex);
        if (find && find.element) {
            this.setFocus(find);
            return true;
        }
        return false;
    }


    public focusToZeroTabIndexOrFirstElementOrToolbox = () => {
        let result = false;

        result = this.focusToZeroTabIndexElement();

        if (result) {
            return;
        }

        result = this.focusToFirstTabIndexElement();

        if (result) {
            return;
        }

        this.tabbing.toolboxTabbing.focus(false);
    }

    public focusToZeroTabIndexElement = (): boolean => {
        const find = this.getFocusAbleElements().find(i => i.tabIndex === 0);
        if (find && find.element) {
            this.setFocus(find);
            return true;
        }
        return false;
    }
    public focusToFirstTabIndexElement = (): boolean => {
        const find = this.getFocusAbleElements()[0];
        if (find && find.element) {
            this.setFocus(find);
            return true;
        }
        return false;
    }

    public focusToMaxTabIndexOrLastElement = () => {
        let result = false;

        result = this.focusToMaxTabIndexElement();

        if (result) {
            return;
        }

        result = this.focusToLastTabIndexElement();

        if (result) {
            return;
        }

        this.tabbing.toolboxTabbing.focus(false);
    }

    public focusToMaxTabIndexElement = (): boolean => {
        const tabindexs = this.getFocusAbleElements().map(i => i.tabIndex);
        const maxIndex = Math.max(...tabindexs);

        return this.focusToElementByTabIndex(maxIndex);
    }
    public focusToLastTabIndexElement = (): boolean => {
        const find = this.getFocusAbleElements()[this.focusAbleElements.length - 1];
        if (find && find.element) {
            this.setFocus(find);
            return true;
        }
        return false;
    }

    public focusToElementByTabIndex = (tabIndex: number): boolean => {
        const find = this.getFocusAbleElements().find(i => i.tabIndex === tabIndex);
        if (find && find.element) {
            this.setFocus(find);
            return true;
        }
        return false;
    }

    public focusToElementByHTMLElement = (element: HTMLElement): boolean => {
        const find = this.getFocusAbleElements().find(i => i.element === element);
        if (find && find.element) {
            this.setFocus(find);
            return true;
        }
        return false;
    }

    // public focusToElementByFactory = (factory: IFocusAbleElementFactory): boolean => {
    //     const find = this.focusAbleElements.find(i => i === factory);
    //     if (find && find.element) {
    //         this.tabbing.activeElementIndex = find.tabIndex;
    //         find.element.focus();
    //         return true;
    //     }
    //     return false;
    // }


    public validateAndFocusNextElement = () => {
        const focusAbleElements = this.getFocusAbleElements();
        const current = focusAbleElements.find(i => i.tabIndex === this.tabbing.activeElementIndex);
        if (current && current instanceof MergeElementAndGridEdit && current.factory && current.factory instanceof ElementFactory) {
            validationTabbing.gotoNextElement(this.tabbing, current.factory);
        } else {
            this.focusNextElement();
        }
    }

    public focusNextElement = () => {
        const focusAbleElements = this.getFocusAbleElements();
        const currentIndex = focusAbleElements.findIndex(i => i.tabIndex === this.tabbing.activeElementIndex);
        if (currentIndex === focusAbleElements.length - 1) {
            this.tabbing.toolboxTabbing.focus(false);
        } else {
            const newElement = focusAbleElements[currentIndex + 1];
            if (newElement && newElement.element) {
                this.setFocus(newElement);
            }
        }
    }

    public focusPreviousElement = () => {
        const focusAbleElements = this.getFocusAbleElements();
        const currentIndex = focusAbleElements.findIndex(i => i.tabIndex === this.tabbing.activeElementIndex);
        if (currentIndex === 0) {
            this.tabbing.toolboxTabbing.focus(false);
        } else {
            const newElement = focusAbleElements[currentIndex - 1];
            if (newElement && newElement.element) {
                this.setFocus(newElement);
            }
        }
    }

    public setFocus(find: MergeElementAndGridEdit) {
        this.tabbing.activeElementIndex = find.tabIndex;
        find.element.focus();
    }
}