import { needleLogoOnlySVG } from "../assets/index.js"
import { isDevEnvironment, showBalloonWarning } from "../debug/index.js";
import { hasCommercialLicense, hasProLicense, runtimeLicenseCheckPromise } from "../engine_license.js";
import { Mathf } from "../engine_math.js";
import { LoadingProgressArgs } from "../engine_setup.js";
import { getParam } from "../engine_utils.js";

const debug = getParam("debugloading");
const debugRendering = getParam("debugloadingrendering");
const debugLicense = getParam("debuglicense");

declare type LoadingStyleOption = "dark" | "light" | "auto";

/** @internal */
export class LoadingElementOptions {
    className?: string;
    additionalClasses?: string[];
}

/** @internal */
export interface ILoadingViewHandler {
    onLoadingBegin(message?: string)
    onLoadingUpdate(progress: LoadingProgressArgs | number, message?: string);
    onLoadingFinished(message?: string);
    setMessage(string: string);
}

let currentFileProgress = 0;
let currentFileName: string;
/** @internal */
export function calculateProgress01(progress: LoadingProgressArgs) {
    if (debug) console.log(progress.progress.loaded.toFixed(0) + "/" + progress.progress.total.toFixed(0), progress);

    const count = progress.count;
    const total: number | undefined = progress.progress.total;
    // if the progress event total amount is unknown / not reported
    // we slowly move the progress bar forward
    if (total === 0 || total === undefined) {
        // reset the temp progress when the file has changed
        if (currentFileName !== progress.name)
            currentFileProgress = 0;
        currentFileName = progress.name;
        // slowly move the progress bar forward
        currentFileProgress += (1 - currentFileProgress) * .001;
        if (debug) showBalloonWarning("Loading " + progress.name + " did not report total size");
    }
    else {
        currentFileProgress = progress.progress.loaded / total;
    }
    const prog = progress.index / count + currentFileProgress / count;
    return Mathf.clamp01(prog);
}

/** @internal */
export class EngineLoadingView implements ILoadingViewHandler {

    static LoadingContainerClassName = "loading";

    // the raw progress
    loadingProgress: number = 0;

    /** Usually the NeedleEngineHTMLElement */
    private _element: HTMLElement;
    private _progress: number = 0;
    private _allowCustomLoadingElement: boolean = true;
    private _loadingElement?: HTMLElement;
    private _loadingTextContainer: HTMLElement | null = null;
    private _loadingBar: HTMLElement | null = null;
    private _loadingBarFinishedColor: string | null = null;
    private _messageContainer: HTMLElement | null = null;
    private _loadingElementOptions?: LoadingElementOptions;

    /**
     * Creates a new loading view
     * @param owner the element that will contain the loading view (should be the NeedleEngineHTMLElement)
     */
    constructor(owner: HTMLElement, opts?: LoadingElementOptions) {
        this._element = owner;
        this._loadingElementOptions = opts;
    }

    async onLoadingBegin(message?: string) {
        const _element = this._element.shadowRoot || this._element;
        if (debug) console.warn("Begin Loading")
        if (!this._loadingElement) {
            for (let i = 0; i < _element.children.length; i++) {
                const el = _element.children[i] as HTMLElement;
                if (el.classList.contains(EngineLoadingView.LoadingContainerClassName)) {
                    if (!this._allowCustomLoadingElement) {
                        if (debug) console.warn("Remove custom loading container")
                        _element.removeChild(el);
                        continue;
                    }
                    this._loadingElement = this.createLoadingElement(el);
                }
            }
            if (!this._loadingElement)
                this._loadingElement = this.createLoadingElement();
        }
        this._progress = 0;
        this.loadingProgress = 0;
        this._loadingElement.style.display = "flex";
        _element.appendChild(this._loadingElement);
        this.smoothProgressLoop();
        this.setMessage(message ?? "");
    }

    onLoadingUpdate(progress: LoadingProgressArgs | ProgressEvent | number, message?: string) {
        if (!this._loadingElement?.parentNode) {
            return;
        }
        // console.log(callback.name, callback.progress.loaded / callback.progress.total, callback.index + "/" + callback.count);
        let total01 = 0;
        if (typeof progress === "number") {
            total01 = progress;
        }
        else {
            if ("index" in progress)
                total01 = calculateProgress01(progress);
            if (!message && "name" in progress)
                this.setMessage("loading " + progress.name);
        }
        this.loadingProgress = total01;
        if (message) this.setMessage(message);
        this.updateDisplay();
    }

    onLoadingFinished() {
        if (debug)
            console.warn("Finished Loading");
        if (!debugRendering) {
            this.loadingProgress = 1;
            this.onDoneLoading();
        }
    }

    setMessage(message: string) {
        if (this._messageContainer) {
            this._messageContainer.innerText = message;
        }
    }

