"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.Emulator = void 0;
const tslib_1 = require("tslib");
const express_1 = (0, tslib_1.__importDefault)(require("express"));
const chokidar_1 = (0, tslib_1.__importDefault)(require("chokidar"));
const child_process_1 = (0, tslib_1.__importDefault)(require("child_process"));
const open_1 = (0, tslib_1.__importDefault)(require("open"));
const express_ws_1 = (0, tslib_1.__importDefault)(require("express-ws"));
const fs_1 = (0, tslib_1.__importStar)(require("fs"));
const cors_1 = (0, tslib_1.__importDefault)(require("cors"));
const path_1 = require("path");
class Emulator {
    constructor(emulatorHost) {
        this.emulatorHost = emulatorHost;
        this.projectDir = process.cwd();
    }
    startEmulator() {
        if (!(0, fs_1.existsSync)((0, path_1.join)(this.projectDir, "tokenscript.xml"))) {
            throw new Error("TokenScript XML file not detected, are you running this command from a TokenScript project directory?");
        }
        this.launchServer();
        this.watchProject();
    }
    launchServer() {
        this.server = (0, express_1.default)();
        const port = process.env.PORT ?? 8090;
        const tsHost = "http://localhost:" + port + "/";
        let isRemoteEmulator = false;
        if (this.emulatorHost) {
            this.server.use((0, cors_1.default)());
            isRemoteEmulator = true;
        }
        else {
            this.emulatorHost = tsHost;
        }
        // Uncomment to use local built version of the tokenscript viewer
        //this.server.use(express.static(__dirname + "/../../node_modules/@tokenscript/browser-runtime/dist/"));
        const viewerBundlePath = fs_1.default.existsSync(Emulator.DEV_VIEWER_LOC) ?
            Emulator.DEV_VIEWER_LOC : Emulator.PROD_VIEWER_LOC;
        this.server.use(express_1.default.static(viewerBundlePath));
        this.server.use(express_1.default.static((0, path_1.join)(this.projectDir, "out")));
        this.ws = (0, express_ws_1.default)(this.server);
        this.ws.app.ws('/ws', function (ws, req) {
            ws.on('message', function (msg) {
                console.log(msg);
            });
        });
        this.server.listen(port, () => {
            console.log(`⚡️[server]: Server is running at https://localhost:${port}`);
        });
        const urlToOpen = isRemoteEmulator ? "=" + encodeURIComponent(tsHost) : "";
        // TODO: Store hashes of previous src build & compare.
        //if (!fs.existsSync(join(this.projectDir, "out", "tokenscript.tsml"))){
        this.buildProject(() => {
            (0, open_1.default)(this.emulatorHost + "?emulator" + urlToOpen + "#card=0");
        });
        /*} else {
            open(this.emulatorHost + "?emulator" + urlToOpen + "#card=0");
        }*/
    }
    watchProject() {
        // For package.json projects we are only interested in monitoring src and tokenscript.xml.
        // But for legacy projects we monitor the entire directory, ignoring some folders
        const watchedPaths = [];
        if ((0, fs_1.existsSync)((0, path_1.join)(this.projectDir, "src"))) {
            watchedPaths.push((0, path_1.join)(this.projectDir, "src"));
            watchedPaths.push((0, path_1.join)(this.projectDir, "tokenscript.xml"));
        }
        else {
            watchedPaths.push(this.projectDir);
        }
        chokidar_1.default.watch(watchedPaths, {
            ignoreInitial: true,
            ignored: [
                (0, path_1.join)(this.projectDir, "out"),
                (0, path_1.join)(this.projectDir, "node_modules"),
            ]
        }).on('all', (event, path) => {
            if (this.buildTimer)
                clearTimeout(this.buildTimer);
            this.buildTimer = setTimeout(() => {
                this.buildProject();
            }, 1000);
        });
    }
    buildProject(callback) {
        if (this.buildProcess) {
            this.buildProcess.kill("SIGINT");
            this.buildProcess = undefined;
        }
        console.log("Build started..");
        const buildCommand = (0, fs_1.existsSync)((0, path_1.join)(this.projectDir, "package.json")) ? "npm run build" : "tokenscript build";
        this.buildProcess = child_process_1.default.exec("cd " + this.projectDir + " && " + buildCommand, (error, stdout, stderr) => {
            if (error) {
                const errMsg = "Failed to build TokenScript project: ";
                if (callback) {
                    throw new Error(errMsg + error.message);
                }
                else {
                    console.error(errMsg, error.message);
                }
            }
            this.sendClientNotification();
            console.log("Build updated!");
            if (callback)
                callback();
        });
        this.buildProcess.stdout?.pipe(process.stdout);
    }
    sendClientNotification() {
        this.ws?.getWss().clients.forEach(function (client) {
            client.send("BUILD_UPDATED");
        });
    }
}
exports.Emulator = Emulator;
Emulator.PROD_VIEWER_LOC = (0, path_1.join)(__dirname, "..", "viewerBundle");
Emulator.DEV_VIEWER_LOC = (0, path_1.join)(__dirname, "..", "..", "static", "viewerBundle");
