/*---------------------------------------------------------------------------------------------
 *  Copyright (c) Microsoft Corporation. All rights reserved.
 *  Licensed under the MIT License. See License.txt in the project root for license information.
 *--------------------------------------------------------------------------------------------*/
(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", "../htmlLanguageTypes", "../parser/htmlScanner"], factory);
    }
})(function (require, exports) {
    "use strict";
    Object.defineProperty(exports, "__esModule", { value: true });
    exports.HTMLFolding = void 0;
    var htmlLanguageTypes_1 = require("../htmlLanguageTypes");
    var htmlScanner_1 = require("../parser/htmlScanner");
    var HTMLFolding = /** @class */ (function () {
        function HTMLFolding(dataManager) {
            this.dataManager = dataManager;
        }
        HTMLFolding.prototype.limitRanges = function (ranges, rangeLimit) {
            ranges = ranges.sort(function (r1, r2) {
                var diff = r1.startLine - r2.startLine;
                if (diff === 0) {
                    diff = r1.endLine - r2.endLine;
                }
                return diff;
            });
            // compute each range's nesting level in 'nestingLevels'.
            // count the number of ranges for each level in 'nestingLevelCounts'
            var top = void 0;
            var previous = [];
            var nestingLevels = [];
            var nestingLevelCounts = [];
            var setNestingLevel = function (index, level) {
                nestingLevels[index] = level;
                if (level < 30) {
                    nestingLevelCounts[level] = (nestingLevelCounts[level] || 0) + 1;
                }
            };
            // compute nesting levels and sanitize
            for (var i = 0; i < ranges.length; i++) {
                var entry = ranges[i];
                if (!top) {
                    top = entry;
                    setNestingLevel(i, 0);
                }
                else {
                    if (entry.startLine > top.startLine) {
                        if (entry.endLine <= top.endLine) {
                            previous.push(top);
                            top = entry;
                            setNestingLevel(i, previous.length);
                        }
                        else if (entry.startLine > top.endLine) {
                            do {
                                top = previous.pop();
                            } while (top && entry.startLine > top.endLine);
                            if (top) {
                                previous.push(top);
                            }
                            top = entry;
                            setNestingLevel(i, previous.length);
                        }
                    }
                }
            }
            var entries = 0;
            var maxLevel = 0;
            for (var i = 0; i < nestingLevelCounts.length; i++) {
                var n = nestingLevelCounts[i];
                if (n) {
                    if (n + entries > rangeLimit) {
                        maxLevel = i;
                        break;
                    }
                    entries += n;
                }
            }
            var result = [];
            for (var i = 0; i < ranges.length; i++) {
                var level = nestingLevels[i];
                if (typeof level === 'number') {
                    if (level < maxLevel || (level === maxLevel && entries++ < rangeLimit)) {
                        result.push(ranges[i]);
                    }
                }
            }
            return result;
        };
        HTMLFolding.prototype.getFoldingRanges = function (document, context) {
            var voidElements = this.dataManager.getVoidElements(document.languageId);
            var scanner = (0, htmlScanner_1.createScanner)(document.getText());
            var token = scanner.scan();
            var ranges = [];
            var stack = [];
            var lastTagName = null;
            var prevStart = -1;
            function addRange(range) {
                ranges.push(range);
                prevStart = range.startLine;
            }
            while (token !== htmlLanguageTypes_1.TokenType.EOS) {
                switch (token) {
                    case htmlLanguageTypes_1.TokenType.StartTag: {
                        var tagName = scanner.getTokenText();
                        var startLine = document.positionAt(scanner.getTokenOffset()).line;
                        stack.push({ startLine: startLine, tagName: tagName });
                        lastTagName = tagName;
                        break;
                    }
                    case htmlLanguageTypes_1.TokenType.EndTag: {
                        lastTagName = scanner.getTokenText();
                        break;
                    }
                    case htmlLanguageTypes_1.TokenType.StartTagClose:
                        if (!lastTagName || !this.dataManager.isVoidElement(lastTagName, voidElements)) {
                            break;
                        }
                    // fallthrough
                    case htmlLanguageTypes_1.TokenType.EndTagClose:
                    case htmlLanguageTypes_1.TokenType.StartTagSelfClose: {
                        var i = stack.length - 1;
                        while (i >= 0 && stack[i].tagName !== lastTagName) {
                            i--;
                        }
                        if (i >= 0) {
                            var stackElement = stack[i];
                            stack.length = i;
                            var line = document.positionAt(scanner.getTokenOffset()).line;
                            var startLine = stackElement.startLine;
                            var endLine = line - 1;
                            if (endLine > startLine && prevStart !== startLine) {
                                addRange({ startLine: startLine, endLine: endLine });
                            }
                        }
                        break;
                    }
                    case htmlLanguageTypes_1.TokenType.Comment: {
                        var startLine = document.positionAt(scanner.getTokenOffset()).line;
                        var text = scanner.getTokenText();
                        var m = text.match(/^\s*#(region\b)|(endregion\b)/);
                        if (m) {
                            if (m[1]) { // start pattern match
                                stack.push({ startLine: startLine, tagName: '' }); // empty tagName marks region
                            }
                            else {
                                var i = stack.length - 1;
                                while (i >= 0 && stack[i].tagName.length) {
                                    i--;
                                }
                                if (i >= 0) {
                                    var stackElement = stack[i];
                                    stack.length = i;
                                    var endLine = startLine;
                                    startLine = stackElement.startLine;
                                    if (endLine > startLine && prevStart !== startLine) {
                                        addRange({ startLine: startLine, endLine: endLine, kind: htmlLanguageTypes_1.FoldingRangeKind.Region });
                                    }
                                }
                            }
                        }
                        else {
                            var endLine = document.positionAt(scanner.getTokenOffset() + scanner.getTokenLength()).line;
                            if (startLine < endLine) {
                                addRange({ startLine: startLine, endLine: endLine, kind: htmlLanguageTypes_1.FoldingRangeKind.Comment });
                            }
                        }
                        break;
                    }
                }
                token = scanner.scan();
            }
            var rangeLimit = context && context.rangeLimit || Number.MAX_VALUE;
            if (ranges.length > rangeLimit) {
                return this.limitRanges(ranges, rangeLimit);
            }
            return ranges;
        };
        return HTMLFolding;
    }());
    exports.HTMLFolding = HTMLFolding;
});
