/// <reference path="typings/windows-mutex.d.ts" />
"use strict";
const Promise = require("bluebird");
const electron_1 = require("electron");
const Utils = require("./util/electronutils");
const minimist = require("minimist");
const path = require("path");
const productloader_1 = require("./util/productloader");
const Telemetry = require("./util/telemetry");
const updateservice_1 = require("./updater/updateservice");
const target = require(productloader_1.default.targetId);
// require.resolve() gives path to [target dir]/built/pxtrequire.js, so move up twice to get target root dir
const targetDir = path.resolve(require.resolve(productloader_1.default.targetId), "..", "..");
const pxtCore = target.pxtCore;
let appOptions = {};
let electronHandlers;
let serverPort;
let wsPort;
let win = null;
let windowsMutex = null;
let updateService = null;
let messagingDelay = Promise.delay(15000);
function parseArgs() {
    const opts = {
        alias: {
            a: "debug-webapp",
            n: "npm-start",
            w: "debug-webview"
        },
        boolean: ["debug-webapp", "npm-start", "debug-webview"]
    };
    const parsedArgs = minimist(process.argv.slice(1), opts);
    if (parsedArgs.hasOwnProperty("a")) {
        appOptions.debugWebapp = parsedArgs["a"];
    }
    if (parsedArgs.hasOwnProperty("n")) {
        appOptions.isNpmStart = parsedArgs["n"];
    }
    if (parsedArgs.hasOwnProperty("w")) {
        appOptions.debugWebview = parsedArgs["w"];
    }
}
function fixCwd() {
    if (appOptions.isNpmStart) {
        return;
    }
    // At this point, we are not in a directory that is the root of the app, so we need to change cwd
    const appPath = electron_1.app.getAppPath();
    if (appPath !== process.cwd()) {
        console.log("Changing current working directory to " + appPath);
        process.chdir(appPath);
    }
}
function isUpdateEnabled() {
    return !!productloader_1.default.releaseManifestUrl && !!productloader_1.default.updateDownloadUrl;
}
function createElectronHandlers() {
    let handlers = {};
    if (isUpdateEnabled()) {
        handlers["check-for-update"] = () => {
            if (!updateService) {
                pxtCore.sendElectronMessage({ type: "update-check-error" });
            }
            else {
                updateService.checkForUpdate();
            }
        };
        handlers["update"] = (args) => {
            if (!updateService) {
                pxtCore.sendElectronMessage({ type: "update-download-error" });
            }
            else {
                updateService.update(args.targetVersion, args.type === updateservice_1.UpdateEventType.Critical);
            }
        };
    }
    handlers["quit"] = () => {
        electron_1.app.quit();
    };
    return handlers;
}
function registerUpdateHandlers() {
    const events = [
        "critical-update",
        "update-available",
        "update-not-available",
        "update-check-error",
        "update-download-error"
    ];
    events.forEach((e) => {
        updateService.on(e, (args) => {
            messagingDelay.then(() => {
                pxtCore.sendElectronMessage({
                    type: e,
                    args
                });
            });
        });
    });
}
function startLocalServer() {
    serverPort = Utils.randomInt(49152, 65535);
    wsPort = Utils.randomInt(49152, 65535);
    return pxtCore.mainCli(targetDir, ["serve", "--no-browser", "--electron", "--port", serverPort.toString(), "--wsport", wsPort.toString()], electronHandlers);
}
function main() {
    parseArgs();
    fixCwd();
    electronHandlers = createElectronHandlers();
    if (process.platform === "win32") {
        try {
            const Mutex = require("windows-mutex").Mutex;
            windowsMutex = new Mutex(productloader_1.default.win32MutexName);
        }
        catch (e) {
            // Continue normally, but user might need to manually close app in case of update
            console.log("Unable to init Windows Mutex");
        }
    }
    Telemetry.init(pxtCore);
    Telemetry.tickEvent("electron.app.details", {
        id: process.platform === "win32" ? productloader_1.default.win32AppId : productloader_1.default.darwinBundleIdentifier,
        name: productloader_1.default.nameLong,
        platform: process.platform
    });
    Utils.retryAsync(startLocalServer, () => true, 50, "Unable to find free TCP port", 20)
        .then(() => {
        createWindow();
        if (isUpdateEnabled()) {
            updateService = new updateservice_1.UpdateService();
            registerUpdateHandlers();
            updateService.initialCheck();
        }
        else {
            Telemetry.tickEvent("electron.update.disabled");
        }
    })
        .catch((e) => {
        electron_1.dialog.showErrorBox("Unable to start app", "Please try again");
        electron_1.app.exit();
    });
}
function createWindow() {
    const url = `file://${__dirname}/webview/index.html`;
    win = new electron_1.BrowserWindow({
        width: 1024,
        height: 768,
        title: productloader_1.default.nameLong,
        kiosk: false
    });
    electron_1.Menu.setApplicationMenu(null);
    win.loadURL(url);
    win.on('closed', () => {
        win = null;
    });
    win.webContents.on("did-stop-loading", () => {
        if (appOptions.debugWebview) {
            win.webContents.openDevTools({ mode: "detach" });
        }
        const loadWebappMessage = {
            devtools: appOptions.debugWebapp,
            localtoken: pxtCore.globalConfig.localToken,
            serverPort,
            wsPort
        };
        win.webContents.send("start", loadWebappMessage);
    });
}
function dispose() {
    if (windowsMutex) {
        windowsMutex.release();
    }
}
electron_1.app.on('ready', () => {
    // globalShortcut.register('Control+Shift+X', () => {
    //   win.webContents.openDevTools();
    // });
    main();
});
electron_1.app.on('window-all-closed', () => {
    // TODO: For OSX, keep the app alive when windows are closed. For this, pxt-core needs to expose a way to shut down the local server.
    /*if (process.platform !== 'darwin') {
        app.quit();
    }*/
    electron_1.app.quit();
});
electron_1.app.on('will-quit', () => {
    dispose();
});
// TODO: For OSX, keep the app alive when windows are closed. For this, pxt-core needs to expose a way to shut down the local server.
/*app.on('activate', () => {
    if (win === null) {
        createWindow();
    }
});*/
// Enfore single instance
let isMultipleInstance = electron_1.app.makeSingleInstance(() => {
    win.restore();
    win.focus();
});
if (isMultipleInstance) {
    electron_1.app.quit();
}
