import { loadCodeCoverage } from "./code_coverage.ts";
import * as data from "./data.ts";
import { loadFlameGraph } from "./flamegraph.ts";

function countLines(code: string) {
    let lines = 0;
    for (const char of code) {
        if (char === "\n") lines += 1;
    }
    return lines;
}

function createLineElement(code: string): HTMLPreElement {
    const lines = countLines(code) + 1;
    const maxLineWidth = lines.toString().length;
    let text = "";
    for (let i = 1; i < lines; ++i) {
        const node = i.toString().padStart(maxLineWidth);
        text += node;
        text += "\n";
    }
    const lineElement = document.createElement("pre");
    lineElement.classList.add("code-lines");
    lineElement.innerText = text;
    return lineElement;
}

async function checkStatus(): Promise<"running" | "done"> {
    const status = await data.status();

    if (status.running) {
        return "running";
    }
    const statusHtml = document.querySelector<HTMLSpanElement>(
        "#status",
    )!;
    statusHtml.innerText = "Done";
    document.body.classList.remove("status-waiting");
    document.body.classList.add("status-done");
    return "done";
}

function sourceCode(view: Element, codeData: string) {
    const outerContainer = document.createElement("div");
    outerContainer.classList.add("code-container");

    const innerContainer = document.createElement("div");
    innerContainer.classList.add("code-container-inner");

    const lines = createLineElement(codeData);
    const code = document.createElement("pre");

    code.classList.add("code-source");
    code.innerText = codeData;
    innerContainer.append(lines, code);
    outerContainer.append(innerContainer);
    view.replaceChildren(outerContainer);
}

async function codeCoverage(view: Element, codeData: string) {
    const codeCoverageData = await data.codeCoverageData();

    const outerContainer = document.createElement("div");
    outerContainer.classList.add("code-container");

    const innerContainer = document.createElement("div");
    innerContainer.classList.add("code-container-inner");

    function createRadio(
        id: string,
        content: string,
    ): [HTMLDivElement, HTMLInputElement] {
        const label = document.createElement("label");
        label.htmlFor = id;
        label.innerText = content;
        const input = document.createElement("input");
        input.id = id;
        input.name = "coverage-radio";
        input.type = "radio";
        input.hidden = true;
        const container = document.createElement("div");
        container.classList.add("coverage-radio-group");
        container.append(input, label);
        return [container, input];
    }
    const [perfGroup, perfInput] = createRadio(
        "performance-coverage",
        "Performance",
    );
    const [testGroup, testRadio] = createRadio("test-coverage", "Test");

    const radios = document.createElement("div");
    radios.append(perfGroup, testGroup);
    radios.classList.add("coverage-radio");

    const tooltip = document.createElement("div");
    tooltip.id = "covers-tooltip";
    tooltip.hidden = true;
    const code = document.createElement("pre");
    code.classList.add("code-source");
    loadCodeCoverage(
        codeData,
        codeCoverageData,
        code,
        tooltip,
    );
    const lines = createLineElement(codeData);
    innerContainer.append(lines, code);
    outerContainer.append(innerContainer);
    view.replaceChildren(outerContainer, tooltip, radios);
}

async function main() {
    type RenderFns = {
        "source-code": () => void;
        "code-coverage": () => void;
        "flame-graph": () => void;
    };

    const codeData = await data.codeData();

    const view = document.querySelector("#view")!;
    const renderFunctions: RenderFns = {
        "source-code": () => sourceCode(view, codeData),
        "code-coverage": async () => await codeCoverage(view, codeData),
        "flame-graph": async () => {
            const flameGraphData = await data.flameGraphData();
            const flameGraphFnNames = await data.flameGraphFnNames();

            const container = document.createElement("div");
            container.classList.add("flame-graph");
            container.id = "flame-graph";
            const view = document.querySelector("#view")!;
            view.replaceChildren(container);
            loadFlameGraph(flameGraphData, flameGraphFnNames, container);
        },
    };

    const viewRadios: NodeListOf<HTMLInputElement> = document.querySelectorAll(
        'input[name="views"]',
    );
    for (const input of viewRadios) {
        input.addEventListener("input", (ev) => {
            const target = ev.target as HTMLInputElement;
            const value = target.value as keyof RenderFns;
            renderFunctions[value]();
        });
        if (input.checked) {
            const value = input.value as keyof RenderFns;
            renderFunctions[value]();
        }
    }

    checkStatus().then((status) => {
        if (status == "done") {
            return;
        }
        const interval = setInterval(async () => {
            const status = await checkStatus();
            if (status == "done") {
                clearInterval(interval);
            }
        }, 500);
    });
}

main();
