"use strict";
/*---------------------------------------------------------------------------------------------
 *  Copyright (c) Red Hat, Inc. All rights reserved.
 *  Copyright (c) Microsoft Corporation. All rights reserved.
 *  Licensed under the MIT License. See License.txt in the project root for license information.
 *--------------------------------------------------------------------------------------------*/
Object.defineProperty(exports, "__esModule", { value: true });
exports.YAMLDocumentSymbols = void 0;
const vscode_languageserver_types_1 = require("vscode-languageserver-types");
const yaml_documents_1 = require("../parser/yaml-documents");
const yaml_1 = require("yaml");
class YAMLDocumentSymbols {
    constructor(telemetry) {
        this.telemetry = telemetry;
    }
    findDocumentSymbols(document, context = { resultLimit: Number.MAX_VALUE }) {
        let results = [];
        try {
            const doc = yaml_documents_1.yamlDocumentsCache.getYamlDocument(document);
            if (!doc || doc.documents.length === 0) {
                return null;
            }
            for (const yamlDoc of doc.documents) {
                if (yamlDoc.root) {
                    results = results.concat(this.findFlatDocumentSymbols(document, yamlDoc.root, context));
                }
            }
        }
        catch (err) {
            this.telemetry?.sendError('yaml.documentSymbols.error', err);
        }
        return results;
    }
    findHierarchicalDocumentSymbols(document, context = { resultLimit: Number.MAX_VALUE }) {
        let results = [];
        try {
            const doc = yaml_documents_1.yamlDocumentsCache.getYamlDocument(document);
            if (!doc || doc.documents.length === 0) {
                return null;
            }
            for (const yamlDoc of doc.documents) {
                if (yamlDoc.root) {
                    results = results.concat(this.findHierarchicalSymbols(document, yamlDoc.root, context));
                }
            }
        }
        catch (err) {
            this.telemetry?.sendError('yaml.hierarchicalDocumentSymbols.error', err);
        }
        return results;
    }
    findFlatDocumentSymbols(document, root, context) {
        let limit = context.resultLimit || Number.MAX_VALUE;
        const toVisit = [{ node: root, containerName: '' }];
        let nextToVisit = 0;
        let limitExceeded = false;
        const result = [];
        const collectOutlineEntries = (node, containerName) => {
            if (node.type === 'array') {
                node.items.forEach((item) => {
                    if (item) {
                        toVisit.push({ node: item, containerName });
                    }
                });
            }
            else if (node.type === 'object') {
                node.properties.forEach((property) => {
                    const valueNode = property.valueNode;
                    if (!valueNode) {
                        return;
                    }
                    if (limit > 0) {
                        limit--;
                        const location = vscode_languageserver_types_1.Location.create(document.uri, getRange(document, property));
                        const childContainerName = containerName ? containerName + '.' + property.keyNode.value : property.keyNode.value;
                        result.push({
                            name: getKeyLabel(property),
                            kind: getSymbolKind(valueNode.type),
                            location,
                            containerName,
                        });
                        toVisit.push({ node: valueNode, containerName: childContainerName });
                    }
                    else {
                        limitExceeded = true;
                    }
                });
            }
        };
        while (nextToVisit < toVisit.length) {
            const next = toVisit[nextToVisit++];
            collectOutlineEntries(next.node, next.containerName);
        }
        if (limitExceeded) {
            context.onResultLimitExceeded?.(document.uri);
        }
        return result;
    }
    findHierarchicalSymbols(document, root, context) {
        let limit = context.resultLimit || Number.MAX_VALUE;
        const result = [];
        const toVisit = [{ node: root, result }];
        let nextToVisit = 0;
        let limitExceeded = false;
        const collectOutlineEntries = (node, result) => {
            if (node.type === 'array') {
                node.items.forEach((item, index) => {
                    if (!item) {
                        return;
                    }
                    if (limit > 0) {
                        limit--;
                        const range = getRange(document, item);
                        const symbol = {
                            name: String(index),
                            kind: getSymbolKind(item.type),
                            range,
                            selectionRange: range,
                            children: [],
                        };
                        result.push(symbol);
                        toVisit.push({ result: symbol.children, node: item });
                    }
                    else {
                        limitExceeded = true;
                    }
                });
            }
            else if (node.type === 'object') {
                node.properties.forEach((property) => {
                    const valueNode = property.valueNode;
                    if (!valueNode) {
                        return;
                    }
                    if (limit > 0) {
                        limit--;
                        const children = [];
                        const symbol = {
                            name: getKeyLabel(property),
                            kind: getSymbolKind(valueNode.type),
                            range: getRange(document, property),
                            selectionRange: getRange(document, property.keyNode),
                            children,
                            detail: getDetail(valueNode),
                        };
                        result.push(symbol);
                        toVisit.push({ result: children, node: valueNode });
                    }
                    else {
                        limitExceeded = true;
                    }
                });
            }
        };
        while (nextToVisit < toVisit.length) {
            const next = toVisit[nextToVisit++];
            collectOutlineEntries(next.node, next.result);
        }
        if (limitExceeded) {
            context.onResultLimitExceeded?.(document.uri);
        }
        return result;
    }
}
exports.YAMLDocumentSymbols = YAMLDocumentSymbols;
function getSymbolKind(nodeType) {
    switch (nodeType) {
        case 'object':
            return vscode_languageserver_types_1.SymbolKind.Module;
        case 'string':
            return vscode_languageserver_types_1.SymbolKind.String;
        case 'number':
            return vscode_languageserver_types_1.SymbolKind.Number;
        case 'array':
            return vscode_languageserver_types_1.SymbolKind.Array;
        case 'boolean':
            return vscode_languageserver_types_1.SymbolKind.Boolean;
        default:
            return vscode_languageserver_types_1.SymbolKind.Variable;
    }
}
function getKeyLabel(property) {
    const keyNode = property.keyNode.internalNode;
    let name;
    if ((0, yaml_1.isMap)(keyNode)) {
        name = '{}';
    }
    else if ((0, yaml_1.isSeq)(keyNode)) {
        name = '[]';
    }
    else if ('source' in keyNode && typeof keyNode.source === 'string') {
        name = keyNode.source;
    }
    else {
        name = property.keyNode.value;
    }
    if (name) {
        name = name.replace(/[\n]/g, '↵');
    }
    if (name && name.trim()) {
        return name;
    }
    return `"${name}"`;
}
function getDetail(node) {
    if (!node) {
        return undefined;
    }
    if (node.type === 'boolean' || node.type === 'number' || node.type === 'null' || node.type === 'string') {
        return String(node.value);
    }
    if (node.type === 'array') {
        return node.children.length ? undefined : '[]';
    }
    if (node.type === 'object') {
        return node.children.length ? undefined : '{}';
    }
    return undefined;
}
function getRange(document, node) {
    return vscode_languageserver_types_1.Range.create(document.positionAt(node.offset), document.positionAt(node.offset + node.length));
}
//# sourceMappingURL=documentSymbols.js.map