"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.cppExtractor = exports.cExtractor = void 0;
exports.normalizeCppReturnType = normalizeCppReturnType;
const tree_sitter_helpers_1 = require("../tree-sitter-helpers");
/**
 * Find the function NAME's `qualified_identifier` (`Foo::bar`) inside a
 * declarator, skipping the `parameter_list` — a parameter with a qualified type
 * (`const std::string& x`) must NOT be mistaken for the method name. Without the
 * skip, a plain free function `std::string TableFileName(const std::string&...)`
 * was named `string` (from the parameter type), so calls to it never resolved
 * and its file looked like nothing depended on it.
 */
function findDeclaratorQualifiedId(declarator) {
    const queue = [declarator];
    while (queue.length > 0) {
        const current = queue.shift();
        if (current.type === 'qualified_identifier')
            return current;
        for (let i = 0; i < current.namedChildCount; i++) {
            const child = current.namedChild(i);
            // Don't descend into parameters or the trailing return type — their types
            // (`const std::string&`, `-> std::string`) aren't the function name.
            if (child && child.type !== 'parameter_list' && child.type !== 'trailing_return_type') {
                queue.push(child);
            }
        }
    }
    return undefined;
}
function extractCppQualifiedMethodName(node, source) {
    const declarator = (0, tree_sitter_helpers_1.getChildByField)(node, 'declarator');
    if (!declarator)
        return undefined;
    const qid = findDeclaratorQualifiedId(declarator);
    if (!qid)
        return undefined;
    const parts = (0, tree_sitter_helpers_1.getNodeText)(qid, source).trim().split('::').filter(Boolean);
    return parts[parts.length - 1];
}
function extractCppReceiverType(node, source) {
    const declarator = (0, tree_sitter_helpers_1.getChildByField)(node, 'declarator');
    if (!declarator)
        return undefined;
    const qid = findDeclaratorQualifiedId(declarator);
    if (!qid)
        return undefined;
    const parts = (0, tree_sitter_helpers_1.getNodeText)(qid, source).trim().split('::').filter(Boolean);
    return parts.length > 1 ? parts.slice(0, -1).join('::') : undefined;
}
/**
 * Built-in / non-class return types that can never be a method receiver. We
 * store no `returnType` for these so resolution never tries to resolve a method
 * on `void` / `int` / etc.
 */
const CPP_NON_CLASS_RETURN = new Set([
    'void', 'bool', 'char', 'short', 'int', 'long', 'float', 'double', 'unsigned',
    'signed', 'size_t', 'ssize_t', 'auto', 'wchar_t', 'char8_t', 'char16_t',
    'char32_t', 'int8_t', 'int16_t', 'int32_t', 'int64_t', 'uint8_t', 'uint16_t',
    'uint32_t', 'uint64_t', 'intptr_t', 'uintptr_t', 'nullptr_t',
]);
/**
 * Normalize a C++ return type to the bare class name a method could be called
 * on. Unwraps smart-pointer / optional wrappers to their element type
 * (`std::unique_ptr<Widget>` → `Widget`) so a factory's `->method()` resolves on
 * the pointee. Strips cv-qualifiers, `&`/`*`, namespace qualifiers, and other
 * template args. Returns undefined for primitives / void / `auto` / empty.
 */
function normalizeCppReturnType(raw) {
    let t = raw.trim();
    if (!t)
        return undefined;
    // Unwrap smart pointers / optional to their pointee (the thing you call `->` on).
    const wrapper = t.match(/\b(?:std\s*::\s*)?(?:unique_ptr|shared_ptr|weak_ptr|optional)\s*<\s*([^,>]+?)\s*>/);
    if (wrapper && wrapper[1])
        t = wrapper[1];
    t = t
        .replace(/\b(?:const|volatile|typename|struct|class|enum)\b/g, ' ')
        .replace(/<[^>]*>/g, ' ')
        .replace(/[*&]+/g, ' ')
        .replace(/\s+/g, ' ')
        .trim();
    if (!t)
        return undefined;
    const last = t.split('::').filter(Boolean).pop();
    if (!last)
        return undefined;
    if (CPP_NON_CLASS_RETURN.has(last))
        return undefined;
    if (!/^[A-Za-z_]\w*$/.test(last))
        return undefined;
    return last;
}
/**
 * A function/method's return type lives in the `function_definition`'s `type`
 * field (`Metrics& Metrics::instance()` → `Metrics`). Constructors, destructors,
 * and conversion operators have no `type` field → undefined.
 */