    private _progressLoop: any;
    private smoothProgressLoop() {
        if (this._progressLoop) return;
        let dt = 1 / 12;
        if (debugRendering) {
            dt = 1 / 500;
            if (typeof debugRendering === "number") dt *= debugRendering;
        }
        this._progressLoop = setInterval(() => {
            // increase loading speed when almost done
            if (this.loadingProgress >= .95 && !debugRendering) dt = .9;
            this._progress = Mathf.lerp(this._progress, this.loadingProgress, dt * this.loadingProgress);
            this.updateDisplay();
        }, dt);
    }

    private onDoneLoading() {
        if (this._loadingElement) {
            if (debug) console.log("Hiding loading element");
            // animate alpha to 0
            const element = this._loadingElement;
            element.animate([
                { opacity: 1 },
                { opacity: 0 }
            ], {
                duration: 200,
                easing: 'ease-in-out',
            }).addEventListener('finish', () => {
                element.style.display = "none";
                element.remove();
            });
        }
        if (this._progressLoop)
            clearInterval(this._progressLoop);
        this._progressLoop = null;
    }

    private updateDisplay() {
        const t = this._progress;
        const percent = (t * 100).toFixed(0) + "%";
        if (this._loadingBar) {
            this._loadingBar.style.width = t * 100 + "%";
            if(t >= 1 && this._loadingBarFinishedColor) {
                this._loadingBar.style.background = this._loadingBarFinishedColor;
            }
        }

        if (this._loadingTextContainer)
            this._loadingTextContainer.textContent = percent;
    }

    private createLoadingElement(existing?: HTMLElement) {
        if (debug && !existing) console.log("Creating loading element");
        this._loadingElement = existing || document.createElement("div");

        let loadingStyle: LoadingStyleOption = this._element.getAttribute("loading-style") as LoadingStyleOption;
        // if nothing is defined OR loadingStyle is set to auto
        if (!loadingStyle || loadingStyle === "auto") {
            if (window.matchMedia('(prefers-color-scheme: dark)').matches)
                loadingStyle = "dark";
            else
                loadingStyle = "light";
        }


        const hasLicense = hasProLicense();
        if (!existing) {
            this._loadingElement.style.position = "absolute";
            this._loadingElement.style.width = "100%";
            this._loadingElement.style.height = "100%";
            this._loadingElement.style.left = "0";
            this._loadingElement.style.top = "0";
            this._loadingElement.style.overflow = "hidden";
            const loadingBackgroundColor = this._element.getAttribute("loading-background");
            if (loadingBackgroundColor) {
                this._loadingElement.style.background = loadingBackgroundColor;
            }
            else
                this._loadingElement.style.backgroundColor = "transparent";
            this._loadingElement.style.display = "flex";
            this._loadingElement.style.alignItems = "center";
            this._loadingElement.style.justifyContent = "center";
            this._loadingElement.style.zIndex = "0";
            this._loadingElement.style.flexDirection = "column";
            this._loadingElement.style.pointerEvents = "none";
            this._loadingElement.style.color = "white";
            this._loadingElement.style.fontFamily = 'system-ui, Roboto, Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol"';
            this._loadingElement.style.fontSize = "1rem";
            if (loadingStyle === "light")
                this._loadingElement.style.color = "rgba(0,0,0,.6)";
            else
                this._loadingElement.style.color = "rgba(255,255,255,.3)";
        }

        const className = this._loadingElementOptions?.className ?? EngineLoadingView.LoadingContainerClassName;
        this._loadingElement.classList.add(className);
        if (this._loadingElementOptions?.additionalClasses) {
            for (const c of this._loadingElementOptions.additionalClasses) {
                this._loadingElement.classList.add(c);
            }
        }

        const content = document.createElement("div");
        content.style.cssText = `
            position: relative;
            display: flex;
            flex-direction: column;
            align-items: center;
            justify-content: center;
            width: 100%;
            height: 100%;
            pointer-events: none;
        `
        this._loadingElement.appendChild(content);

        const poster = this._element.getAttribute("poster");
        if (poster !== null && poster !== "0") {
            const backgroundImage = document.createElement("div");
            const backgroundBlur = poster?.length ? "0px" : "50px";
            backgroundImage.style.cssText = `
            position: absolute;
            left: 0;
            top: 0;
            bottom: 0;
            right: 0;
            z-index: -1;
            overflow: hidden;

            margin: -${backgroundBlur};
            background: url('${poster?.length ? poster : "/include/poster.webp"}') center center no-repeat;
            background-size: cover;
            filter: blur(${backgroundBlur});
        `;
            this._loadingElement.appendChild(backgroundImage);
        }

        const logo = document.createElement("img");
        const logoWidth = "80%";
        const logoHeight = "15%";
        const logoDelay = ".2s";
        logo.style.userSelect = "none";
        logo.style.objectFit = "contain";
        logo.style.transform = "translateY(30px)";
        logo.style.opacity = "0.0000001";
        logo.style.transition = `transform 1s ease-out ${logoDelay}, opacity .3s ease-in-out ${logoDelay}`;
        logo.src = needleLogoOnlySVG;
        let isUsingCustomLogo = false;
        if (hasLicense && this._element) {
            const customLogo = this._element.getAttribute("logo-src");
            if (customLogo) {
                isUsingCustomLogo = true;
                logo.src = customLogo;
                setTimeout(() => {
                    logo.style.opacity = "1";
                    logo.style.transform = "translateY(0px)";
                }, 1);
            }
        }

        logo.style.width = `${logoWidth}`;
        logo.style.height = `min(1000px, max(${logoHeight}, 50px))`;
        content.appendChild(logo);

        const details = document.createElement("div");
        details.style.cssText = `
        display: flex;
        flex-direction: column;
        align-items: center;
        justify-content: center;
        width: 100%;
        opacity: 0;
        transition: opacity 1s ease-in-out 4s;
        `;
        setTimeout(() => { details.style.opacity = "1"; }, 1);
        this._loadingElement.appendChild(details);

        const loadingBarContainer = document.createElement("div");
        const maxWidth = 100;
        loadingBarContainer.style.display = "flex";
        loadingBarContainer.style.width = maxWidth + "%";
        loadingBarContainer.style.height = "5px";
        loadingBarContainer.style.position = "absolute";
        loadingBarContainer.style.left = "0";
        loadingBarContainer.style.top = "0px";
        loadingBarContainer.style.opacity = "0";
        loadingBarContainer.style.transition = "opacity 1s ease-in-out";
        loadingBarContainer.style.backgroundColor = "rgba(240,240,240,.5)"
        setTimeout(() => { loadingBarContainer.style.opacity = "1"; }, 1);

        this._loadingElement.appendChild(loadingBarContainer);


        this._loadingBar = document.createElement("div");
        loadingBarContainer.appendChild(this._loadingBar);
        const getGradientPos = function (t: number): string {
            return Mathf.lerp(0, maxWidth, t) + "%";
        }
        // `linear-gradient(90deg, #204f49 ${getGradientPos(0)}, #0BA398 ${getGradientPos(.3)}, #66A22F ${getGradientPos(.6)}, #D7DB0A ${getGradientPos(1)})`;
        this._loadingBar.style.backgroundAttachment = "fixed";
        this._loadingBar.style.background = "#c4c4c4ab";
        this._loadingBarFinishedColor = "#ddddddab";
        this._loadingBar.style.width = "0%";
        this._loadingBar.style.height = "100%";

        // this._loadingTextContainer = document.createElement("div");
        // this._loadingTextContainer.style.display = "flex";
        // this._loadingTextContainer.style.justifyContent = "center";
        // this._loadingTextContainer.style.marginTop = ".2rem";
        // details.appendChild(this._loadingTextContainer);

        // const messageContainer = document.createElement("div");
        // this._messageContainer = messageContainer;
        // messageContainer.style.display = "flex";
        // messageContainer.style.fontSize = ".8rem";
        // messageContainer.style.paddingTop = ".1rem";
        // // messageContainer.style.border = "1px solid rgba(255,255,255,.1)";
        // messageContainer.style.justifyContent = "center";
        // details.appendChild(messageContainer);

        // if (hasLicense && this._element) {
        //     const loadingTextColor = this._element.getAttribute("loading-text-color");
        //     if (loadingTextColor) {
        //         messageContainer.style.color = loadingTextColor;
        //     }
        // }

        // this.handleRuntimeLicense(this._loadingElement);

        return this._loadingElement;
    }

