'use strict';

var syntaxTree = require('../syntax-tree.js');
var stream = require('./stream.js');

/******************************************************************************
 * Copyright 2021 TypeFox GmbH
 * This program and the accompanying materials are made available under the
 * terms of the MIT License, which is available in the project root.
 ******************************************************************************/
/**
 * Create a stream of all CST nodes that are directly and indirectly contained in the given root node,
 * including the root node itself.
 */
function streamCst(node) {
    return new stream.TreeStreamImpl(node, element => {
        if (syntaxTree.isCompositeCstNode(element)) {
            return element.content;
        }
        else {
            return [];
        }
    }, { includeRoot: true });
}
/**
 * Determines whether the specified cst node is a child of the specified parent node.
 */
function isChildNode(child, parent) {
    while (child.container) {
        child = child.container;
        if (child === parent) {
            return true;
        }
    }
    return false;
}
function tokenToRange(token) {
    // Chevrotain uses 1-based indices everywhere
    // So we subtract 1 from every value to align with the LSP
    return {
        start: {
            character: token.startColumn - 1,
            line: token.startLine - 1
        },
        end: {
            character: token.endColumn, // endColumn uses the correct index
            line: token.endLine - 1
        }
    };
}
function toDocumentSegment(node) {
    if (!node) {
        return undefined;
    }
    const { offset, end, range } = node;
    return {
        range,
        offset,
        end,
        length: end - offset
    };
}
exports.RangeComparison = void 0;
(function (RangeComparison) {
    RangeComparison[RangeComparison["Before"] = 0] = "Before";
    RangeComparison[RangeComparison["After"] = 1] = "After";
    RangeComparison[RangeComparison["OverlapFront"] = 2] = "OverlapFront";
    RangeComparison[RangeComparison["OverlapBack"] = 3] = "OverlapBack";
    RangeComparison[RangeComparison["Inside"] = 4] = "Inside";
    RangeComparison[RangeComparison["Outside"] = 5] = "Outside";
})(exports.RangeComparison || (exports.RangeComparison = {}));
function compareRange(range, to) {
    if (range.end.line < to.start.line || (range.end.line === to.start.line && range.end.character <= to.start.character)) {
        return exports.RangeComparison.Before;
    }
    else if (range.start.line > to.end.line || (range.start.line === to.end.line && range.start.character >= to.end.character)) {
        return exports.RangeComparison.After;
    }
    const startInside = range.start.line > to.start.line || (range.start.line === to.start.line && range.start.character >= to.start.character);
    const endInside = range.end.line < to.end.line || (range.end.line === to.end.line && range.end.character <= to.end.character);
    if (startInside && endInside) {
        return exports.RangeComparison.Inside;
    }
    else if (startInside) {
        return exports.RangeComparison.OverlapBack;
    }
    else if (endInside) {
        return exports.RangeComparison.OverlapFront;
    }
    else {
        return exports.RangeComparison.Outside;
    }
}
function inRange(range, to) {
    const comparison = compareRange(range, to);
    return comparison > exports.RangeComparison.After;
}
// The \p{L} regex matches any unicode letter character, i.e. characters from non-english alphabets
// Together with \w it matches any kind of character which can commonly appear in IDs
const DefaultNameRegexp = /^[\w\p{L}]$/u;
function findCommentNode(cstNode, commentNames) {
    if (cstNode) {
        const previous = getPreviousNode(cstNode, true);
        if (previous && isCommentNode(previous, commentNames)) {
            return previous;
        }
        if (syntaxTree.isRootCstNode(cstNode)) {
            // Go from the first non-hidden node through all nodes in reverse order
            // We do this to find the comment node which directly precedes the root node
            const endIndex = cstNode.content.findIndex(e => !e.hidden);
            for (let i = endIndex - 1; i >= 0; i--) {
                const child = cstNode.content[i];
                if (isCommentNode(child, commentNames)) {
                    return child;
                }
            }
        }
    }
    return undefined;
}
function isCommentNode(cstNode, commentNames) {
    return syntaxTree.isLeafCstNode(cstNode) && commentNames.includes(cstNode.tokenType.name);
}
function getPreviousNode(node, hidden = true) {
    while (node.container) {
        const parent = node.container;
        let index = parent.content.indexOf(node);
        while (index > 0) {
            index--;
            const previous = parent.content[index];
            if (hidden || !previous.hidden) {
                return previous;
            }
        }
        node = parent;
    }
    return undefined;
}

exports.DefaultNameRegexp = DefaultNameRegexp;
exports.compareRange = compareRange;
exports.findCommentNode = findCommentNode;
exports.getPreviousNode = getPreviousNode;
exports.inRange = inRange;
exports.isChildNode = isChildNode;
exports.isCommentNode = isCommentNode;
exports.streamCst = streamCst;
exports.toDocumentSegment = toDocumentSegment;
exports.tokenToRange = tokenToRange;