function extractCppReturnType(node, source) {
    const typeNode = (0, tree_sitter_helpers_1.getChildByField)(node, 'type');
    if (!typeNode)
        return undefined;
    return normalizeCppReturnType((0, tree_sitter_helpers_1.getNodeText)(typeNode, source));
}
exports.cExtractor = {
    functionTypes: ['function_definition'],
    classTypes: [],
    methodTypes: [],
    interfaceTypes: [],
    structTypes: ['struct_specifier'],
    enumTypes: ['enum_specifier'],
    enumMemberTypes: ['enumerator'],
    typeAliasTypes: ['type_definition'], // typedef
    importTypes: ['preproc_include'],
    callTypes: ['call_expression'],
    variableTypes: ['declaration'],
    nameField: 'declarator',
    bodyField: 'body',
    paramsField: 'parameters',
    // A `const`/`static const` file-scope declaration carries a `type_qualifier`
    // child reading "const" — extract those as `constant`, plain globals as
    // `variable`.
    isConst: (node) => node.namedChildren.some((c) => c.type === 'type_qualifier' && c.text === 'const'),
    getReturnType: extractCppReturnType,
    resolveTypeAliasKind: (node, _source) => {
        // C typedef: `typedef enum { ... } name;` or `typedef struct { ... } name;`
        // The inner enum_specifier/struct_specifier is anonymous, but we want the typedef name
        // to become the enum/struct node name.
        for (let i = 0; i < node.namedChildCount; i++) {
            const child = node.namedChild(i);
            if (!child)
                continue;
            if (child.type === 'enum_specifier' && (0, tree_sitter_helpers_1.getChildByField)(child, 'body'))
                return 'enum';
            if (child.type === 'struct_specifier' && (0, tree_sitter_helpers_1.getChildByField)(child, 'body'))
                return 'struct';
        }
        return undefined;
    },
    extractImport: (node, source) => {
        const importText = source.substring(node.startIndex, node.endIndex).trim();
        // C includes: #include <stdio.h>, #include "myheader.h"
        const systemLib = node.namedChildren.find((c) => c.type === 'system_lib_string');
        if (systemLib) {
            return { moduleName: (0, tree_sitter_helpers_1.getNodeText)(systemLib, source).replace(/^<|>$/g, ''), signature: importText };
        }
        const stringLiteral = node.namedChildren.find((c) => c.type === 'string_literal');
        if (stringLiteral) {
            const stringContent = stringLiteral.namedChildren.find((c) => c.type === 'string_content');
            if (stringContent) {
                return { moduleName: (0, tree_sitter_helpers_1.getNodeText)(stringContent, source), signature: importText };
            }
        }
        return null;
    },
};
exports.cppExtractor = {
    functionTypes: ['function_definition'],
    classTypes: ['class_specifier'],
    methodTypes: ['function_definition'],
    interfaceTypes: [],
    structTypes: ['struct_specifier'],
    enumTypes: ['enum_specifier'],
    enumMemberTypes: ['enumerator'],
    typeAliasTypes: ['type_definition', 'alias_declaration'], // typedef and using
    importTypes: ['preproc_include'],
    callTypes: ['call_expression'],
    variableTypes: ['declaration'],
    nameField: 'declarator',
    bodyField: 'body',
    paramsField: 'parameters',
    resolveName: extractCppQualifiedMethodName,
    getReceiverType: extractCppReceiverType,
    getReturnType: extractCppReturnType,
    getVisibility: (node) => {
        // Check for access specifier in parent
        const parent = node.parent;
        if (parent) {
            for (let i = 0; i < parent.childCount; i++) {
                const child = parent.child(i);
                if (child?.type === 'access_specifier') {
                    const text = child.text;
                    if (text.includes('public'))
                        return 'public';
                    if (text.includes('private'))
                        return 'private';
                    if (text.includes('protected'))
                        return 'protected';
                }
            }
        }
        return undefined;
    },
    resolveTypeAliasKind: (node, _source) => {
        // C++ typedef: `typedef enum { ... } name;` or `typedef struct { ... } name;`
        for (let i = 0; i < node.namedChildCount; i++) {
            const child = node.namedChild(i);
            if (!child)
                continue;
            if (child.type === 'enum_specifier' && (0, tree_sitter_helpers_1.getChildByField)(child, 'body'))
                return 'enum';
            if (child.type === 'struct_specifier' && (0, tree_sitter_helpers_1.getChildByField)(child, 'body'))
                return 'struct';
        }
        return undefined;
    },
    isMisparsedFunction: (name) => {
        // C++ macros like NLOHMANN_JSON_NAMESPACE_BEGIN cause tree-sitter to misparse
        // namespace blocks as function_definitions (e.g. name = "namespace detail").
        // Also filter C++ keywords that tree-sitter occasionally misinterprets as
        // function/method names (e.g. switch statements inside macro-confused scopes).
        if (name.startsWith('namespace'))
            return true;
        const cppKeywords = ['switch', 'if', 'for', 'while', 'do', 'case', 'return'];
        return cppKeywords.includes(name);
    },
    extractImport: (node, source) => {
        const importText = source.substring(node.startIndex, node.endIndex).trim();
        // C++ includes: #include <iostream>, #include "myheader.h"
        const systemLib = node.namedChildren.find((c) => c.type === 'system_lib_string');
        if (systemLib) {
            return { moduleName: (0, tree_sitter_helpers_1.getNodeText)(systemLib, source).replace(/^<|>$/g, ''), signature: importText };
        }
        const stringLiteral = node.namedChildren.find((c) => c.type === 'string_literal');
        if (stringLiteral) {
            const stringContent = stringLiteral.namedChildren.find((c) => c.type === 'string_content');
            if (stringContent) {
                return { moduleName: (0, tree_sitter_helpers_1.getNodeText)(stringContent, source), signature: importText };
            }
        }
        return null;
    },
};
//# sourceMappingURL=c-cpp.js.map