"use strict";
var __extends = (this && this.__extends) || function (d, b) {
    for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p];
    function __() { this.constructor = d; }
    d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
};
var HF2 = pxt.HF2;
var U = pxt.U;
var HID = undefined;
function getHID() {
    if (HID === undefined) {
        try {
            HID = require("node-hid");
        }
        catch (e) {
            pxt.log('node-hid failed to load, ignoring...');
            HID = null;
        }
    }
    return HID;
}
function listAsync() {
    for (var _i = 0, _a = getHF2Devices(); _i < _a.length; _i++) {
        var h = _a[_i];
        console.log(deviceInfo(h));
    }
    return Promise.resolve();
}
exports.listAsync = listAsync;
function serialAsync() {
    return initAsync()
        .then(function (d) {
        connectSerial(d);
    });
}
exports.serialAsync = serialAsync;
function dmesgAsync() {
    return initAsync()
        .then(function (d) { return d.talkAsync(pxt.HF2.HF2_CMD_DMESG)
        .then(function (resp) {
        console.log(U.fromUTF8(U.uint8ArrayToString(resp)));
        return d.disconnectAsync();
    }); });
}
exports.dmesgAsync = dmesgAsync;
function hex(n) {
    return ("000" + n.toString(16)).slice(-4);
}
function deviceInfo(h) {
    return h.product + " (by " + h.manufacturer + " at USB " + hex(h.vendorId) + ":" + hex(h.productId) + ")";
}
exports.deviceInfo = deviceInfo;
function getHF2Devices() {
    var hid = getHID();
    if (!hid)
        return [];
    var devices = hid.devices();
    var serial = pxt.appTarget.serial;
    return devices.filter(function (d) {
        return (serial && parseInt(serial.productId) == d.productId && parseInt(serial.vendorId) == d.vendorId) ||
            (d.release & 0xff00) == 0x4200;
    });
}
exports.getHF2Devices = getHF2Devices;
function hf2ConnectAsync(path, raw) {
    if (raw === void 0) { raw = false; }
    return Promise.resolve()
        .then(function () {
        // in .then() to make sure we catch errors
        var h = new HF2.Wrapper(new HidIO(path));
        h.rawMode = raw;
        return h.reconnectAsync(true).then(function () { return h; });
    });
}
exports.hf2ConnectAsync = hf2ConnectAsync;
function mkPacketIOAsync() {
    return Promise.resolve()
        .then(function () {
        // in .then() to make sure we catch errors
        return new HidIO(null);
    });
}
exports.mkPacketIOAsync = mkPacketIOAsync;
pxt.HF2.mkPacketIOAsync = mkPacketIOAsync;
var hf2Dev;
function initAsync(path) {
    if (path === void 0) { path = null; }
    if (!hf2Dev) {
        hf2Dev = hf2ConnectAsync(path);
    }
    return hf2Dev;
}
exports.initAsync = initAsync;
function connectSerial(w) {
    process.stdin.on("data", function (buf) {
        w.sendSerialAsync(new Uint8Array(buf)).done();
    });
    w.onSerial = function (arr, iserr) {
        var buf = new Buffer(arr);
        if (iserr)
            process.stderr.write(buf);
        else
            process.stdout.write(buf);
    };
}
exports.connectSerial = connectSerial;
var HIDError = (function (_super) {
    __extends(HIDError, _super);
    function HIDError(m) {
        _super.call(this, m);
        this.message = m;
    }
    return HIDError;
}(Error));
exports.HIDError = HIDError;
var HidIO = (function () {
    function HidIO(requestedPath) {
        this.requestedPath = requestedPath;
        this.onData = function (v) { };
        this.onEvent = function (v) { };
        this.onError = function (e) { };
        this.connect();
    }
    HidIO.prototype.connect = function () {
        var _this = this;
        var hid = getHID();
        U.assert(hid);
        if (this.requestedPath == null) {
            var devs = getHF2Devices();
            if (devs.length == 0)
                throw new HIDError("no devices found");
            this.path = devs[0].path;
        }
        else {
            this.path = this.requestedPath;
        }
        this.dev = new HID.HID(this.path);
        this.dev.on("data", function (v) {
            //console.log("got", v.toString("hex"))
            _this.onData(new Uint8Array(v));
        });
        this.dev.on("error", function (v) { return _this.onError(v); });
    };
    HidIO.prototype.talkOneAsync = function (pkt) {
        var _this = this;
        return new Promise(function (resolve, reject) {
            var prev = _this.onData;
            _this.onData = function (buf) {
                _this.onData = prev;
                resolve(buf);
            };
            _this.sendPacketAsync(pkt).catch(reject);
        });
    };
    HidIO.prototype.sendPacketAsync = function (pkt) {
        var _this = this;
        //console.log("SEND: " + new Buffer(pkt).toString("hex"))
        return Promise.resolve()
            .then(function () {
            var lst = [0];
            for (var i = 0; i < Math.max(64, pkt.length); ++i)
                lst.push(pkt[i] || 0);
            _this.dev.write(lst);
        });
    };
    HidIO.prototype.error = function (msg) {
        var fullmsg = "HID error on " + this.path + ": " + msg;
        console.error(fullmsg);
        throw new HIDError(fullmsg);
    };
    HidIO.prototype.disconnectAsync = function () {
        var _this = this;
        if (!this.dev)
            return Promise.resolve();
        // see https://github.com/node-hid/node-hid/issues/61
        this.dev.removeAllListeners("data");
        this.dev.removeAllListeners("error");
        var pkt = new Uint8Array([0x48]);
        this.sendPacketAsync(pkt).catch(function (e) { });
        return Promise.delay(100)
            .then(function () {
            if (_this.dev) {
                _this.dev.close();
                _this.dev = null;
            }
        });
    };
    HidIO.prototype.reconnectAsync = function () {
        var _this = this;
        return this.disconnectAsync()
            .then(function () {
            _this.connect();
        });
    };
    return HidIO;
}());
exports.HidIO = HidIO;
