import { createElement, onDestroy } from "../../lib/skeleton/index.js";
import { qs } from "../../lib/dom.js";

export default async function(IImage, { $menubar }) {
    return class Impl extends IImage {
        constructor() {
            super();
        }

        async getSRC(response) {
            const raw = new LibRaw();
            await raw.open(new Uint8Array(response));
            const imageData = await raw.imageData();
            if (!imageData) throw new Error("Not Found");
            const { buffer, type, preview } = imageData;
            const url = URL.createObjectURL(new Blob([ buffer ], { type }));
            onDestroy(() => {
                URL.revokeObjectURL(url);
                raw.close();
            });
            if (preview) rawMode({ $menubar, raw  });
            return url;
        }
    }
}

function rawMode({ $menubar, raw }) {
    const ICON = {
        RAW: "data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAzODQgMTkyIj48cGF0aCBmaWxsPSIjZjJmMmYyIiBkPSJNMzg0IDE3NmMwIDguOC03LjIgMTYtMTYgMTZIMTZjLTguOCAwLTE2LTcuMi0xNi0xNlYxNmMwLTguOCA3LjItMTYgMTYtMTZoMzUyYzguOCAwIDE2IDcuMiAxNiAxNnoiLz48ZyBmaWxsPSIjNTc1OTVBIiB0cmFuc2Zvcm09InRyYW5zbGF0ZSgtMzIgLTI0MCkiPjxwYXRoIGQ9Ik05NC45MTIgMzc1LjY4YzAgMTEuMTItMTcuMDI0IDExLjUwNC0xNy4wMjQuMjU2VjMwMy4yOGMwLTQuNDggMy40NzItNy44MDggNy42OC03LjgwOEgxMTkuNmMzMi40OCAwIDM5LjEzNiA0My41MDQgMTIuMDE2IDU0LjM2OGwxNy4wMDggMjAuNzJjNi42NTYgOS44NTYtNi42NCAxOS4zMTItMTQuMzM2IDkuNkwxMTQuOTc2IDM1Mi41MjhIOTQuOTEyek05NC45MTIgMzM3LjgwOEgxMTkuNmMxNi42MjQgMCAxNy42NjQtMjYuODY0IDAtMjYuODY0SDk0LjkxMnoiLz48cGF0aCBkPSJNMTYyLjYyNCAzODRjLTQuMDk2LTIuMzItNi42NTYtNi45MTItNC4wOTYtMTIuMjg4bDM2LjcwNC03MS43NmMzLjQ1Ni02Ljc4NCAxMi42NzItNy4wNCAxNS44NzIgMGwzNi4wNjQgNzEuNzZjNS4yNDggOS45NjgtMTAuMjQgMTcuOTA0LTE0LjgzMiA3LjkzNmwtNS42NDgtMTEuMjY0aC00Ny4ybC01LjUwNCAxMS4yNjRjLTIuMDMyIDQuMzUyLTYuNzY4IDUuMjY0LTExLjM2IDQuMzUyek0yMTcuNjMyIDM1MS41MDRsLTE0LjQ0OC0zMS42LTE1LjcyOCAzMS42eiIvPjxwYXRoIGQ9Ik0zNDEuMjQ4IDM1My40MjRsMTkuMDU2LTUyLjcwNGMzLjg0LTEwLjM1MiAxOS4zMTItNS41MDQgMTUuNDg4IDUuNjMybC0yNS4zMjggNjguNzA0Yy0yLjMyIDcuMjk2LTQuNDggOS40NzItOC44MzIgOS40NzItNC42MDggMC02LjAxNi0yLjgzMi04LjU3Ni03LjQyNGwtMjIuMjU2LTUwLjUyOC0yMS4yNDggNDkuNzZjLTIuMzA0IDUuMzYtNC40NjQgOC40MzItOS4wNzIgOC40MzItNC40NjQgMC02Ljc4NC0zLjA3Mi04LjgzMi04LjcwNGwtMjQuODE2LTY5LjcxMmMtMy44NC0xMS41MDQgMTIuNC0xNS43MjggMTUuNzI4LTUuNjMybDE4Ljk0NCA1Mi43MDQgMjIuNjQtNTIuNzA0YzMuMDU2LTcuODA4IDExLjEyLTguMTkyIDE0LjQ0OC0uMzY4eiIvPjwvZz48L3N2Zz4K",
        LOADING: "data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4KPHN2ZyB3aWR0aD0nMTIwcHgnIGhlaWdodD0nMTIwcHgnIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgdmlld0JveD0iMCAwIDEwMCAxMDAiIHByZXNlcnZlQXNwZWN0UmF0aW89InhNaWRZTWlkIiBjbGFzcz0idWlsLXJpbmctYWx0Ij4KICA8cmVjdCB4PSIwIiB5PSIwIiB3aWR0aD0iMTAwIiBoZWlnaHQ9IjEwMCIgZmlsbD0ibm9uZSIgY2xhc3M9ImJrIj48L3JlY3Q+CiAgPGNpcmNsZSBjeD0iNTAiIGN5PSI1MCIgcj0iNDAiIHN0cm9rZT0ibm9uZSIgZmlsbD0ibm9uZSIgc3Ryb2tlLXdpZHRoPSIxMCIgc3Ryb2tlLWxpbmVjYXA9InJvdW5kIj48L2NpcmNsZT4KICA8Y2lyY2xlIGN4PSI1MCIgY3k9IjUwIiByPSI0MCIgc3Ryb2tlPSIjZmZmZmZmIiBmaWxsPSJub25lIiBzdHJva2Utd2lkdGg9IjYiIHN0cm9rZS1saW5lY2FwPSJyb3VuZCI+CiAgICA8YW5pbWF0ZSBhdHRyaWJ1dGVOYW1lPSJzdHJva2UtZGFzaG9mZnNldCIgZHVyPSIycyIgcmVwZWF0Q291bnQ9ImluZGVmaW5pdGUiIGZyb209IjAiIHRvPSI1MDIiPjwvYW5pbWF0ZT4KICAgIDxhbmltYXRlIGF0dHJpYnV0ZU5hbWU9InN0cm9rZS1kYXNoYXJyYXkiIGR1cj0iMnMiIHJlcGVhdENvdW50PSJpbmRlZmluaXRlIiB2YWx1ZXM9IjE1MC42IDEwMC40OzEgMjUwOzE1MC42IDEwMC40Ij48L2FuaW1hdGU+CiAgPC9jaXJjbGU+Cjwvc3ZnPgo=",
    };
    const buttonRender = ({ onclick }) => {
        const $raw = createElement(`
            <span>
               <img class="component_icon" style="width: inherit;" src="${ICON.RAW}" />
            </span>
        `);
        $raw.onclick = onclick
        return $raw;
    };
    let nclick = 0;
    $menubar.add(buttonRender({
        onclick: async (e) => {
            const $photo = qs($menubar.parentElement, ".photo");
            const $button = e.target;
            if (nclick != 0) return;
            nclick += 1;

            const animationBefore = (() => {
                const anim = $photo.animate(
                    [ { filter: "blur(0px)" }, { filter: "blur(10px)" } ],
                    { duration: 300, fill: "forwards", easing: "cubic-bezier(0.16, 1, 0.3, 1)" },
                );
                $button.setAttribute("src", ICON.LOADING);
                return anim;
            })();

            const { buffer, type } = await raw.imageData();
            const url = URL.createObjectURL(new Blob([ buffer ], { type }));
            onDestroy(() => URL.revokeObjectURL(url));

            const animateAfter = (() => {
                $photo.onload = () => {
                    $photo.style.opacity = "0.4";
                    animationBefore.cancel();
                    $photo.animate(
                        [ { opacity: 0.4 }, { opacity: 1 } ],
                        { duration: 1000, fill: "forwards" },
                    );
                };
                $photo.setAttribute("src", url);
                $button.animate(
                    [ { opacity: 1 }, { opacity: 0 } ],
                    { duration: 500, fill: "forwards" },
                ).onfinish = () => $button.classList.add("hidden");
            })();
        },
    }));
}

class LibRaw {
	constructor() {
		this.worker = new Worker(
            new URL("./loader_raw.worker.js", import.meta.url),
            { type:"module" },
        );
		this.waitForWorker = false;
		this.worker.onmessage = ({ data }) => {
            if (!this.waitForWorker) return;
			let { resolve, reject } = this.waitForWorker;
			this.waitForWorker = false;
			if ("reject" in data) reject(data.reject);
		    else resolve(data.resolve);
		};
	}

	async runFn(fn, ...args) {
		let ret = new Promise((resolve, reject) => {
			this.waitForWorker = { reject, resolve };
		});
        const transfer = [];
        if (args.length > 0 && args[0] instanceof Uint8Array) transfer.push(args[0].buffer);
		this.worker.postMessage({ fn, args }, transfer);
		return await ret;
	}

	async open(buffer) {
		return await this.runFn("open", buffer);
	}

	async imageData() {
		return await this.runFn("imageData");
	}

    async close() {
        this.worker.terminate();
    }
}
