#!/bin/bash
# agent_sdk 联合编译脚本（芯祥联科技 - Agent SNMP协议栈）
# 适配目录：D:\project_snmp\snmp_code\code\agent_sdk
# 功能：编译agent_sdk开源代码 + 链接lib目录闭源库，生成多平台目标文件
#!/usr/bin/env bash
# agent_sdk 编译脚本（修复MinGW下启动/执行异常 + 中文乱码）
# 适配MINGW64环境，兼容set -euo pipefail严格模式

# ==================== 最小化新增：MinGW编码环境配置 ====================
if [[ "$(uname -s)" == *"MINGW"* ]]; then
    chcp 936 >/dev/null 2>&1  # 设置Windows控制台为GBK编码
    export LC_ALL=zh_CN.GBK   # 适配MinGW环境编码
fi

# 关闭严格模式的“未定义变量”检测（MinGW下兼容），保留错误退出
set -eo pipefail
# set -x  # 调试用，可注释

# ==================== 【强制启动提示（优先执行）】 ====================
echo -e "\033[31m====================================\033[0m" >&2
echo -e "\033[31m[AGENT SDK 编译脚本] 启动中...\033[0m" >&2
echo -e "\033[31m====================================\033[0m" >&2
sleep 0.5

# ==================== 【核心配置】 ====================
OPEN_SRC_DIRS=(
    "./comm"
    "./platform"
    "./tps"
    "./trans"
    "./usermib"
)
INC_DIRS=(
    "./inc"          
    "./comm"         
    "./platform"     
    "./tps"          
    "./trans"        
    "./usermib"      
)
SDK_LIB_ROOT="./lib"
OUTPUT_ROOT="./bin"
TARGET_BASE_NAME="xxl_agent_snmp_demo"
STM32_CPU="cortex-m4"
STM32_LINK_SCRIPT="./platform/stm32/linker.ld"
STM32_LD_FLAGS="-nostdlib -lc -lm -lnosys -Wl,--gc-sections"

# ==================== 【全局变量（显式初始化，避免未定义）】 ====================
OS_TYPE=""
CC=""
SDK_LIB_FILE=""
TARGET_NAME=""
BUILD_PLATFORM="all"  # 默认值
ERROR_LOG="./compile_error.log"
SCRIPT_DIR=$(cd "$(dirname "$0")" || exit 1; pwd)

# ==================== 【工具函数】 ====================
show_help() {
    echo -e "\033[32m==== Agent SNMP SDK 编译脚本（MinGW64） ====\033[0m" >&2
    echo "用法：$0 [-p|--platform <平台>] [-h|--help]" >&2
    echo "支持平台：linux/windows/stm32/all" >&2
    echo "示例：./build_agent.sh -p linux" >&2
    echo "示例：./build_agent.sh -p windows" >&2
    exit 0
}

# 校验Agent SDK目录结构
check_agent_dir() {
    echo -e "\033[32m[INFO] 校验Agent SDK目录结构...\033[0m" >&2
    local required_dirs=("comm" "inc" "lib")
    for dir in "${required_dirs[@]}"; do
        if [ ! -d "${SCRIPT_DIR}/${dir}" ]; then
            echo -e "\033[31m[ERROR] 缺少核心目录：${SCRIPT_DIR}/${dir}\033[0m" >&2
            exit 1
        fi
    done
    echo -e "\033[32m[INFO] ✅ 目录结构校验通过\033[0m" >&2
}

# 检查编译依赖
check_deps() {
    local platform="$1"
    echo -e "\033[32m[INFO] 检查${platform}平台依赖...\033[0m" >&2
    case "${platform}" in
        linux)
            if ! command -v gcc &>/dev/null; then
                echo -e "\033[31m[ERROR] MinGW下未找到gcc！请确认MSYS2安装了mingw-w64-x86_64-gcc\033[0m" >&2
                exit 1
            fi
            ;;
        windows)
            if ! command -v gcc &>/dev/null; then
                echo -e "\033[31m[ERROR] MinGW下未找到gcc！\033[0m" >&2
                exit 1
            fi
            ;;
        stm32)
            if ! command -v arm-none-eabi-gcc &>/dev/null; then
                echo -e "\033[31m[ERROR] 未找到ARM交叉编译器！\033[0m" >&2
                exit 1
            fi
            ;;
    esac
    echo -e "\033[32m[INFO] ✅ ${platform}依赖校验通过\033[0m" >&2
}

# 解析命令行参数（修复MinGW下参数解析异常）
parse_args() {
    echo -e "\033[32m[INFO] 解析命令行参数...\033[0m" >&2
    while [[ $# -gt 0 ]]; do
        case "$1" in
            -p|--platform) 
                BUILD_PLATFORM="$2"
                shift 2 
                ;;
            -h|--help) 
                show_help 
                ;;
            *) 
                echo -e "\033[31m[ERROR] 无效参数：$1\033[0m" >&2
                show_help 
                ;;
        esac
    done
    if [[ "${BUILD_PLATFORM}" != "linux" && "${BUILD_PLATFORM}" != "windows" && "${BUILD_PLATFORM}" != "stm32" && "${BUILD_PLATFORM}" != "all" ]]; then
        echo -e "\033[31m[ERROR] 无效平台！仅支持linux/windows/stm32/all\033[0m" >&2
        exit 1
    fi
    echo -e "\033[32m[INFO] ✅ 目标编译平台：${BUILD_PLATFORM}\033[0m" >&2
}