    // private async handleRuntimeLicense(loadingElement: HTMLElement) {
    //     // First check if we have a commercial license
    //     let commercialLicense = hasCommercialLicense();
    //     // if it's the case then we don't need to perform a runtime check
    //     if (commercialLicense) return;

    //     // If we don't have a commercial license, then we need to display our message
    //     if (debugLicense) console.log("Loading UI has commercial license?", commercialLicense);
    //     const nonCommercialContainer = document.createElement("div");
    //     nonCommercialContainer.style.paddingTop = ".6em";
    //     nonCommercialContainer.style.fontSize = ".8em";
    //     nonCommercialContainer.style.textTransform = "uppercase";
    //     nonCommercialContainer.innerText = "NEEDLE ENGINE NON COMMERCIAL VERSION\nCLICK HERE TO GET A LICENSE";
    //     nonCommercialContainer.style.cursor = "pointer";
    //     nonCommercialContainer.style.userSelect = "none";
    //     nonCommercialContainer.style.textAlign = "center";
    //     nonCommercialContainer.style.pointerEvents = "all";
    //     nonCommercialContainer.addEventListener("click", () => window.open("https://needle.tools/pricing", "_self"));
    //     nonCommercialContainer.style.opacity = "0";
    //     loadingElement.appendChild(nonCommercialContainer);

    //     // Use the runtime license check
    //     if (!isDevEnvironment() && runtimeLicenseCheckPromise) {
    //         if (debugLicense) console.log("Waiting for runtime license check");
    //         await runtimeLicenseCheckPromise;
    //         commercialLicense = hasCommercialLicense();
    //     }
    //     if (commercialLicense) return;
    //     nonCommercialContainer.style.transition = "opacity .5s ease-in-out";
    //     nonCommercialContainer.style.opacity = "1";
    // }
}