import os
import re
import subprocess
import random
import string
import sys

# ========== 配置区域 ==========
# Doxygen 源码根目录（包含源码的路径）
SOURCE_ROOT = './'  # 你放源码的路径（比如克隆的doxygen源码根目录）

# 编译数据库路径（compile_commands.json所在目录）
BUILD_PATH = './build'

# clang-rename 可执行文件路径（根据brew或自定义安装路径调整）
CLANG_RENAME_PATH = '/usr/local/opt/llvm/bin/clang-rename'

# 支持的源码文件后缀
SOURCE_EXTENSIONS = ['.cpp', '.c', '.h', '.hpp', '.cc']

# 是否实际执行clang-rename（True执行，False只打印模拟）
EXECUTE_RENAME = True

# ======== 混淆名生成 ========
def generate_random_name(length=8):
    # 生成随机字母数字串，比如 A1b2C3d4
    letters_and_digits = string.ascii_letters + string.digits
    return ''.join(random.choice(letters_and_digits) for _ in range(length))

# ======== 扫描源码符号（示例仅提取函数和类名）========
def scan_symbols(source_root):
    symbols = set()
    function_pattern = re.compile(r'\b(?:class|struct|void|int|bool|char|float|double|unsigned|static|virtual|inline|constexpr|auto)\s+([a-zA-Z_]\w*)\s*\(')
    class_pattern = re.compile(r'\b(class|struct)\s+([a-zA-Z_]\w*)')
    variable_pattern = re.compile(r'\b(?:int|bool|char|float|double|unsigned|auto|static|constexpr)\s+([a-zA-Z_]\w*)\b')

    for root, dirs, files in os.walk(source_root):
        for file in files:
            if any(file.endswith(ext) for ext in SOURCE_EXTENSIONS):
                path = os.path.join(root, file)
                try:
                    with open(path, 'r', encoding='utf-8', errors='ignore') as f:
                        content = f.read()

                    # 提取类名
                    for match in class_pattern.finditer(content):
                        symbols.add(match.group(2))

                    # 提取函数名
                    for match in function_pattern.finditer(content):
                        symbols.add(match.group(1))

                    # 提取变量名（谨慎，容易误报）
                    # for match in variable_pattern.finditer(content):
                    #     symbols.add(match.group(1))

                except Exception as e:
                    print(f"读取文件失败 {path}: {e}", file=sys.stderr)

    # 过滤 C++ 关键字和常见函数名（简单示例）
    keywords = {'if','for','while','switch','return','else','class','struct','static','void','int','bool','char','float','double','unsigned','auto','constexpr','inline','virtual','main'}
    symbols = {s for s in symbols if s not in keywords and len(s) > 2}

    return symbols

# ======== 调用 clang-rename 重命名 ========
def run_clang_rename(old_name, new_name):
    print(f'[*] Renaming {old_name} -> {new_name}')
    if not EXECUTE_RENAME:
        print(f'(模拟) clang-rename -p {BUILD_PATH} -old-name={old_name} -new-name={new_name}')
        return

    try:
        subprocess.run([
            CLANG_RENAME_PATH,
            f'-p={BUILD_PATH}',
            f'-old-name={old_name}',
            f'-new-name={new_name}'
        ], check=True)
    except subprocess.CalledProcessError as e:
        print(f'[错误] 重命名 {old_name} 到 {new_name} 失败: {e}', file=sys.stderr)

def main():
    print('[*] 扫描源码符号...')
    symbols = scan_symbols(SOURCE_ROOT)
    print(f'[+] 共发现 {len(symbols)} 个符号')

    # 手动排除或者白名单（防止混淆关键API），可自行扩展
    exclude_symbols = {
        'main', 'printf', 'fprintf', 'exit',
        'Doxygen', 'Config', 'Parser', 'run', 'Error',
        # 可以加入更多Doxygen项目的核心接口名
    }

    symbols = [s for s in symbols if s not in exclude_symbols]

    print(f'[+] 过滤后准备混淆 {len(symbols)} 个符号')

    # 构建映射：旧名 -> 新名
    rename_map = {}

    for s in symbols:
        new_name = generate_random_name(10)
        rename_map[s] = new_name

    # 逐个重命名
    for old_name, new_name in rename_map.items():
        run_clang_rename(old_name, new_name)

    print('[*] 混淆完成')

if __name__ == '__main__':
    main()
