let outputIndex = 0;

export const jsConsole = console;

const loggingEnabled = false;
function log(...message) {
    if (loggingEnabled) {
        jsConsole.log("[eval]", ...message);
    }
}

// Overwrite globalThis.console with domConsole to redirect output to the DOM console.
// To always output to the JavaScript console regardless, use jsConsole.
export const domConsole = {
    log(...message) {
        postMessage({
            kind: "output",
            output: {
                kind: "console.log",
                message: [...message],
            },
            outputIndex,
        });
    },
};

export async function defaultEvalModule(_state, source, language, _params) {
    if (language == "javascript") {
        let blobUrl = URL.createObjectURL(new Blob([source], { type: "text/javascript" }));
        let module = await import(blobUrl);
        for (let exportedKey in module) {
            globalThis[exportedKey] = module[exportedKey];
        }
        return _state;
    } else {
        return null;
    }
}

let kernel = {
    evalModule: defaultEvalModule,
};

export function getKernel() {
    return kernel;
}

let evaluationComplete = null;

export async function evaluate(commands, { error, newOutput }) {
    if (evaluationComplete != null) {
        await evaluationComplete;
    }

    let signalEvaluationComplete;
    evaluationComplete = new Promise((resolve, _reject) => {
        signalEvaluationComplete = resolve;
    });

    outputIndex = 0;
    try {
        let kernelState = {};
        for (let command of commands) {
            log(`frame ${treehouseSandboxInternals.outputIndex} module`, command);
            if (command.kind == "module") {
                await kernel.evalModule(
                    kernelState,
                    command.source,
                    command.language,
                    command.kernelParameters,
                );
            } else if (command.kind == "output") {
                if (newOutput != null) {
                    newOutput(outputIndex);
                }
                ++outputIndex;
            }
        }
        log(`frame ${treehouseSandboxInternals.outputIndex} evalComplete`);
        postMessage({
            kind: "evalComplete",
        });
    } catch (err) {
        log(`frame ${treehouseSandboxInternals.outputIndex} error`, err);
        postMessage({
            kind: "output",
            output: {
                kind: "error",
                message: [
                    err.stack.length > 0 ? err.toString() + "\n\n" + err.stack : err.toString(),
                ],
            },
            outputIndex,
        });
        if (error != null) {
            error();
        }
    }
    signalEvaluationComplete();
}