var __extends = (this && this.__extends) || (function () {
    var extendStatics = Object.setPrototypeOf ||
        ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
        function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; };
    return function (d, b) {
        extendStatics(d, b);
        function __() { this.constructor = d; }
        d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
    };
})();
(function (factory) {
    if (typeof module === "object" && typeof module.exports === "object") {
        var v = factory(require, exports);
        if (v !== undefined) module.exports = v;
    }
    else if (typeof define === "function" && define.amd) {
        define(["require", "exports", "../common/ErrorFormatter", "fs", "path", "source-map", "./util"], factory);
    }
})(function (require, exports) {
    "use strict";
    Object.defineProperty(exports, "__esModule", { value: true });
    var ErrorFormatter_1 = require("../common/ErrorFormatter");
    var fs_1 = require("fs");
    var path_1 = require("path");
    var source_map_1 = require("source-map");
    var util_1 = require("./util");
    var NodeErrorFormatter = (function (_super) {
        __extends(NodeErrorFormatter, _super);
        function NodeErrorFormatter(executor) {
            var _this = _super.call(this, executor) || this;
            _this.fileSourceMaps = {};
            _this.fileSources = {};
            return _this;
        }
        /**
         * Dereference the source from a traceline.
         */
        NodeErrorFormatter.prototype._getSource = function (tracepath) {
            if (tracepath === '<anonymous>') {
                return 'anonymous';
            }
            var sourcepath = this._getSourceHelper(tracepath);
            while (sourcepath !== tracepath) {
                tracepath = sourcepath;
                sourcepath = this._getSourceHelper(tracepath);
            }
            return tracepath;
        };
        NodeErrorFormatter.prototype._getSourceHelper = function (tracepath) {
            var match;
            var source;
            var line;
            var col;
            var map;
            var originalPos;
            var result;
            if (!(match = /^(.*?):(\d+)(:\d+)?$/.exec(tracepath))) {
                // no line or column data
                return tracepath;
            }
            tracepath = match[1];
            line = Number(match[2]);
            col = match[3] ? Number(match[3].substring(1)) : undefined;
            // If the tracepath starts with the server URL, resolve it to something local
            if (tracepath.indexOf(this.executor.config.serverUrl) === 0) {
                tracepath = tracepath.slice(this.executor.config.serverUrl.length);
                tracepath = tracepath.replace(/^__intern\//, this.executor.config.internPath);
            }
            // Make the tracepath absolute since that's how it will be stored in map stores
            tracepath = path_1.resolve(tracepath);
            var instrumentedStore = this.executor.instrumentedMapStore;
            // first, check for an instrumentation source map
            if (tracepath in instrumentedStore.data) {
                map = new source_map_1.SourceMapConsumer(instrumentedStore.data[tracepath].data);
                originalPos = this.getOriginalPosition(map, line, col);
                line = originalPos.line;
                col = originalPos.column;
                if (originalPos.source) {
                    source = originalPos.source;
                }
            }
            // next, check for original source map
            var sourceMapStore = this.executor.sourceMapStore;
            if (tracepath in sourceMapStore.data) {
                map = new source_map_1.SourceMapConsumer(sourceMapStore.data[tracepath].data);
            }
            else {
                map = this.getSourceMap(tracepath);
            }
            source = path_1.relative('.', tracepath);
            if (map) {
                originalPos = this.getOriginalPosition(map, line, col);
                line = originalPos.line;
                col = originalPos.column;
                if (originalPos.source) {
                    // If original source starts with ./ or ../, or is just a bare filename, assume it's relative to the
                    // current source
                    if (originalPos.source.indexOf('/') === -1 || /\.\.?\//.test(originalPos.source)) {
                        source = path_1.join(path_1.dirname(source), originalPos.source);
                    }
                    else {
                        source = originalPos.source;
                    }
                }
            }
            // Source should be relative, because that's what we want the user to see
            source = path_1.relative('.', source);
            result = source + ':' + line;
            if (col !== null) {
                result += ':' + col;
            }
            return result;
        };
        /**
         * Get the original position of line:column based on map.
         *
         * Assumes mappings are is in order by generatedLine, then by generatedColumn; maps created with
         * SourceMapConsumer.eachMapping should be in this order by default.
         */
        NodeErrorFormatter.prototype.getOriginalPosition = function (map, line, column) {
            var originalPosition = map.originalPositionFor({ line: line, column: column });
            // if the SourceMapConsumer was able to find a location, return it
            if (originalPosition.line != null) {
                return originalPosition;
            }
            var entries = [];
            // find all map entries that apply to the given line in the generated output
            map.eachMapping(function (entry) {
                if (entry.generatedLine === line) {
                    entries.push(entry);
                }
            }, null, source_map_1.SourceMapConsumer.GENERATED_ORDER);
            if (entries.length === 0) {
                // no valid mappings exist -- return the line and column arguments
                return { line: line, column: column };
            }
            var position = entries[0];
            // Chrome/Node.js column is at the start of the term that generated the exception
            // IE column is at the beginning of the expression/line with the exceptional term
            // Safari column number is just after the exceptional term
            //   - need to go back one element in the mapping
            // Firefox, PhantomJS have no column number
            //   - for no col number, find the largest original line number for the generated line
            if (column != null) {
                // find the most likely mapping for the given generated line and column
                var entry = void 0;
                for (var i = 1; i < entries.length; i++) {
                    entry = entries[i];
                    if (column > position.generatedColumn && column >= entry.generatedColumn) {
                        position = entry;
                    }
                }
            }
            return {
                line: position.originalLine,
                column: position.originalColumn,
                source: position.source
            };
        };
        /**
         * Load and process the source map for a given file.
         */
        NodeErrorFormatter.prototype.getSourceMap = function (filepath) {
            if (filepath in this.fileSourceMaps) {
                return this.fileSourceMaps[filepath];
            }
            try {
                var data = void 0;
                if (filepath in this.fileSources) {
                    data = this.fileSources[filepath];
                }
                else {
                    data = fs_1.readFileSync(filepath).toString('utf-8');
                    this.fileSources[filepath] = data;
                }
                var rawMap = util_1.readSourceMap(filepath, data);
                if (rawMap) {
                    this.fileSourceMaps[filepath] = new source_map_1.SourceMapConsumer(rawMap);
                    return this.fileSourceMaps[filepath];
                }
            }
            catch (error) {
                // this is normal for files like node.js -- just return null
            }
        };
        return NodeErrorFormatter;
    }(ErrorFormatter_1.default));
    exports.default = NodeErrorFormatter;
});
//# sourceMappingURL=ErrorFormatter.js.map