import argparse
import os, sys
import re
from pycparser import c_parser, c_ast

# 定义默认的标准库头文件搜索路径，根据实际情况修改
DEFAULT_STANDARD_LIBRARY_PATHS = [
    r"C:\Program Files (x86)\Microsoft Visual Studio\2022\Community\VC\Tools\MSVC\14.34.31933\include",
    r"C:\Program Files (x86)\Windows Kits\10\Include\10.0.22621.0\ucrt",
    r"C:\Program Files (x86)\Windows Kits\10\Include\10.0.22621.0\um",
    r"C:\Program Files (x86)\Windows Kits\10\Include\10.0.22621.0\shared",
    r"C:\Program Files (x86)\Windows Kits\10\Include\10.0.22621.0\codeanalysis"
]

# 定义默认需要忽略的头文件列表
DEFAULT_IGNORE_HEADERS = [
    "CodeAnalysis/sourceannotations.h"
]

def process_define_directives(c_code):
    """处理 #define 指令，提取宏定义并替换代码中的宏"""
    define_pattern = re.compile(r'^\s*#define\s+(\w+)\s+([^\n]+)$', re.MULTILINE)
    defines = {}
    for match in define_pattern.finditer(c_code):
        macro_name = match.group(1)
        macro_value = match.group(2)
        defines[macro_name] = macro_value

    # 替换代码中的宏
    for macro_name, macro_value in defines.items():
        c_code = c_code.replace(macro_name, macro_value)

    # 移除 #define 指令
    c_code = define_pattern.sub('', c_code)
    return c_code

def process_include_directives(c_code, base_dir, standard_library_paths, ignore_headers):
    """处理 #include 指令，读取包含文件内容并插入到代码中"""
    include_pattern = re.compile(r'^\s*#include\s+["<](.*)[">]$', re.MULTILINE)
    while True:
        match = include_pattern.search(c_code)
        if not match:
            break
        include_file = match.group(1)
        if include_file in ignore_headers:
            # 忽略指定的头文件
            c_code = include_pattern.sub('', c_code, count=1)
            continue

        include_path = None
        # 先在当前目录查找
        current_path = os.path.join(base_dir, include_file)
        if os.path.exists(current_path):
            include_path = current_path
        else:
            # 在标准库路径中查找
            for lib_path in standard_library_paths:
                lib_include_path = os.path.join(lib_path, include_file)
                if os.path.exists(lib_include_path):
                    include_path = lib_include_path
                    break

        if include_path:
            try:
                with open(include_path, 'r') as f:
                    include_content = f.read()
                start, end = match.span()
                c_code = c_code[:start] + include_content + c_code[end:]
            except FileNotFoundError:
                print(f"未找到包含文件: {include_path}")
                c_code = include_pattern.sub('', c_code, count=1)
        else:
            print(f"未找到包含文件: {include_file}")
            c_code = include_pattern.sub('', c_code, count=1)

    return c_code

def generate_pyi(input_file, output_dir=None, output_name=None, additional_paths=None, additional_ignores=None):
    try:
        # 合并默认和额外的标准库头文件搜索路径
        standard_library_paths = DEFAULT_STANDARD_LIBRARY_PATHS.copy()
        if additional_paths:
            standard_library_paths.extend(additional_paths)

        # 合并默认和额外需要忽略的头文件
        ignore_headers = DEFAULT_IGNORE_HEADERS.copy()
        if additional_ignores:
            ignore_headers.extend(additional_ignores)

        # 读取 C 文件内容
        with open(input_file, 'r') as f:
            c_code = f.read()

        base_dir = os.path.dirname(os.path.abspath(input_file))

        # 处理 #define 指令
        c_code = process_define_directives(c_code)

        # 处理 #include 指令
        c_code = process_include_directives(c_code, base_dir, standard_library_paths, ignore_headers)

        # 创建 C 解析器
        parser = c_parser.CParser()
        ast = parser.parse(c_code)

        # 构建 .pyi 文件内容
        pyi_content = []
        pyi_content.append('from typing import Any\n\n')
        pyi_content.append('C: Any\n\n')

        # 遍历 AST 提取函数声明
        for node in ast.ext:
            if isinstance(node, c_ast.FuncDecl):
                function_name = node.type.declname
                params = []
                if node.args:
                    for param in node.args.params:
                        if isinstance(param, c_ast.Decl):
                            param_name = param.name
                            param_type = 'Any'  # 简单处理，假设参数类型为 Any
                            params.append(f'{param_name}: {param_type}')
                args_str = ', '.join(params)
                return_type = 'Any'  # 简单处理，假设返回类型为 Any
                pyi_content.append(f'def {function_name}({args_str}) -> {return_type}: ...\n')

        # 确定输出路径
        if output_dir is None:
            output_dir = os.getcwd()
        if output_name is None:
            base_name = os.path.splitext(os.path.basename(input_file))[0]
            output_name = f'{base_name}.pyi'
        pyi_file_path = os.path.join(output_dir, output_name)

        # 写入 .pyi 文件
        with open(pyi_file_path, 'w') as f:
            f.write(''.join(pyi_content))
        print(f'.pyi 文件已生成: {pyi_file_path}')
    except FileNotFoundError:
        print(f"错误：未找到输入文件 {input_file}")
    except Exception as e:
        print(f"执行过程中出现错误: {e}")
        print('如果出现未找到头文件，建议使用-p增加标准库头文件搜索路径，如果还是找不到，建议使用-q增加需要忽略的头文件')
        sys.exit(1)

def main():
    parser = argparse.ArgumentParser(description='为 C 文件生成 .pyi 文件')
    parser.add_argument('input_file', help='输入的 C 文件路径')
    parser.add_argument('-o', '--output-dir', help='指定输出目录')
    parser.add_argument('-i', '--output-name', help='指定输出文件名')
    parser.add_argument('-p', '--additional-paths', nargs='+', help='增加标准库头文件搜索路径')
    parser.add_argument('-q', '--additional-ignores', nargs='+', help='增加需要忽略的头文件')
    args = parser.parse_args()

    generate_pyi(args.input_file, args.output_dir, args.output_name, args.additional_paths, args.additional_ignores)

if __name__ == "__main__":
    main()