"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
var tslib_1 = require("tslib");
var tiny_invariant_1 = tslib_1.__importDefault(require("tiny-invariant"));
var util_1 = require("./util");
var Mapping = /** @class */ (function () {
    function Mapping(sourceLines, sourceLoc, targetLoc) {
        if (targetLoc === void 0) { targetLoc = sourceLoc; }
        this.sourceLines = sourceLines;
        this.sourceLoc = sourceLoc;
        this.targetLoc = targetLoc;
    }
    Mapping.prototype.slice = function (lines, start, end) {
        if (end === void 0) { end = lines.lastPos(); }
        var sourceLines = this.sourceLines;
        var sourceLoc = this.sourceLoc;
        var targetLoc = this.targetLoc;
        function skip(name) {
            var sourceFromPos = sourceLoc[name];
            var targetFromPos = targetLoc[name];
            var targetToPos = start;
            if (name === "end") {
                targetToPos = end;
            }
            else {
                (0, tiny_invariant_1.default)(name === "start");
            }
            return skipChars(sourceLines, sourceFromPos, lines, targetFromPos, targetToPos);
        }
        if ((0, util_1.comparePos)(start, targetLoc.start) <= 0) {
            if ((0, util_1.comparePos)(targetLoc.end, end) <= 0) {
                targetLoc = {
                    start: subtractPos(targetLoc.start, start.line, start.column),
                    end: subtractPos(targetLoc.end, start.line, start.column),
                };
                // The sourceLoc can stay the same because the contents of the
                // targetLoc have not changed.
            }
            else if ((0, util_1.comparePos)(end, targetLoc.start) <= 0) {
                return null;
            }
            else {
                sourceLoc = {
                    start: sourceLoc.start,
                    end: skip("end"),
                };
                targetLoc = {
                    start: subtractPos(targetLoc.start, start.line, start.column),
                    end: subtractPos(end, start.line, start.column),
                };
            }
        }
        else {
            if ((0, util_1.comparePos)(targetLoc.end, start) <= 0) {
                return null;
            }
            if ((0, util_1.comparePos)(targetLoc.end, end) <= 0) {
                sourceLoc = {
                    start: skip("start"),
                    end: sourceLoc.end,
                };
                targetLoc = {
                    // Same as subtractPos(start, start.line, start.column):
                    start: { line: 1, column: 0 },
                    end: subtractPos(targetLoc.end, start.line, start.column),
                };
            }
            else {
                sourceLoc = {
                    start: skip("start"),
                    end: skip("end"),
                };
                targetLoc = {
                    // Same as subtractPos(start, start.line, start.column):
                    start: { line: 1, column: 0 },
                    end: subtractPos(end, start.line, start.column),
                };
            }
        }
        return new Mapping(this.sourceLines, sourceLoc, targetLoc);
    };
    Mapping.prototype.add = function (line, column) {
        return new Mapping(this.sourceLines, this.sourceLoc, {
            start: addPos(this.targetLoc.start, line, column),
            end: addPos(this.targetLoc.end, line, column),
        });
    };
    Mapping.prototype.subtract = function (line, column) {
        return new Mapping(this.sourceLines, this.sourceLoc, {
            start: subtractPos(this.targetLoc.start, line, column),
            end: subtractPos(this.targetLoc.end, line, column),
        });
    };
    Mapping.prototype.reindent = function (oldLines, newLines) {
        var start = this.targetLoc.start;
        var end = this.targetLoc.end;
        // Ask the Lines objects how far each line really moved, rather than
        // assuming every line moved by the same amount, because getIndentAt
        // never reports a negative indentation, so lines that already begin at
        // column zero stay put while the lines around them are dedented.
        var startShift = newLines.getIndentAt(start.line) - oldLines.getIndentAt(start.line);
        var endShift = newLines.getIndentAt(end.line) - oldLines.getIndentAt(end.line);
        if (startShift === 0 && endShift === 0) {
            return this;
        }
        return new Mapping(this.sourceLines, this.sourceLoc, {
            start: {
                line: start.line,
                column: Math.max(0, start.column + startShift),
            },
            end: {
                line: end.line,
                column: Math.max(0, end.column + endShift),
            },
        });
    };
    return Mapping;
}());
exports.default = Mapping;
function addPos(toPos, line, column) {
    return {
        line: toPos.line + line - 1,
        column: toPos.line === 1 ? toPos.column + column : toPos.column,
    };
}
function subtractPos(fromPos, line, column) {
    return {
        line: fromPos.line - line + 1,
        column: fromPos.line === line ? fromPos.column - column : fromPos.column,
    };
}
function skipChars(sourceLines, sourceFromPos, targetLines, targetFromPos, targetToPos) {
    var targetComparison = (0, util_1.comparePos)(targetFromPos, targetToPos);
    if (targetComparison === 0) {
        // Trivial case: no characters to skip.
        return sourceFromPos;
    }
    var sourceCursor, targetCursor;
    if (targetComparison < 0) {
        // Skipping forward.
        sourceCursor =
            sourceLines.skipSpaces(sourceFromPos) || sourceLines.lastPos();
        targetCursor =
            targetLines.skipSpaces(targetFromPos) || targetLines.lastPos();
        var lineDiff = targetToPos.line - targetCursor.line;
        sourceCursor.line += lineDiff;
        targetCursor.line += lineDiff;
        if (lineDiff > 0) {
            // If jumping to later lines, reset columns to the beginnings
            // of those lines.
            sourceCursor.column = 0;
            targetCursor.column = 0;
        }
        else {
            (0, tiny_invariant_1.default)(lineDiff === 0);
        }
        while ((0, util_1.comparePos)(targetCursor, targetToPos) < 0 &&
            targetLines.nextPos(targetCursor, true)) {
            (0, tiny_invariant_1.default)(sourceLines.nextPos(sourceCursor, true));
            (0, tiny_invariant_1.default)(sourceLines.charAt(sourceCursor) === targetLines.charAt(targetCursor));
        }
    }
    else {
        // Skipping backward.
        sourceCursor =
            sourceLines.skipSpaces(sourceFromPos, true) || sourceLines.firstPos();
        targetCursor =
            targetLines.skipSpaces(targetFromPos, true) || targetLines.firstPos();
        var lineDiff = targetToPos.line - targetCursor.line;
        sourceCursor.line += lineDiff;
        targetCursor.line += lineDiff;
        if (lineDiff < 0) {
            // If jumping to earlier lines, reset columns to the ends of
            // those lines.
            sourceCursor.column = sourceLines.getLineLength(sourceCursor.line);
            targetCursor.column = targetLines.getLineLength(targetCursor.line);
        }
        else {
            (0, tiny_invariant_1.default)(lineDiff === 0);
        }
        while ((0, util_1.comparePos)(targetToPos, targetCursor) < 0 &&
            targetLines.prevPos(targetCursor, true)) {
            (0, tiny_invariant_1.default)(sourceLines.prevPos(sourceCursor, true));
            (0, tiny_invariant_1.default)(sourceLines.charAt(sourceCursor) === targetLines.charAt(targetCursor));
        }
    }
    return sourceCursor;
}
