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

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

const TPL = new Template(`
    <slot>
        <div id="default-animation">
        </div>
    </slot>
`);

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

const STYLE = new GlobalStyle(`
    :host {
        position: absolute;
        display: flex !important;
        justify-content: center;
        align-items: center;
        left: 0;
        top: 0;
        right: 0;
        bottom: 0;
        cursor: wait;
        pointer-events: none;
        z-index: var(--z-index-busy-indicator, 1000000000);
        background: rgba(0, 0, 0, 0.3);
        transform: translateZ(0);
        animation: popInLater 0ms ease-in 100ms forwards;
        animation-fill-mode: forwards;
        opacity: 0;
    }
    
    #default-animation {
        width: 5vmin;
        height: 5vmin;
        border-radius: 50%;
        background: conic-gradient(rgba(255, 255, 255, 0) 220deg, rgba(255, 255, 255, 1) 360deg);
        animation: rotate 1s linear infinite;
    }
    
    @keyframes rotate {
        0% {
            transform: rotate(0deg)
        }
    
        100% {
            transform: rotate(360deg)
        }
    }
    
    @keyframes popInLater {
        to {
            opacity: 1;
            pointer-events: all;
        }
    }
    
`);

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

import CustomElement from "./element/CustomElement.js";
import ActiveCounter from "../util/counter/ActiveCounter.js";

// TODO test with 0 delay
const DOM_CHANGE_DELAY = 10;

export default class BusyIndicator extends CustomElement {

    #isActive = false;

    #targetEl = document.body;

    #activeCounter = new ActiveCounter();

    #promiseCounter = new ActiveCounter();

    constructor() {
        super();
        this.shadowRoot.append(TPL.generate());
        STYLE.apply(this.shadowRoot);
    }

    busy() {
        return new Promise((resolve) => {
            if (this.#activeCounter.add() && !this.#isActive) {
                this.#isActive = true;
                this.#targetEl.append(this);
                setTimeout(()=> {
                    resolve();
                }, DOM_CHANGE_DELAY);
            } else {
                resolve();
            }
        });
    }

    unbusy() {
        return new Promise((resolve) => {
            if (this.#activeCounter.remove() && this.#isActive) {
                this.#isActive = false;
                this.remove();
                setTimeout(()=> {
                    resolve();
                }, 0);
            } else {
                resolve();
            }
        });
    }

    reset() {
        return new Promise((resolve) => {
            if (this.#activeCounter.reset() && this.#isActive) {
                this.#isActive = false;
                this.remove();
                setTimeout(()=> {
                    resolve();
                }, 0);
            } else {
                resolve();
            }
        });
    }

    async promise(promise) {
        if (promise instanceof Promise) {
            await this.#busyPromise();
            try {
                const value = await promise;
                await this.#unbusyPromise();
                return value;
            } catch (error) {
                await this.#unbusyPromise();
                throw error;
            }
        }
        return promise;
    }

    setTarget(element) {
        if (element instanceof HTMLElement || element instanceof ShadowRoot) {
            this.#targetEl = element;
        } else {
            this.#targetEl = document.body;
        }
        if (this.#isActive) {
            this.#targetEl.append(this);
        }
    }

    #busyPromise() {
        return new Promise((resolve) => {
            if (this.#promiseCounter.add() && !this.#isActive) {
                this.#isActive = true;
                this.#targetEl.append(this);
                setTimeout(()=> {
                    resolve();
                }, DOM_CHANGE_DELAY);
            } else {
                resolve();
            }
        });
    }

    #unbusyPromise() {
        return new Promise((resolve) => {
            if (this.#promiseCounter.remove() && this.#isActive) {
                this.#isActive = false;
                this.remove();
                setTimeout(()=> {
                    resolve();
                }, 0);
            } else {
                resolve();
            }
        });
    }

}

customElements.define("emc-busy-indicator", BusyIndicator);
