export function getResolver(timeout = 5000) {
    const resolver = {};
    resolver.id = generateId(8);
    resolver.completed = false;
    resolver.resolved = false;
    resolver.rejected = false;
    resolver.promise = new Promise((resolve, reject) => {
        resolver.reject = (e) => {
            resolver.completed = true;
            resolver.rejected = true;
            reject(e);
        };
        resolver.resolve = (data) => {
            resolver.completed = true;
            resolver.resolved = true;
            resolve(data);
        };
    });
    resolver.timeout = setTimeout(() => {
        if (!resolver.completed) {
            resolver.reject();
        }
    }, timeout);
    return resolver;
}
const DEBOUNCE_FN_TO_PROMISE = new WeakMap();
export function debounce(fn, ms = 64) {
    if (!DEBOUNCE_FN_TO_PROMISE.get(fn)) {
        DEBOUNCE_FN_TO_PROMISE.set(fn, wait(ms).then(() => {
            DEBOUNCE_FN_TO_PROMISE.delete(fn);
            fn();
        }));
    }
    return DEBOUNCE_FN_TO_PROMISE.get(fn);
}
export function check(value, msg = "", ...args) {
    if (!value) {
        console.error(msg, ...(args || []));
        throw new Error(msg || "Error");
    }
}
export function wait(ms = 16) {
    if (ms === 16) {
        return new Promise((resolve) => {
            requestAnimationFrame(() => {
                resolve();
            });
        });
    }
    return new Promise((resolve) => {
        setTimeout(() => {
            resolve();
        }, ms);
    });
}
export function deepFreeze(obj) {
    const propNames = Reflect.ownKeys(obj);
    for (const name of propNames) {
        const value = obj[name];
        if ((value && typeof value === "object") || typeof value === "function") {
            deepFreeze(value);
        }
    }
    return Object.freeze(obj);
}
function dec2hex(dec) {
    return dec.toString(16).padStart(2, "0");
}
export function generateId(length) {
    const arr = new Uint8Array(length / 2);
    crypto.getRandomValues(arr);
    return Array.from(arr, dec2hex).join("");
}
export function getObjectValue(obj, objKey, def) {
    if (!obj || !objKey)
        return def;
    const keys = objKey.split(".");
    const key = keys.shift();
    const found = obj[key];
    if (keys.length) {
        return getObjectValue(found, keys.join("."), def);
    }
    return found;
}
export function setObjectValue(obj, objKey, value, createMissingObjects = true) {
    if (!obj || !objKey)
        return obj;
    const keys = objKey.split(".");
    const key = keys.shift();
    if (obj[key] === undefined) {
        if (!createMissingObjects) {
            return;
        }
        obj[key] = {};
    }
    if (!keys.length) {
        obj[key] = value;
    }
    else {
        if (typeof obj[key] != "object") {
            obj[key] = {};
        }
        setObjectValue(obj[key], keys.join("."), value, createMissingObjects);
    }
    return obj;
}
export function moveArrayItem(arr, itemOrFrom, to) {
    const from = typeof itemOrFrom === "number" ? itemOrFrom : arr.indexOf(itemOrFrom);
    arr.splice(to, 0, arr.splice(from, 1)[0]);
}
export function removeArrayItem(arr, itemOrIndex) {
    const index = typeof itemOrIndex === "number" ? itemOrIndex : arr.indexOf(itemOrIndex);
    arr.splice(index, 1);
}
export function injectCss(href) {
    if (document.querySelector(`link[href^="${href}"]`)) {
        return Promise.resolve();
    }
    return new Promise((resolve) => {
        const link = document.createElement("link");
        link.setAttribute("rel", "stylesheet");
        link.setAttribute("type", "text/css");
        const timeout = setTimeout(resolve, 1000);
        link.addEventListener("load", (e) => {
            clearInterval(timeout);
            resolve();
        });
        link.href = href;
        document.head.appendChild(link);
    });
}
export function defineProperty(instance, property, desc) {
    var _a, _b, _c, _d, _e, _f;
    const existingDesc = Object.getOwnPropertyDescriptor(instance, property);
    if ((existingDesc === null || existingDesc === void 0 ? void 0 : existingDesc.configurable) === false) {
        throw new Error(`Error: rgthree-comfy cannot define un-configurable property "${property}"`);
    }
    if ((existingDesc === null || existingDesc === void 0 ? void 0 : existingDesc.get) && desc.get) {
        const descGet = desc.get;
        desc.get = () => {
            existingDesc.get.apply(instance, []);
            return descGet.apply(instance, []);
        };
    }
    if ((existingDesc === null || existingDesc === void 0 ? void 0 : existingDesc.set) && desc.set) {
        const descSet = desc.set;
        desc.set = (v) => {
            existingDesc.set.apply(instance, [v]);
            return descSet.apply(instance, [v]);
        };
    }
    desc.enumerable = (_b = (_a = desc.enumerable) !== null && _a !== void 0 ? _a : existingDesc === null || existingDesc === void 0 ? void 0 : existingDesc.enumerable) !== null && _b !== void 0 ? _b : true;
    desc.configurable = (_d = (_c = desc.configurable) !== null && _c !== void 0 ? _c : existingDesc === null || existingDesc === void 0 ? void 0 : existingDesc.configurable) !== null && _d !== void 0 ? _d : true;
    if (!desc.get && !desc.set) {
        desc.writable = (_f = (_e = desc.writable) !== null && _e !== void 0 ? _e : existingDesc === null || existingDesc === void 0 ? void 0 : existingDesc.writable) !== null && _f !== void 0 ? _f : true;
    }
    return Object.defineProperty(instance, property, desc);
}
export function areDataViewsEqual(a, b) {
    if (a.byteLength !== b.byteLength) {
        return false;
    }
    for (let i = 0; i < a.byteLength; i++) {
        if (a.getUint8(i) !== b.getUint8(i)) {
            return false;
        }
    }
    return true;
}
function looksLikeBase64(source) {
    return source.length > 500 || source.startsWith("data:") || source.includes(";base64,");
}
export function areArrayBuffersEqual(a, b) {
    if (a == b || !a || !b) {
        return a == b;
    }
    return areDataViewsEqual(new DataView(a), new DataView(b));
}
export function newCanvas(widthOrPtOrImage, height) {
    let width;
    if (typeof widthOrPtOrImage !== "number") {
        width = widthOrPtOrImage.width;
        height = widthOrPtOrImage.height;
    }
    else {
        width = widthOrPtOrImage;
        height = height;
    }
    if (height == null) {
        throw new Error("Invalid height supplied when creating new canvas object.");
    }
    const canvas = document.createElement("canvas");
    canvas.width = width;
    canvas.height = height;
    if (widthOrPtOrImage instanceof HTMLImageElement) {
        const ctx = canvas.getContext("2d");
        ctx.drawImage(widthOrPtOrImage, 0, 0, width, height);
    }
    return canvas;
}
export function getCanvasImageData(image) {
    const canvas = newCanvas(image);
    const ctx = canvas.getContext("2d");
    const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);
    return [canvas, ctx, imageData];
}
export async function convertToBase64(source) {
    if (source instanceof Promise) {
        source = await source;
    }
    if (typeof source === "string" && looksLikeBase64(source)) {
        return source;
    }
    if (typeof source === "string" || source instanceof Blob || source instanceof ArrayBuffer) {
        return convertToBase64(await loadImage(source));
    }
    if (source instanceof HTMLImageElement) {
        if (looksLikeBase64(source.src)) {
            return source.src;
        }
        const [canvas, ctx, imageData] = getCanvasImageData(source);
        return convertToBase64(canvas);
    }
    if (source instanceof HTMLCanvasElement) {
        return source.toDataURL("image/png");
    }
    throw Error("Unknown source to convert to base64.");
}
export async function convertToArrayBuffer(source) {
    if (source instanceof Promise) {
        source = await source;
    }
    if (source instanceof ArrayBuffer) {
        return source;
    }
    if (typeof source === "string") {
        if (looksLikeBase64(source)) {
            var binaryString = atob(source.replace(/^.*?;base64,/, ""));
            var bytes = new Uint8Array(binaryString.length);
            for (var i = 0; i < binaryString.length; i++) {
                bytes[i] = binaryString.charCodeAt(i);
            }
            return bytes.buffer;
        }
        return convertToArrayBuffer(await loadImage(source));
    }
    if (source instanceof HTMLImageElement) {
        const [canvas, ctx, imageData] = getCanvasImageData(source);
        return convertToArrayBuffer(canvas);
    }
    if (source instanceof HTMLCanvasElement) {
        return convertToArrayBuffer(source.toDataURL());
    }
    if (source instanceof Blob) {
        return source.arrayBuffer();
    }
    throw Error("Unknown source to convert to arraybuffer.");
}
export async function loadImage(source) {
    if (source instanceof Promise) {
        source = await source;
    }
    if (source instanceof HTMLImageElement) {
        return loadImage(source.src);
    }
    if (source instanceof Blob) {
        return loadImage(source.arrayBuffer());
    }
    if (source instanceof HTMLCanvasElement) {
        return loadImage(source.toDataURL());
    }
    if (source instanceof ArrayBuffer) {
        var binary = "";
        var bytes = new Uint8Array(source);
        var len = bytes.byteLength;
        for (var i = 0; i < len; i++) {
            binary += String.fromCharCode(bytes[i]);
        }
        return loadImage(`data:${getMimeTypeFromArrayBuffer(bytes)};base64,${btoa(binary)}`);
    }
    return new Promise((resolve, reject) => {
        const img = new Image();
        img.addEventListener("load", () => {
            resolve(img);
        });
        img.addEventListener("error", () => {
            reject(img);
        });
        img.src = source;
    });
}
function getMimeTypeFromArrayBuffer(buffer) {
    const len = 4;
    if (buffer.length >= len) {
        let signatureArr = new Array(len);
        for (let i = 0; i < len; i++)
            signatureArr[i] = buffer[i].toString(16);
        const signature = signatureArr.join("").toUpperCase();
        switch (signature) {
            case "89504E47":
                return "image/png";
            case "47494638":
                return "image/gif";
            case "25504446":
                return "application/pdf";
            case "FFD8FFDB":
            case "FFD8FFE0":
                return "image/jpeg";
            case "504B0304":
                return "application/zip";
            default:
                return null;
        }
    }
    return null;
}
export class Broadcaster extends EventTarget {
    constructor(channelName) {
        super();
        this.queue = {};
        this.queue = {};
        this.channel = new BroadcastChannel(channelName);
        this.channel.addEventListener("message", (e) => {
            this.onMessage(e);
        });
    }
    getId() {
        let id;
        do {
            id = generateId(6);
        } while (this.queue[id]);
        return id;
    }
    async broadcastAndWait(action, payload, options) {
        const id = this.getId();
        this.queue[id] = getResolver(options === null || options === void 0 ? void 0 : options.timeout);
        this.channel.postMessage({
            id,
            action,
            payload,
        });
        let response;
        try {
            response = await this.queue[id].promise;
        }
        catch (e) {
            console.log("CAUGHT", e);
            response = [];
        }
        return response;
    }
    broadcast(action, payload) {
        this.channel.postMessage({
            id: this.getId(),
            action,
            payload,
        });
    }
    reply(replyId, action, payload) {
        this.channel.postMessage({
            id: this.getId(),
            replyId,
            action,
            payload,
        });
    }
    openWindowAndWaitForMessage(rgthreePath, windowName) {
        const id = this.getId();
        this.queue[id] = getResolver();
        const win = window.open(`/rgthree/${rgthreePath}#broadcastLoadMsgId=${id}`, windowName);
        return { window: win, promise: this.queue[id].promise };
    }
    onMessage(e) {
        var _a, _b;
        const msgId = ((_a = e.data) === null || _a === void 0 ? void 0 : _a.replyId) || "";
        const queueItem = this.queue[msgId];
        if (queueItem) {
            if (queueItem.completed) {
                console.error(`${msgId} already completed..`);
            }
            queueItem.deferment = queueItem.deferment || { data: [] };
            queueItem.deferment.data.push(e.data.payload);
            queueItem.deferment.timeout && clearTimeout(queueItem.deferment.timeout);
            queueItem.deferment.timeout = setTimeout(() => {
                queueItem.resolve(queueItem.deferment.data);
            }, 250);
        }
        else {
            this.dispatchEvent(new CustomEvent("rgthree-broadcast-message", {
                detail: Object.assign({ replyTo: (_b = e.data) === null || _b === void 0 ? void 0 : _b.id }, e.data),
            }));
        }
    }
    addMessageListener(callback, options) {
        return super.addEventListener("rgthree-broadcast-message", callback, options);
    }
}
const broadcastChannelMap = new Map();
export function broadcastOnChannel(channel, action, payload) {
    let queue = broadcastChannelMap.get(channel);
    if (!queue) {
        broadcastChannelMap.set(channel, {});
        queue = broadcastChannelMap.get(channel);
    }
    let id;
    do {
        id = generateId(6);
    } while (queue[id]);
    queue[id] = getResolver();
    channel.postMessage({
        id,
        action,
        payload,
    });
    return queue[id].promise;
}
