/// <reference path="../typings/globals/node/index.d.ts"/>
"use strict";
var child_process = require('child_process');
var fs = require('fs');
var net = require('net');
var U = pxt.Util;
function fatal(msg) {
    U.userError(msg);
}
function getOpenOcdPath() {
    var dirs = [
        process.env["HOME"] + "/Library/Arduino",
        process.env["USERPROFILE"] + "/AppData/Local/Arduino",
        process.env["HOME"] + "/.arduino",
    ];
    var pkgDir = "";
    for (var ardV = 15; ardV < 50; ++ardV) {
        for (var _i = 0, dirs_1 = dirs; _i < dirs_1.length; _i++) {
            var d = dirs_1[_i];
            pkgDir = d + ardV + "/packages/arduino/";
            if (fs.existsSync(pkgDir))
                break;
            pkgDir = "";
        }
        if (pkgDir)
            break;
    }
    if (!pkgDir)
        fatal("cannot find Arduino packages directory");
    var openocdParPath = pkgDir + "tools/openocd/";
    if (!fs.existsSync(openocdParPath))
        fatal(openocdParPath + " doesn't exists; openocd not installed in Arduino?");
    var subdirs = fs.readdirSync(openocdParPath);
    if (!subdirs.length)
        fatal("no directories in " + openocdParPath);
    subdirs.sort(pxt.semver.strcmp);
    subdirs.reverse();
    var openocdPath = openocdParPath + subdirs[0] + "/";
    if (!fs.existsSync(openocdPath + "bin"))
        fatal("openocd not installed in Arduino");
    var openocdBin = openocdPath + "bin/openocd";
    if (process.platform == "win32")
        openocdBin += ".exe";
    var script = pxt.appTarget.compile.openocdScript;
    if (!script)
        fatal("no openocdScript in pxtarget.json");
    var cmd = "log_output built/openocd.log; " + script + "; init; halt;";
    var args = [openocdBin, "-d2",
        "-s", openocdPath + "/share/openocd/scripts/",
        "-c", cmd];
    return args;
}
function startAsync(c) {
    var cs = pxt.appTarget.compileService;
    var f = cs.codalBinary ?
        "built/codal/build/" + cs.codalBinary :
        "built/yt/build/" + cs.yottaTarget + "/source/" + cs.yottaBinary.replace(/\.hex$/, "").replace(/-combined$/, "");
    if (!fs.existsSync(f))
        fatal("compiled file not found: " + f);
    var oargs = getOpenOcdPath();
    fs.writeFileSync("built/openocd.gdb", "\ntarget extended-remote localhost:3333\ndefine rst\n  set {int}(0x20008000-4) = 0xf02669ef\n  monitor reset halt\n  continue\nend\necho Use 'rst' command to re-run program from start (set your breakpoints first!).\\n\n");
    pxt.log("starting openocd: " + oargs.join(" "));
    var oproc = child_process.spawn(oargs[0], oargs.slice(1), {
        stdio: "inherit",
        detached: true,
    });
    var gdbargs = ["--command=built/openocd.gdb", f].concat(c.arguments);
    pxt.log("starting gdb with: " + gdbargs.join(" "));
    var proc = child_process.spawn("arm-none-eabi-gdb", gdbargs, {
        stdio: "inherit",
        detached: true,
    });
    process.on('SIGINT', function () {
        // this doesn't actully kill it, it usually just stops the target program
        proc.kill('SIGINT');
    });
    var shutdownOpenocdAsync = function () { return new Promise(function (resolve, reject) {
        var s = net.connect(4444);
        s.on("connect", function () {
            pxt.log("shutdown openocd...");
            s.write("shutdown\n");
            s.end();
        });
        s.on("error", function () {
            pxt.log("Cannot connect to openocd to shut it down. Probably already down.");
            resolve();
        });
        s.on("close", function () { return resolve(); });
    }); };
    var start = Date.now();
    return new Promise(function (resolve, reject) {
        proc.on("error", function (err) { reject(err); });
        proc.on("close", function () {
            resolve();
        });
    })
        .finally(function () { return Promise.delay(Math.max(0, 2000 - (Date.now() - start)))
        .then(shutdownOpenocdAsync); });
}
exports.startAsync = startAsync;
