import { ContextRegistry } from "../engine_context_registry.js";
import { isLocalNetwork } from "../engine_networking_utils.js";
import { DeviceUtilities, getParam } from "../engine_utils.js";
const debug = getParam("debugdebug");
let hide = false;
if (getParam("noerrors") || getParam("nooverlaymessages"))
    hide = true;
const globalErrorContainerKey = "needle_engine_global_error_container";
export var LogType;
(function (LogType) {
    LogType[LogType["Log"] = 0] = "Log";
    LogType[LogType["Warn"] = 1] = "Warn";
    LogType[LogType["Error"] = 2] = "Error";
})(LogType || (LogType = {}));
export function getErrorCount() {
    return _errorCount;
}
const _errorListeners = new Array();
/** Register callback when a new error happens */
export function onError(cb) { _errorListeners.push(cb); }
/** Unregister error callback */
export function offError(cb) { _errorListeners.splice(_errorListeners.indexOf(cb), 1); }
let isInvokingErrorListeners = false;
function invokeErrorListeners(...args) {
    if (isInvokingErrorListeners)
        return; // prevent infinite loop
    isInvokingErrorListeners = true;
    try {
        for (let i = 0; i < _errorListeners.length; i++) {
            _errorListeners[i](...args);
        }
    }
    catch (e) {
        console.error(e);
    }
    isInvokingErrorListeners = false;
}
const originalConsoleError = console.error;
const patchedConsoleError = function (...args) {
    originalConsoleError.apply(console, args);
    onParseError(args);
    addLog(LogType.Error, args, {}, null, null);
    onReceivedError(...args);
};
/** Set false to prevent overlay messages from being shown */
export function setAllowBalloonMessages(allow) {
    hide = !allow;
    if (allow)
        console.error = patchedConsoleError;
    else
        console.error = originalConsoleError;
}
/**
 * @deprecated Use {@link setAllowBalloonMessages} instead
 */
export function setAllowOverlayMessages(allow) {
    return setAllowBalloonMessages(allow);
}
export function makeErrorsVisibleForDevelopment() {
    if (hide)
        return;
    if (debug)
        console.warn("Patch console", window.location.hostname);
    console.error = patchedConsoleError;
    window.addEventListener("error", (event) => {
        if (!event)
            return;
        const message = event.error;
        if (message === undefined) {
            if (isLocalNetwork())
                console.warn("Received unknown error", event, event.target);
            return;
        }
        addLog(LogType.Error, message, {}, event.filename, event.lineno);
        onReceivedError(event);
    }, true);
    window.addEventListener("unhandledrejection", (event) => {
        if (hide)
            return;
        if (!event)
            return;
        if (event.reason)
            addLog(LogType.Error, event.reason.message, {}, event.reason.stack);
        else
            addLog(LogType.Error, "unhandled rejection");
        onReceivedError(event);
    });
}
let _errorCount = 0;
function onReceivedError(...args) {
    _errorCount += 1;
    invokeErrorListeners(...args);
}
function onParseError(args) {
    if (Array.isArray(args)) {
        for (let i = 0; i < args.length; i++) {
            const arg = args[i];
            if (typeof arg === "string" && arg.startsWith("THREE.PropertyBinding: Trying to update node for track:")) {
                args[i] = "Some animated objects couldn't be found: see console for details";
            }
        }
    }
}
const seen = new Set();
export function addLog(type, message, opts = {}, _file, _line) {
    if (hide)
        return;
    if (opts.once === true) {
        let key = "";
        if (Array.isArray(message)) {
            for (let i = 0; i < message.length; i++) {
                let msgPart = message[i];
                if (msgPart instanceof Error) {
                    msgPart = msgPart.message;
                }
                if (typeof msgPart === "object")
                    continue;
                if (i > 0)
                    key += " ";
                key += msgPart;
            }
        }
        else if (typeof message === "string") {
            key = message;
        }
        if (seen.has(key))
            return;
        seen.add(key);
    }
    const context = ContextRegistry.Current;
    let domElement = context?.domElement ?? document.querySelector("needle-engine");
    // check if we're in webxr dom overlay
    if (context?.isInAR) {
        domElement = context.arOverlayElement;
    }
    if (!domElement)
        return;
    if (Array.isArray(message)) {
        let newMessage = "";
        for (let i = 0; i < message.length; i++) {
            let msg = message[i];
            if (msg instanceof Error) {
                msg = msg.message;
            }
            if (typeof msg === "object")
                continue;
            if (i > 0)
                newMessage += " ";
            newMessage += msg;
        }
        message = newMessage;
    }
    if (!message || message.length <= 0)
        return;
    showMessage(type, domElement, message, opts);
}
// function getLocation(err: Error): string {
//     const location = err.stack;
//     console.log(location);
//     if (location) {
//         locationRegex.exec(location);
//         const match = locationRegex.exec(location);
//         return match ? match[1] : "";
//     }
//     return "";
// }
/**
 *
 */
