let wasmSize = null;
// See comment in dist/web/Dockerfile about imhex.wasm.size
fetch("imhex.wasm.size").then(async (resp) => {
    wasmSize = parseInt((await resp.text()).trim());
    console.log(`Real WASM binary size is ${wasmSize} bytes`);
});

// Monkeypatch WebAssembly to have a progress bar
// inspired from: https://github.com/WordPress/wordpress-playground/pull/46 (but had to be modified)
function monkeyPatch(progressFun) {
    const _instantiateStreaming = WebAssembly.instantiateStreaming;
    WebAssembly.instantiateStreaming = (response, ...args) => {
        // Do not collect wasm content length here see above
        const file = response.url.substring(
            new URL(response.url).origin.length + 1
        );
        const reportingResponse = new Response(
            new ReadableStream(
                {
                    async start(controller) {
                        const reader = response.clone().body.getReader();
                        let loaded = 0;
                        for (; ;) {
                            const { done, value } = await reader.read();
                            if (done) {
                                if(wasmSize) progressFun(file, wasmSize);
                                break;
                            }
                            loaded += value.byteLength;
                            progressFun(file, loaded);
                            controller.enqueue(value);
                        }
                        controller.close();
                    }
                },
                {
                    status: response.status,
                    statusText: response.statusText
                }
            )
        );
        for (const pair of response.headers.entries()) {
            reportingResponse.headers.set(pair[0], pair[1]);
        }

        return _instantiateStreaming(reportingResponse, ...args);
    }
}
monkeyPatch((file, done) =>  {
    if (!wasmSize) return;
    if (done > wasmSize) {
        console.warn(`Downloaded binary size ${done} is larger than expected WASM size ${wasmSize}`);
        return;
    }

    const percent  = ((done / wasmSize) * 100).toFixed(0);
    const mibNow   = (done / 1024**2).toFixed(1);
    const mibTotal = (wasmSize / 1024**2).toFixed(1);

    let root = document.querySelector(':root');
    root.style.setProperty("--progress", `${percent}%`)
    document.getElementById("progress-bar-content").innerHTML = `${percent}%  [${mibNow} MiB / ${mibTotal} MiB]`;
});

function glfwSetCursorCustom(wnd, shape) {
    let body = document.getElementsByTagName("body")[0]
    switch (shape) {
        case 0x00036001: // GLFW_ARROW_CURSOR
            body.style.cursor = "default";
            break;
        case 0x00036002: // GLFW_IBEAM_CURSOR
            body.style.cursor = "text";
            break;
        case 0x00036003: // GLFW_CROSSHAIR_CURSOR
            body.style.cursor = "crosshair";
            break;
        case 0x00036004: // GLFW_HAND_CURSOR
            body.style.cursor = "pointer";
            break;
        case 0x00036005: // GLFW_HRESIZE_CURSOR
            body.style.cursor = "ew-resize";
            break;
        case 0x00036006: // GLFW_VRESIZE_CURSOR
            body.style.cursor = "ns-resize";
            break;
        default:
            body.style.cursor = "default";
            break;
    }

}

function glfwCreateStandardCursorCustom(shape) {
    return shape
}

var notWorkingTimer = setTimeout(() => {
    document.getElementById("not_working").classList.add("visible")
}, 5000);

