"use strict";
var __assign = (this && this.__assign) || function () {
    __assign = Object.assign || function(t) {
        for (var s, i = 1, n = arguments.length; i < n; i++) {
            s = arguments[i];
            for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
                t[p] = s[p];
        }
        return t;
    };
    return __assign.apply(this, arguments);
};
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
    if (k2 === undefined) k2 = k;
    Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } });
}) : (function(o, m, k, k2) {
    if (k2 === undefined) k2 = k;
    o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
    Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
    o["default"] = v;
});
var __importStar = (this && this.__importStar) || function (mod) {
    if (mod && mod.__esModule) return mod;
    var result = {};
    if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
    __setModuleDefault(result, mod);
    return result;
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.defaultSideBySideRendererConfig = void 0;
var Rematch = __importStar(require("./rematch"));
var renderUtils = __importStar(require("./render-utils"));
var types_1 = require("./types");
exports.defaultSideBySideRendererConfig = __assign(__assign({}, renderUtils.defaultRenderConfig), { renderNothingWhenEmpty: false, matchingMaxComparisons: 2500, maxLineSizeInBlockForComparison: 200 });
var genericTemplatesPath = 'generic';
var baseTemplatesPath = 'side-by-side';
var iconsBaseTemplatesPath = 'icon';
var tagsBaseTemplatesPath = 'tag';
var SideBySideRenderer = (function () {
    function SideBySideRenderer(hoganUtils, config) {
        if (config === void 0) { config = {}; }
        this.hoganUtils = hoganUtils;
        this.config = __assign(__assign({}, exports.defaultSideBySideRendererConfig), config);
    }
    SideBySideRenderer.prototype.render = function (diffFiles) {
        var _this = this;
        var diffsHtml = diffFiles
            .map(function (file) {
            var diffs;
            if (file.blocks.length) {
                diffs = _this.generateFileHtml(file);
            }
            else {
                diffs = _this.generateEmptyDiff();
            }
            return _this.makeFileDiffHtml(file, diffs);
        })
            .join('\n');
        return this.hoganUtils.render(genericTemplatesPath, 'wrapper', { content: diffsHtml });
    };
    SideBySideRenderer.prototype.makeFileDiffHtml = function (file, diffs) {
        if (this.config.renderNothingWhenEmpty && Array.isArray(file.blocks) && file.blocks.length === 0)
            return '';
        var fileDiffTemplate = this.hoganUtils.template(baseTemplatesPath, 'file-diff');
        var filePathTemplate = this.hoganUtils.template(genericTemplatesPath, 'file-path');
        var fileIconTemplate = this.hoganUtils.template(iconsBaseTemplatesPath, 'file');
        var fileTagTemplate = this.hoganUtils.template(tagsBaseTemplatesPath, renderUtils.getFileIcon(file));
        return fileDiffTemplate.render({
            file: file,
            fileHtmlId: renderUtils.getHtmlId(file),
            diffs: diffs,
            filePath: filePathTemplate.render({
                fileDiffName: renderUtils.filenameDiff(file),
            }, {
                fileIcon: fileIconTemplate,
                fileTag: fileTagTemplate,
            }),
        });
    };
    SideBySideRenderer.prototype.generateEmptyDiff = function () {
        return {
            right: '',
            left: this.hoganUtils.render(genericTemplatesPath, 'empty-diff', {
                contentClass: 'd2h-code-side-line',
                CSSLineClass: renderUtils.CSSLineClass,
            }),
        };
    };
    SideBySideRenderer.prototype.generateFileHtml = function (file) {
        var _this = this;
        var matcher = Rematch.newMatcherFn(Rematch.newDistanceFn(function (e) { return renderUtils.deconstructLine(e.content, file.isCombined).content; }));
        return file.blocks
            .map(function (block) {
            var fileHtml = {
                left: _this.makeHeaderHtml(block.header),
                right: _this.makeHeaderHtml(''),
            };
            _this.applyLineGroupping(block).forEach(function (_a) {
                var contextLines = _a[0], oldLines = _a[1], newLines = _a[2];
                if (oldLines.length && newLines.length && !contextLines.length) {
                    _this.applyRematchMatching(oldLines, newLines, matcher).map(function (_a) {
                        var oldLines = _a[0], newLines = _a[1];
                        var _b = _this.processChangedLines(file.isCombined, oldLines, newLines), left = _b.left, right = _b.right;
                        fileHtml.left += left;
                        fileHtml.right += right;
                    });
                }
                else if (contextLines.length) {
                    contextLines.forEach(function (line) {
                        var _a = renderUtils.deconstructLine(line.content, file.isCombined), prefix = _a.prefix, content = _a.content;
                        var _b = _this.generateLineHtml({
                            type: renderUtils.CSSLineClass.CONTEXT,
                            prefix: prefix,
                            content: content,
                            number: line.oldNumber,
                        }, {
                            type: renderUtils.CSSLineClass.CONTEXT,
                            prefix: prefix,
                            content: content,
                            number: line.newNumber,
                        }), left = _b.left, right = _b.right;
                        fileHtml.left += left;
                        fileHtml.right += right;
                    });
                }
                else if (oldLines.length || newLines.length) {
                    var _b = _this.processChangedLines(file.isCombined, oldLines, newLines), left = _b.left, right = _b.right;
                    fileHtml.left += left;
                    fileHtml.right += right;
                }
                else {
                    console.error('Unknown state reached while processing groups of lines', contextLines, oldLines, newLines);
                }
            });
            return fileHtml;
        })
            .reduce(function (accomulated, html) {
            return { left: accomulated.left + html.left, right: accomulated.right + html.right };
        }, { left: '', right: '' });
    };
    SideBySideRenderer.prototype.applyLineGroupping = function (block) {
        var blockLinesGroups = [];
        var oldLines = [];
        var newLines = [];
        for (var i = 0; i < block.lines.length; i++) {
            var diffLine = block.lines[i];
            if ((diffLine.type !== types_1.LineType.INSERT && newLines.length) ||
                (diffLine.type === types_1.LineType.CONTEXT && oldLines.length > 0)) {
                blockLinesGroups.push([[], oldLines, newLines]);
                oldLines = [];
                newLines = [];
            }
            if (diffLine.type === types_1.LineType.CONTEXT) {
                blockLinesGroups.push([[diffLine], [], []]);
            }
            else if (diffLine.type === types_1.LineType.INSERT && oldLines.length === 0) {
                blockLinesGroups.push([[], [], [diffLine]]);
            }
            else if (diffLine.type === types_1.LineType.INSERT && oldLines.length > 0) {
                newLines.push(diffLine);
            }
            else if (diffLine.type === types_1.LineType.DELETE) {
                oldLines.push(diffLine);
            }
        }
        if (oldLines.length || newLines.length) {
            blockLinesGroups.push([[], oldLines, newLines]);
            oldLines = [];
            newLines = [];
        }
        return blockLinesGroups;
    };
    SideBySideRenderer.prototype.applyRematchMatching = function (oldLines, newLines, matcher) {
        var comparisons = oldLines.length * newLines.length;
        var maxLineSizeInBlock = Math.max.apply(null, [0].concat(oldLines.concat(newLines).map(function (elem) { return elem.content.length; })));
        var doMatching = comparisons < this.config.matchingMaxComparisons &&
            maxLineSizeInBlock < this.config.maxLineSizeInBlockForComparison &&
            (this.config.matching === 'lines' || this.config.matching === 'words');
        return doMatching ? matcher(oldLines, newLines) : [[oldLines, newLines]];
    };
    SideBySideRenderer.prototype.makeHeaderHtml = function (blockHeader) {
        return this.hoganUtils.render(genericTemplatesPath, 'block-header', {
            CSSLineClass: renderUtils.CSSLineClass,
            blockHeader: renderUtils.escapeForHtml(blockHeader),
            lineClass: 'd2h-code-side-linenumber',
            contentClass: 'd2h-code-side-line',
        });
    };
    SideBySideRenderer.prototype.processChangedLines = function (isCombined, oldLines, newLines) {
        var fileHtml = {
            right: '',
            left: '',
        };
        var maxLinesNumber = Math.max(oldLines.length, newLines.length);
        for (var i = 0; i < maxLinesNumber; i++) {
            var oldLine = oldLines[i];
            var newLine = newLines[i];
            var diff = oldLine !== undefined && newLine !== undefined
                ? renderUtils.diffHighlight(oldLine.content, newLine.content, isCombined, this.config)
                : undefined;
            var preparedOldLine = oldLine !== undefined && oldLine.oldNumber !== undefined
                ? __assign(__assign({}, (diff !== undefined
                    ? {
                        prefix: diff.oldLine.prefix,
                        content: diff.oldLine.content,
                        type: renderUtils.CSSLineClass.DELETE_CHANGES,
                    }
                    : __assign(__assign({}, renderUtils.deconstructLine(oldLine.content, isCombined)), { type: renderUtils.toCSSClass(oldLine.type) }))), { number: oldLine.oldNumber }) : undefined;
            var preparedNewLine = newLine !== undefined && newLine.newNumber !== undefined
                ? __assign(__assign({}, (diff !== undefined
                    ? {
                        prefix: diff.newLine.prefix,
                        content: diff.newLine.content,
                        type: renderUtils.CSSLineClass.INSERT_CHANGES,
                    }
                    : __assign(__assign({}, renderUtils.deconstructLine(newLine.content, isCombined)), { type: renderUtils.toCSSClass(newLine.type) }))), { number: newLine.newNumber }) : undefined;
            var _a = this.generateLineHtml(preparedOldLine, preparedNewLine), left = _a.left, right = _a.right;
            fileHtml.left += left;
            fileHtml.right += right;
        }
        return fileHtml;
    };
    SideBySideRenderer.prototype.generateLineHtml = function (oldLine, newLine) {
        return {
            left: this.generateSingleHtml(oldLine),
            right: this.generateSingleHtml(newLine),
        };
    };
    SideBySideRenderer.prototype.generateSingleHtml = function (line) {
        var lineClass = 'd2h-code-side-linenumber';
        var contentClass = 'd2h-code-side-line';
        return this.hoganUtils.render(genericTemplatesPath, 'line', {
            type: (line === null || line === void 0 ? void 0 : line.type) || renderUtils.CSSLineClass.CONTEXT + " d2h-emptyplaceholder",
            lineClass: line !== undefined ? lineClass : lineClass + " d2h-code-side-emptyplaceholder",
            contentClass: line !== undefined ? contentClass : contentClass + " d2h-code-side-emptyplaceholder",
            prefix: (line === null || line === void 0 ? void 0 : line.prefix) === ' ' ? '&nbsp;' : line === null || line === void 0 ? void 0 : line.prefix,
            content: line === null || line === void 0 ? void 0 : line.content,
            lineNumber: line === null || line === void 0 ? void 0 : line.number,
        });
    };
    return SideBySideRenderer;
}());
exports.default = SideBySideRenderer;
//# sourceMappingURL=side-by-side-renderer.js.map