const currentMessages = new Map();
const minDuration = .2; // minimum duration in seconds for messages to be shown, to prevent flicker
function showMessage(type, element, msg, opts = {}) {
    if (msg === null || msg === undefined)
        return;
    const container = getLogsContainer(element);
    if (container.childElementCount >= 20) {
        const last = container.lastElementChild;
        returnMessageContainer(last);
    }
    // truncate long messages before they go into the cache/set
    if (msg.length > 400)
        msg = msg.substring(0, 400) + "...";
    const key = opts.key ?? msg;
    if (currentMessages.has(key)) {
        const existing = currentMessages.get(key);
        existing?.update(msg, opts);
        return;
    }
    const msgcontainer = getMessageContainer(type, msg);
    container.prepend(msgcontainer);
    const removeFunction = () => {
        currentMessages.delete(key);
        returnMessageContainer(msgcontainer);
    };
    let timeout = setTimeout(removeFunction, (Math.max(minDuration, opts.duration ?? 10)) * 1000);
    currentMessages.set(key, {
        update: (newText, newOpts) => {
            if (newText.length > 400)
                newText = newText.substring(0, 400) + "...";
            msgcontainer.innerHTML = newText;
            if (newOpts.duration) {
                clearTimeout(timeout);
                timeout = setTimeout(removeFunction, Math.max(minDuration, newOpts.duration) * 1000);
            }
        },
        removeFunction
    });
}
/**
 * Clear all overlay messages from the screen
 */