# 初始化平台编译配置
init_platform() {
    local platform="$1"
    rm -f "${ERROR_LOG}"
    echo -e "\033[32m[INFO] 初始化${platform}平台配置...\033[0m" >&2

    case "${platform}" in
        linux)
            OS_TYPE="Linux"
            CC="gcc"
            SDK_LIB_FILE="${SDK_LIB_ROOT}/linux/lib_agent_sdk_linux.a"
            TARGET_NAME="${TARGET_BASE_NAME}_${platform}"
            OUTPUT_DIR="${OUTPUT_ROOT}/linux"
            # 新增通用宏定义：SNMPV3_EN=0、SNMPV3_TRIAL=1、USE_XXL_CRYPTO
            CFLAGS="-O2 -std=c99 -DLINUX -DSNMPV3_EN=0 -DSNMPV3_TRIAL=1 -DUSE_XXL_CRYPTO -Wall -Wno-error -fPIC -include stdio.h -include stdlib.h"
            LDFLAGS=""
            ;;
        windows)
            OS_TYPE="Windows"
            CC="gcc"
            SDK_LIB_FILE="${SDK_LIB_ROOT}/windows/lib_agent_sdk_windows.lib"
            TARGET_NAME="${TARGET_BASE_NAME}_${platform}.exe"
            OUTPUT_DIR="${OUTPUT_ROOT}/windows"
            # 关键修改：
            # 1. -Dmain=WinMain：映射main到WinMain，解决入口函数未定义
            # 2. -Wno-lto-type-mismatch：关闭LTO类型不匹配警告
            # 3. 保留原有宏定义和__NOP兼容
            # 关键修改：新增字符编码参数解决中文乱码
            CFLAGS="-O2 -std=c99 -DWIN32 -D_WIN32 -D__NOP\(\)=\(\) -DSNMPV3_EN=0 -DSNMPV3_TRIAL=1 -DUSE_XXL_CRYPTO -DWIN_AGENT_APP -Dmain=WinMain -Wno-lto-type-mismatch -Wall -Wno-error -include stdio.h -include stdlib.h -finput-charset=UTF-8 -fexec-charset=GBK"
            # 关键修改：新增-lgdi32 -luser32 补充Windows基础库，避免WinMain依赖缺失
            LDFLAGS="-lws2_32 -lgdi32 -luser32 -mconsole"
            ;;
        stm32)
            OS_TYPE="STM32"
            CC="arm-none-eabi-gcc"
            SDK_LIB_FILE="${SDK_LIB_ROOT}/stm32/lib_agent_sdk_stm32.a"
            TARGET_NAME="${TARGET_BASE_NAME}_${platform}.elf"
            OUTPUT_DIR="${OUTPUT_ROOT}/stm32"
            # 新增通用宏定义：SNMPV3_EN=0、SNMPV3_TRIAL=1、USE_XXL_CRYPTO
            CFLAGS="-O2 -std=c99 -DSTM32 -DSNMPV3_EN=0 -DSNMPV3_TRIAL=1 -DUSE_XXL_CRYPTO -mcpu=${STM32_CPU} -mthumb -ffunction-sections -fdata-sections -Wall -Wno-error -include stdio.h -include stdlib.h"
            LDFLAGS="-mcpu=${STM32_CPU} -mthumb -T${STM32_LINK_SCRIPT} ${STM32_LD_FLAGS}"
            ;;
    esac

    # 创建输出目录（兼容MinGW路径）
    mkdir -p "${OUTPUT_DIR}" || {
        echo -e "\033[31m[ERROR] 创建${platform}输出目录失败！\033[0m" >&2
        exit 1
    }

    # 检查SDK库文件
    if [ ! -f "${SDK_LIB_FILE}" ]; then
        echo -e "\033[31m[ERROR] ${platform}库文件不存在：${SDK_LIB_FILE}\033[0m" >&2
        echo -e "\033[33m[提示] 请将库文件放入对应目录！\033[0m" >&2
        exit 1
    fi

    # 生成头文件参数（兼容MinGW路径）
    INC_PARAMS=""
    for dir in "${INC_DIRS[@]}"; do
        local abs_dir="${SCRIPT_DIR}/${dir}"
        [ -d "${abs_dir}" ] && INC_PARAMS+=" -I\"${abs_dir}\""
    done

    # 导出变量（避免子shell未定义）
    export OS_TYPE CC SDK_LIB_FILE TARGET_NAME OUTPUT_DIR CFLAGS LDFLAGS INC_PARAMS
    echo -e "\033[32m[INFO] ✅ ${platform}配置初始化完成\033[0m" >&2
}