var Module = {
    preRun:  [],
    postRun: function() {
        // Patch the emscripten GLFW module to send mouse and touch events in the right order
        // For ImGui interactions to correctly work with touch input, MousePos events need
        // to be processed first and then MouseButton events in the next frame. By default,
        // GLFW does the exact opposite, which causes buttons to require two taps to register
        // and windows get "stuck" to the cursor when dragged or resized
        GLFW.onMousemove = event => {
            if (event.type === "touchmove") {
                event.preventDefault();
                let primaryChanged = false;
                for (let i of event.changedTouches) {
                    if (GLFW.primaryTouchId === i.identifier) {
                        Browser.setMouseCoords(i.pageX, i.pageY);
                        primaryChanged = true;
                        break;
                    }
                }
                if (!primaryChanged) {
                    return;
                }
            } else {
                Browser.calculateMouseEvent(event);
            }
        };

        GLFW.onMouseButtonChanged = (event, status) => {
            if (!GLFW.active) return;
            if (event.target != Module["canvas"]) return;
            const isTouchType = event.type === "touchstart" || event.type === "touchend" || event.type === "touchcancel";
            let eventButton = 0;
            if (isTouchType) {
                event.preventDefault();
                let primaryChanged = false;
                if (GLFW.primaryTouchId === null && event.type === "touchstart" && event.targetTouches.length > 0) {
                    const chosenTouch = event.targetTouches[0];
                    GLFW.primaryTouchId = chosenTouch.identifier;
                    Browser.setMouseCoords(chosenTouch.pageX, chosenTouch.pageY);
                    primaryChanged = true;
                } else if (event.type === "touchend" || event.type === "touchcancel") {
                    for (let i of event.changedTouches) {
                        if (GLFW.primaryTouchId === i.identifier) {
                            GLFW.primaryTouchId = null;
                            primaryChanged = true;
                            break;
                        }
                    }
                }
                if (!primaryChanged) {
                    return;
                }
            } else {
                Browser.calculateMouseEvent(event);
                eventButton = GLFW.DOMToGLFWMouseButton(event);
            }
            if (status == 1) {
                GLFW.active.buttons |= (1 << eventButton);
                try {
                    event.target.setCapture();
                } catch (e) {}
            } else {
                GLFW.active.buttons &= ~(1 << eventButton);
            }

            if (GLFW.active.cursorPosFunc) {
                getWasmTableEntry(GLFW.active.cursorPosFunc)(GLFW.active.id, Browser.mouseX, Browser.mouseY);
            }
            if (GLFW.active.mouseButtonFunc) {
                getWasmTableEntry(GLFW.active.mouseButtonFunc)(GLFW.active.id, eventButton, status, GLFW.getModBits(GLFW.active));
            }
        };
    },
    onRuntimeInitialized: function() {
        // Triggered when the wasm module is loaded and ready to use.
        document.getElementById("loading").style.display = "none"
        document.getElementById("canvas").style.display = "initial"

        clearTimeout(notWorkingTimer);
    },
    print:   (function() { })(),
    printErr: function(text) {  },
    canvas: (function() {
        const canvas = document.getElementById('canvas');
        canvas.addEventListener("webglcontextlost", function(e) {
            alert('WebGL context lost, please reload the page');
            e.preventDefault();
        }, false);

        if (typeof WebGL2RenderingContext !== 'undefined') {
            let gl = canvas.getContext('webgl2', { stencil: true });
            if (!gl) {
                console.error('WebGL 2 not available, falling back to WebGL');
                gl = canvas.getContext('webgl', { stencil: true });
            }
            if (!gl) {
                alert('WebGL not available with stencil buffer');
            }
            return canvas;
        } else {
            alert('WebGL 2 not supported by this browser');
        }
    })(),
    setStatus: function(text) { },
    totalDependencies: 0,
    monitorRunDependencies: function(left) {
    },
    instantiateWasm: function(imports, successCallback) {
        imports.env.glfwSetCursor = glfwSetCursorCustom
        imports.env.glfwCreateStandardCursor = glfwCreateStandardCursorCustom
        instantiateAsync(wasmBinary, wasmBinaryFile, imports, (result) => {
            successCallback(result.instance, result.module)
        });
    },
    arguments: []
};

// Handle passing arguments to the wasm module
const queryString = window.location.search;
const urlParams = new URLSearchParams(queryString);
if (urlParams.has("lang")) {
    Module["arguments"].push("--language");
    Module["arguments"].push(urlParams.get("lang"));
}

window.addEventListener('resize', js_resizeCanvas, false);
function js_resizeCanvas() {
    let canvas = document.getElementById('canvas');

    canvas.top    = document.documentElement.clientTop;
    canvas.left   = document.documentElement.clientLeft;
    canvas.width  = Math.min(document.documentElement.clientWidth, window.innerWidth || 0);
    canvas.height = Math.min(document.documentElement.clientHeight, window.innerHeight || 0);
}