export function clearMessages() {
    if (debug)
        console.log("Clearing messages");
    for (const cur of currentMessages.values()) {
        cur?.removeFunction.call(cur);
    }
    currentMessages.clear();
}
const logsContainerStyles = `

@import url('https://fonts.googleapis.com/css2?family=Roboto+Flex:opsz,wght@8..144,100..1000&display=swap');

div[data-needle_engine_debug_overlay] {
    font-family: 'Roboto Flex', sans-serif;
    font-weight: 400;
    font-size: 16px;
}

div[data-needle_engine_debug_overlay] strong {
    font-weight: 700;
}

div[data-needle_engine_debug_overlay] a {
    color: white;
    text-decoration: none;
    border-bottom: 1px solid rgba(255, 255, 255, 0.3);
}

div[data-needle_engine_debug_overlay] a:hover {
    text-decoration: none;
    border: none;
}

div[data-needle_engine_debug_overlay] .log strong {
    color: rgba(200,200,200,.9);
}

div[data-needle_engine_debug_overlay] .warn strong {
    color: rgba(255,255,230, 1);
}

div[data-needle_engine_debug_overlay] .error strong {
    color: rgba(255,100,120, 1);
}
`;
function getLogsContainer(domElement) {
    if (!globalThis[globalErrorContainerKey]) {
        globalThis[globalErrorContainerKey] = new Map();
    }
    const errorsMap = globalThis[globalErrorContainerKey];
    if (errorsMap.has(domElement)) {
        return errorsMap.get(domElement);
    }
    else {
        const container = document.createElement("div");
        errorsMap.set(domElement, container);
        container.setAttribute("data-needle_engine_debug_overlay", "");
        container.classList.add("debug-container");
        container.style.cssText = `
            position: absolute;
            top: 0;
            right: 5px;
            padding-top: env(safe-area-inset-top, 0px);
            max-width: 70%;
            max-height: calc(100% - 105px);
            z-index: 100000;
            pointer-events: scroll;
            display: flex;
            align-items: end;
            flex-direction: column;
            color: white;
            overflow: auto;
            word-break: break-word;
        `;
        // Show the messages left aligned in app clip (because there's a menu button on the right side)
        if (DeviceUtilities.isNeedleAppClip()) {
            container.style.left = "5px";
            container.style.right = "unset";
        }
        // for safe area insets to work we need to set 
        //  <meta name="viewport" content="viewport-fit=cover">
        const meta = document.querySelector('meta[name="viewport"]');
        if (meta && !meta.getAttribute("content")?.includes("viewport-fit=")) {
            meta.setAttribute("content", meta.getAttribute("content") + ",viewport-fit=cover");
        }
        if (domElement.shadowRoot)
            domElement.shadowRoot.appendChild(container);
        else
            domElement.appendChild(container);
        const style = document.createElement("style");
        style.innerHTML = logsContainerStyles;
        container.appendChild(style);
        return container;
    }
}
const typeSymbol = Symbol("logtype");
const containerCache = new Map();
function returnMessageContainer(container) {
    container.remove();
    const type = container[typeSymbol];
    const containers = containerCache.get(type) ?? [];
    containers.push(container);
    containerCache.set(type, containers);
}
function getMessageContainer(type, msg) {
    if (containerCache.has(type)) {
        const containers = containerCache.get(type);
        if (containers.length > 0) {
            const container = containers.pop();
            container.innerHTML = msg;
            return container;
        }
    }
    const element = document.createElement("div");
    element.setAttribute("data-id", "__needle_engine_debug_overlay");
    element.style.marginRight = "5px";
    element.style.padding = ".5em";
    element.style.backgroundColor = "rgba(0,0,0,.9)";
    element.style.marginTop = "5px";
    element.style.marginBottom = "3px";
    element.style.borderRadius = "8px";
    element.style.pointerEvents = "all";
    element.style.userSelect = "text";
    element.style.maxWidth = "250px";
    element.style.whiteSpace = "pre-wrap";
    element.style["backdrop-filter"] = "blur(10px)";
    element.style["-webkit-backdrop-filter"] = "blur(10px)";
    element.style.backgroundColor = "rgba(20,20,20,.8)";
    element.style.boxShadow = "inset 0 0 80px rgba(0,0,0,.2), 0 0 5px rgba(0,0,0,.2)";
    element.style.border = "1px solid rgba(160,160,160,.2)";
    element[typeSymbol] = type;
    switch (type) {
        case LogType.Log:
            element.classList.add("log");
            element.style.color = "rgba(200,200,200,.7)";
            element.style.backgroundColor = "rgba(40,40,40,.7)";
            // element.style.backgroundColor = "rgba(200,200,200,.5)";
            break;
        case LogType.Warn:
            element.classList.add("warn");
            element.style.color = "rgb(255, 255, 150)";
            element.style.backgroundColor = "rgba(50,50,20,.8)";
            // element.style.backgroundColor = "rgba(245,245,50,.5)";
            break;
        case LogType.Error:
            element.classList.add("error");
            element.style.color = "rgb(255, 50, 50";
            element.style.backgroundColor = "rgba(50,20,20,.8)";
            // element.style.backgroundColor = "rgba(255,50,50,.5)";
            break;
    }
    element.title = "Open the browser console (F12) for more information";
    // msg = msg.replace(/[\n\r]/g, "<br/>");
    // console.log(msg);
    element.innerHTML = msg;
    return element;
}
//# sourceMappingURL=debug_overlay.js.map