# 收集所有.c文件（兼容MinGW find命令）
collect_c_files() {
    echo -e "\033[32m[INFO] 收集开源.c文件...\033[0m" >&2
    local c_files=()
    for dir in "${OPEN_SRC_DIRS[@]}"; do
        local abs_dir="${SCRIPT_DIR}/${dir}"
        [ ! -d "${abs_dir}" ] && {
            echo -e "\033[33m[WARN] 跳过不存在的目录：${abs_dir}\033[0m" >&2
            continue
        }
        # 兼容MinGW的find命令（不使用-print0）
        while IFS= read -r file; do
            [ -f "${file}" ] && c_files+=("${file}")
        done < <(find "${abs_dir}" -name "*.c" -type f)
    done

    if [ ${#c_files[@]} -eq 0 ]; then
        echo -e "\033[31m[ERROR] 未找到任何.c文件！\033[0m" >&2
        exit 1
    fi

    echo -e "\033[32m[INFO] ✅ 找到${#c_files[@]}个.c文件\033[0m" >&2
    printf "%s\n" "${c_files[@]}"
}

# 编译单个平台
build_platform() {
    local platform="$1"
    echo -e "\033[32m\n====================================\033[0m" >&2
    echo -e "\033[32m[INFO] 开始编译${platform}平台...\033[0m" >&2
    echo -e "\033[32m====================================\033[0m" >&2

    # 前置校验
    check_deps "${platform}"
    init_platform "${platform}"

    # 收集C文件
    local c_files=($(collect_c_files))

    # 编译.c → .o
    local obj_files=""
    for c_file in "${c_files[@]}"; do
        local obj_basename=$(basename "${c_file}" .c)
        local obj_file="${OUTPUT_DIR}/${obj_basename}.o"
        echo -e "\033[32m[INFO] 编译：${c_file} → ${obj_file}\033[0m" >&2
        # 兼容MinGW的路径引号
        compile_cmd="${CC} ${CFLAGS} ${INC_PARAMS} -c \"${c_file}\" -o \"${obj_file}\""
        
        # 用bash -c替代eval，修复MinGW下引号解析问题
        if ! bash -c "${compile_cmd}" 2>"${ERROR_LOG}"; then
            echo -e "\033[31m[ERROR] 编译失败：${c_file}\033[0m" >&2
            echo -e "\033[31m[错误详情]：$(cat ${ERROR_LOG})\033[0m" >&2
            rm -f "${ERROR_LOG}"
            exit 1
        fi
        obj_files+=" \"${obj_file}\""
    done
    rm -f "${ERROR_LOG}"

    # 链接生成目标文件
    local target_path="${OUTPUT_DIR}/${TARGET_NAME}"
    echo -e "\033[32m[INFO] 链接生成产物：${target_path}\033[0m" >&2
    link_cmd="${CC} ${obj_files} \"${SDK_LIB_FILE}\" ${LDFLAGS} -o \"${target_path}\""
    
    # 用bash -c替代eval，修复MinGW下引号解析问题
    if ! bash -c "${link_cmd}" 2>"${ERROR_LOG}"; then
        echo -e "\033[31m[ERROR] ${platform}平台链接失败！\033[0m" >&2
        echo -e "\033[31m[错误详情]：$(cat ${ERROR_LOG})\033[0m" >&2
        rm -f "${ERROR_LOG}"
        exit 1
    fi

    # 清理临时文件
    echo -e "\033[32m[INFO] 清理临时.o文件...\033[0m" >&2
    # 用bash -c替代eval，修复MinGW下引号解析问题
    bash -c "rm -f ${obj_files}"
    rm -f "${OUTPUT_DIR}"/*.o "${ERROR_LOG}"

    echo -e "\033[32m[INFO] ✅ ${platform}平台编译完成！\033[0m" >&2
    echo -e "\033[32m[INFO] 产物路径：${target_path}\033[0m" >&2
}

# 编译所有平台
build_all() {
    build_platform "linux"
    build_platform "windows"
    build_platform "stm32"
}

# ==================== 【主逻辑（修正执行顺序）】 ====================
main() {
    # 步骤1：打印基础信息
    echo -e "\033[32m[INFO] 脚本工作目录：${SCRIPT_DIR}\033[0m" >&2
    echo -e "\033[32m[INFO] 当前时间：$(date)\033[0m" >&2
    
    # 步骤2：校验目录结构
    check_agent_dir

    # 步骤3：解析参数（移到此处，避免提前退出）
    parse_args "$@"

    # 步骤4：开始编译
    case "${BUILD_PLATFORM}" in
        linux) build_platform "linux" ;;
        windows) build_platform "windows" ;;
        stm32) build_platform "stm32" ;;
        all) build_all ;;
    esac

    # 步骤5：执行完成提示
    echo -e "\033[31m====================================\033[0m" >&2
    echo -e "\033[31m[AGENT SDK 编译脚本] 全部执行完成！\033[0m" >&2
    echo -e "\033[31m====================================\033[0m" >&2
}

# ==================== 【启动入口（必执行）】 ====================
# 强制执行main函数，传递所有参数
main "$@"
