/*---------------------------------------------------------------------------------------------
 *  Copyright (c) Microsoft Corporation. All rights reserved.
 *  Licensed under the MIT License. See License.txt in the project root for license information.
 *--------------------------------------------------------------------------------------------*/
import { FoldingRangeKind } from 'vscode-languageserver-types';
import { TokenType } from '../htmlLanguageTypes';
import { createScanner } from '../parser/htmlScanner';
import { isVoidElement } from '../languageFacts/fact';
function limitRanges(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;
}
export function getFoldingRanges(document, context) {
    var scanner = 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 !== TokenType.EOS) {
        switch (token) {
            case TokenType.StartTag: {
                var tagName = scanner.getTokenText();
                var startLine = document.positionAt(scanner.getTokenOffset()).line;
                stack.push({ startLine: startLine, tagName: tagName });
                lastTagName = tagName;
                break;
            }
            case TokenType.EndTag: {
                lastTagName = scanner.getTokenText();
                break;
            }
            case TokenType.StartTagClose:
                if (!lastTagName || !isVoidElement(lastTagName)) {
                    break;
                }
            // fallthrough
            case TokenType.EndTagClose:
            case 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 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: FoldingRangeKind.Region });
                            }
                        }
                    }
                }
                else {
                    var endLine = document.positionAt(scanner.getTokenOffset() + scanner.getTokenLength()).line;
                    if (startLine < endLine) {
                        addRange({ startLine: startLine, endLine: endLine, kind: FoldingRangeKind.Comment });
                    }
                }
                break;
            }
        }
        token = scanner.scan();
    }
    var rangeLimit = context && context.rangeLimit || Number.MAX_VALUE;
    if (ranges.length > rangeLimit) {
        return limitRanges(ranges, rangeLimit);
    }
    return ranges;
}
