import { animation_duration, animation_easing } from '../script.js';
import { delay } from './utils.js';



/**@readonly*/
/**@enum {Number}*/
export const POPUP_TYPE = {
    'TEXT': 1,
    'CONFIRM': 2,
    'INPUT': 3,
};

/**@readonly*/
/**@enum {Boolean}*/
export const POPUP_RESULT = {
    'AFFIRMATIVE': true,
    'NEGATIVE': false,
    'CANCELLED': undefined,
};



export class Popup {
    /**@type {POPUP_TYPE}*/ type;

    /**@type {HTMLElement}*/ dom;
    /**@type {HTMLElement}*/ dlg;
    /**@type {HTMLElement}*/ text;
    /**@type {HTMLTextAreaElement}*/ input;
    /**@type {HTMLElement}*/ ok;
    /**@type {HTMLElement}*/ cancel;

    /**@type {POPUP_RESULT}*/ result;
    /**@type {any}*/ value;

    /**@type {Promise}*/ promise;
    /**@type {Function}*/ resolver;

    /**@type {Function}*/ keyListenerBound;



    /**
     * @typedef {{okButton?: string, cancelButton?: string, rows?: number, wide?: boolean, large?: boolean, allowHorizontalScrolling?: boolean, allowVerticalScrolling?: boolean }} PopupOptions - Options for the popup.
     * @param {JQuery<HTMLElement>|string|Element} text - Text to display in the popup.
     * @param {POPUP_TYPE} type - One of Popup.TYPE
     * @param {string} inputValue - Value to set the input to.
     * @param {PopupOptions} options - Options for the popup.
     */
    constructor(text, type, inputValue = '', { okButton, cancelButton, rows, wide, large, allowHorizontalScrolling, allowVerticalScrolling } = {}) {
        this.type = type;

        /**@type {HTMLTemplateElement}*/
        const template = document.querySelector('#shadow_popup_template');
        // @ts-ignore
        this.dom = template.content.cloneNode(true).querySelector('.shadow_popup');
        const dlg = this.dom.querySelector('.dialogue_popup');
        // @ts-ignore
        this.dlg = dlg;
        this.text = this.dom.querySelector('.dialogue_popup_text');
        this.input = this.dom.querySelector('.dialogue_popup_input');
        this.ok = this.dom.querySelector('.dialogue_popup_ok');
        this.cancel = this.dom.querySelector('.dialogue_popup_cancel');

        if (wide) dlg.classList.add('wide_dialogue_popup');
        if (large) dlg.classList.add('large_dialogue_popup');
        if (allowHorizontalScrolling) dlg.classList.add('horizontal_scrolling_dialogue_popup');
        if (allowVerticalScrolling) dlg.classList.add('vertical_scrolling_dialogue_popup');

        this.ok.textContent = okButton ?? 'OK';
        this.cancel.textContent = cancelButton ?? 'Cancel';

        switch (type) {
            case POPUP_TYPE.TEXT: {
                this.input.style.display = 'none';
                this.cancel.style.display = 'none';
                break;
            }
            case POPUP_TYPE.CONFIRM: {
                this.input.style.display = 'none';
                this.ok.textContent = okButton ?? 'Yes';
                this.cancel.textContent = cancelButton ?? 'No';
                break;
            }
            case POPUP_TYPE.INPUT: {
                this.input.style.display = 'block';
                this.ok.textContent = okButton ?? 'Save';
                break;
            }
            default: {
                // illegal argument
            }
        }

        this.input.value = inputValue;
        this.input.rows = rows ?? 1;

        this.text.innerHTML = '';
        if (text instanceof jQuery) {
            $(this.text).append(text);
        } else if (text instanceof HTMLElement) {
            this.text.append(text);
        } else if (typeof text == 'string') {
            this.text.innerHTML = text;
        } else {
            // illegal argument
        }

        this.input.addEventListener('keydown', (evt) => {
            if (evt.key != 'Enter' || evt.altKey || evt.ctrlKey || evt.shiftKey) return;
            evt.preventDefault();
            evt.stopPropagation();
            this.completeAffirmative();
        });

        this.ok.addEventListener('click', () => this.completeAffirmative());
        this.cancel.addEventListener('click', () => this.completeNegative());
        const keyListener = (evt) => {
            switch (evt.key) {
                case 'Escape': {
                    // does it really matter where we check?
                    const topModal = document.elementFromPoint(window.innerWidth / 2, window.innerHeight / 2)?.closest('.shadow_popup');
                    if (topModal == this.dom) {
                        evt.preventDefault();
                        evt.stopPropagation();
                        this.completeCancelled();
                        window.removeEventListener('keydown', keyListenerBound);
                        break;
                    }
                }
            }
        };
        const keyListenerBound = keyListener.bind(this);
        window.addEventListener('keydown', keyListenerBound);
    }

    async show() {
        document.body.append(this.dom);
        this.dom.style.display = 'block';
        switch (this.type) {
            case POPUP_TYPE.INPUT: {
                this.input.focus();
                break;
            }
        }

        $(this.dom).transition({
            opacity: 1,
            duration: animation_duration,
            easing: animation_easing,
        });

        this.promise = new Promise((resolve) => {
            this.resolver = resolve;
        });
        return this.promise;
    }

    completeAffirmative() {
        switch (this.type) {
            case POPUP_TYPE.TEXT:
            case POPUP_TYPE.CONFIRM: {
                this.value = true;
                break;
            }
            case POPUP_TYPE.INPUT: {
                this.value = this.input.value;
                break;
            }
        }
        this.result = POPUP_RESULT.AFFIRMATIVE;
        this.hide();
    }

    completeNegative() {
        switch (this.type) {
            case POPUP_TYPE.TEXT:
            case POPUP_TYPE.CONFIRM:
            case POPUP_TYPE.INPUT: {
                this.value = false;
                break;
            }
        }
        this.result = POPUP_RESULT.NEGATIVE;
        this.hide();
    }

    completeCancelled() {
        switch (this.type) {
            case POPUP_TYPE.TEXT:
            case POPUP_TYPE.CONFIRM:
            case POPUP_TYPE.INPUT: {
                this.value = null;
                break;
            }
        }
        this.result = POPUP_RESULT.CANCELLED;
        this.hide();
    }



    hide() {
        $(this.dom).transition({
            opacity: 0,
            duration: animation_duration,
            easing: animation_easing,
        });
        delay(animation_duration).then(() => {
            this.dom.remove();
        });

        this.resolver(this.value);
    }
}



/**
 * Displays a blocking popup with a given text and type.
 * @param {JQuery<HTMLElement>|string|Element} text - Text to display in the popup.
 * @param {POPUP_TYPE} type
 * @param {string} inputValue - Value to set the input to.
 * @param {PopupOptions} options - Options for the popup.
 * @returns
 */
export function callGenericPopup(text, type, inputValue = '', { okButton, cancelButton, rows, wide, large, allowHorizontalScrolling, allowVerticalScrolling } = {}) {
    const popup = new Popup(
        text,
        type,
        inputValue,
        { okButton, cancelButton, rows, wide, large, allowHorizontalScrolling, allowVerticalScrolling },
    );
    return popup.show();
}