"use strict";
var util = require("util");
var path = require("path");
var fiberBootstrap = require("./fiber-bootstrap");
function Exception() {
}
Exception.prototype = new Error();
function resolveCallStack(error) {
    var stackLines = error.stack.split("\n");
    var parsed = _.map(stackLines, function (line) {
        var match = line.match(/^\s*at ([^(]*) \((.*?):([0-9]+):([0-9]+)\)$/);
        if (match) {
            return match;
        }
        match = line.match(/^\s*at (.*?):([0-9]+):([0-9]+)$/);
        if (match) {
            match.splice(1, 0, "<anonymous>");
            return match;
        }
        return line;
    });
    var SourceMapConsumer = require("./vendor/source-map").sourceMap.SourceMapConsumer;
    var fs = require("fs");
    var remapped = _.map(parsed, function (parsedLine) {
        if (_.isString(parsedLine)) {
            return parsedLine;
        }
        var functionName = parsedLine[1];
        var fileName = parsedLine[2];
        var line = +parsedLine[3];
        var column = +parsedLine[4];
        var mapFileName = fileName + ".map";
        if (!fs.existsSync(mapFileName)) {
            return parsedLine.input;
        }
        var mapData = JSON.parse(fs.readFileSync(mapFileName).toString());
        var consumer = new SourceMapConsumer(mapData);
        var sourcePos = consumer.originalPositionFor({ line: line, column: column });
        var source = path.join(path.dirname(fileName), sourcePos.source);
        return util.format("    at %s (%s:%s:%s)", functionName, source, sourcePos.line, sourcePos.column);
    });
    var outputMessage = remapped.join("\n");
    if (outputMessage.indexOf(error.message) === -1) {
        outputMessage = outputMessage.replace(/Error/, "Error: " + error.message);
    }
    return outputMessage;
}
function installUncaughtExceptionListener(actionOnException) {
    process.on("uncaughtException", function (err) {
        var callstack = err.stack;
        if (callstack) {
            try {
                callstack = resolveCallStack(err);
            }
            catch (err) {
                console.error("Error while resolving callStack:", err);
            }
        }
        console.error(callstack || err.toString());
        tryTrackException(err, $injector);
        if (actionOnException) {
            actionOnException();
        }
    });
}
exports.installUncaughtExceptionListener = installUncaughtExceptionListener;
function tryTrackException(error, injector) {
    var disableAnalytics;
    try {
        disableAnalytics = injector.resolve("staticConfig").disableAnalytics;
    }
    catch (err) {
        disableAnalytics = true;
    }
    if (!disableAnalytics) {
        try {
            var analyticsService = injector.resolve("analyticsService");
            fiberBootstrap.run(function () {
                analyticsService.trackException(error, error.message).wait();
            });
        }
        catch (e) {
            console.error("Error while reporting exception: " + e);
        }
    }
}
var Errors = (function () {
    function Errors($injector) {
        this.$injector = $injector;
        this.printCallStack = false;
    }
    Errors.prototype.fail = function (optsOrFormatStr) {
        var args = [];
        for (var _i = 1; _i < arguments.length; _i++) {
            args[_i - 1] = arguments[_i];
        }
        var opts = optsOrFormatStr;
        if (_.isString(opts)) {
            opts = { formatStr: opts };
        }
        var exception = new Exception();
        exception.name = opts.name || "Exception";
        exception.message = (_a = this.$injector.resolve("messagesService")).getMessage.apply(_a, [opts.formatStr].concat(args));
        exception.stack = (new Error(exception.message)).stack;
        exception.errorCode = opts.errorCode || 127;
        exception.suppressCommandHelp = opts.suppressCommandHelp;
        this.$injector.resolve("logger").trace(opts.formatStr);
        throw exception;
        var _a;
    };
    Errors.prototype.failWithoutHelp = function (message) {
        var args = [];
        for (var _i = 1; _i < arguments.length; _i++) {
            args[_i - 1] = arguments[_i];
        }
        args.unshift(message);
        this.fail({ formatStr: util.format.apply(null, args), suppressCommandHelp: true });
    };
    Errors.prototype.beginCommand = function (action, printCommandHelp) {
        var _this = this;
        return (function () {
            try {
                return action().wait();
            }
            catch (ex) {
                var loggerLevel = $injector.resolve("logger").getLevel().toUpperCase();
                var printCallStack = _this.printCallStack || loggerLevel === "TRACE" || loggerLevel === "DEBUG";
                console.error(printCallStack
                    ? resolveCallStack(ex)
                    : "\x1B[31;1m" + ex.message + "\x1B[0m");
                if (!ex.suppressCommandHelp) {
                    printCommandHelp().wait();
                }
                tryTrackException(ex, _this.$injector);
                process.exit(_.isNumber(ex.errorCode) ? ex.errorCode : 127);
            }
        }).future()();
    };
    Errors.prototype.verifyHeap = function (message) {
        if (global.gc) {
            console.log("verifyHeap: '%s'", message);
            global.gc();
        }
    };
    return Errors;
})();
exports.Errors = Errors;
$injector.register("errors", Errors);
