cmake_minimum_required(VERSION 3.21)

# 雷达数据处理动态链接库
# 开发环境已由 Qt/qmake 迁移到 VS Code + CMake。
# 编译器已由 Microsoft Visual C++ Compiler 12.0 (VS2013) 升级为
# Microsoft Visual C++ Compiler 2022（v143 工具集，_MSC_VER 19.4x，x64）。
# 语言标准同步提升为 C++17。
#
# 注意：VS2022 生成器（"Visual Studio 17 2022"）需要 CMake >= 3.21，
#       因此本工程的最低 CMake 版本随之提高；NMake/Ninja 生成器同样满足。
project(data_process_class_dll LANGUAGES CXX)

# 机扫 / 相扫版本已合并为同一份 DLL：构建不再区分扫描体制，也不再定义
# PHASE_SCANNING / MECHANICAL_SCANNING 编译期宏，也没有 RADAR_SCAN_MODE 缓存变量。
# 扫描模式改由运行期配置项的 `[radar_params] scan_mode` 决定（0 相扫 / 1 机扫），
# 随全部雷达工作参数一起由 TOML 配置文件经 parameters_reload() 载入，详见 README 4.2、10。

# ---------------------------------------------------------------------------
# 数据记录调试功能开关（可选编译，详见 docs/data_record_debug.md）
#   AUTO : Debug 构建启用、Release 构建关闭（默认；交付构建不做任何记录）
#   ON   : 强制启用（可用于 Release 排查，需自行承担性能开销）
#   OFF  : 强制关闭
# 启用时新增独立静态库 rdp_record：以 C++20 编译（ormpp 要求）、编译内置的 sqlite3
# amalgamation，并链接进 DLL；源码中的 RDP_RECORD 宏由 RDP_RECORD_ENABLE 控制，关闭时
# 展开为空操作（实参不求值），记录模块完全不参与编译。
# 依赖来源与升级方式见 third_party/ormpp/UPSTREAM.md。
# ---------------------------------------------------------------------------
set(RDP_DATA_RECORD "AUTO" CACHE STRING "数据记录调试功能：AUTO(Debug=ON/Release=OFF) / ON / OFF")
set_property(CACHE RDP_DATA_RECORD PROPERTY STRINGS AUTO ON OFF)

if(RDP_DATA_RECORD STREQUAL "AUTO")
    if(CMAKE_CONFIGURATION_TYPES)
        message(FATAL_ERROR "多配置生成器下 RDP_DATA_RECORD=AUTO 无法判定，请显式传入 ON 或 OFF")
    elseif(CMAKE_BUILD_TYPE STREQUAL "Debug")
        set(RDP_RECORD_ENABLED TRUE)
    else()
        set(RDP_RECORD_ENABLED FALSE)
    endif()
elseif(RDP_DATA_RECORD STREQUAL "ON")
    set(RDP_RECORD_ENABLED TRUE)
elseif(RDP_DATA_RECORD STREQUAL "OFF")
    set(RDP_RECORD_ENABLED FALSE)
else()
    message(FATAL_ERROR "RDP_DATA_RECORD 必须为 AUTO / ON / OFF，当前值：${RDP_DATA_RECORD}")
endif()

if(RDP_RECORD_ENABLED)
    message(STATUS "数据记录调试功能：启用（C++20 静态库 rdp_record + 内置 SQLite）")
else()
    message(STATUS "数据记录调试功能：关闭（不编译记录模块，RDP_RECORD 为空操作）")
endif()

# ---------------------------------------------------------------------------
# 日志编译期级别（spdlog）
#   原 parameters.h 的 RDP_LOG_ENABLE 布尔开关已移除：日志级别改由 spdlog 的
#   SPDLOG_ACTIVE_LEVEL 在编译期控制（低于该级别的 RDP_LOG_* 日志在编译期被
#   剔除、实参不求值，OFF 即等价于原「关闭日志模块」）。本缓存变量把级别映射
#   到 SPDLOG_ACTIVE_LEVEL，并同时用于 data_process_class_dll 与 rdp_record
#   两个目标（两个目标使用同一份 spdlog 头文件配置，必须保持一致）。
#     AUTO : Debug 构建 = TRACE，其余构建 = INFO（默认）
#     TRACE / DEBUG / INFO / WARN / ERROR / CRITICAL : 显式指定最低编译级别
#     OFF  : 编译期关闭全部 RDP_LOG_* 日志（rdp_log::Init() 也不再创建日志文件）
#   修改方式：cmake -DRDP_LOG_LEVEL=WARN ...（或在 CMakeCache 中修改后重新配置）
# ---------------------------------------------------------------------------
set(RDP_LOG_LEVEL "AUTO" CACHE STRING "日志编译期级别：AUTO(Debug=TRACE/其它=INFO) / TRACE / DEBUG / INFO / WARN / ERROR / CRITICAL / OFF")
set_property(CACHE RDP_LOG_LEVEL PROPERTY STRINGS AUTO TRACE DEBUG INFO WARN ERROR CRITICAL OFF)

if(RDP_LOG_LEVEL STREQUAL "AUTO")
    if(CMAKE_CONFIGURATION_TYPES)
        set(RDP_LOG_ACTIVE_LEVEL_DEFS
            "$<$<CONFIG:Debug>:SPDLOG_ACTIVE_LEVEL=SPDLOG_LEVEL_TRACE>"
            "$<$<NOT:$<CONFIG:Debug>>:SPDLOG_ACTIVE_LEVEL=SPDLOG_LEVEL_INFO>")
        set(RDP_LOG_LEVEL_DESC "AUTO（多配置生成器：Debug=TRACE，其它=INFO）")
    elseif(CMAKE_BUILD_TYPE STREQUAL "Debug")
        set(RDP_LOG_ACTIVE_LEVEL_DEFS "SPDLOG_ACTIVE_LEVEL=SPDLOG_LEVEL_TRACE")
        set(RDP_LOG_LEVEL_DESC "AUTO（Debug 构建 → TRACE）")
    else()
        set(RDP_LOG_ACTIVE_LEVEL_DEFS "SPDLOG_ACTIVE_LEVEL=SPDLOG_LEVEL_INFO")
        if(CMAKE_BUILD_TYPE)
            set(RDP_LOG_LEVEL_DESC "AUTO（${CMAKE_BUILD_TYPE} 构建 → INFO）")
        else()
            set(RDP_LOG_LEVEL_DESC "AUTO（未指定构建类型 → INFO）")
        endif()
    endif()
elseif(RDP_LOG_LEVEL STREQUAL "OFF")
    set(RDP_LOG_ACTIVE_LEVEL_DEFS "SPDLOG_ACTIVE_LEVEL=SPDLOG_LEVEL_OFF")
    set(RDP_LOG_LEVEL_DESC "OFF（编译期关闭全部 RDP_LOG_* 日志）")
elseif(RDP_LOG_LEVEL MATCHES "^(TRACE|DEBUG|INFO|WARN|ERROR|CRITICAL)$")
    set(RDP_LOG_ACTIVE_LEVEL_DEFS "SPDLOG_ACTIVE_LEVEL=SPDLOG_LEVEL_${RDP_LOG_LEVEL}")
    set(RDP_LOG_LEVEL_DESC "${RDP_LOG_LEVEL}")
else()
    message(FATAL_ERROR
        "RDP_LOG_LEVEL 必须为 AUTO / TRACE / DEBUG / INFO / WARN / ERROR / CRITICAL / OFF，"
        "当前值：${RDP_LOG_LEVEL}")
endif()
message(STATUS "日志编译期级别：${RDP_LOG_LEVEL_DESC}（映射为 SPDLOG_ACTIVE_LEVEL）")

# ---------------------------------------------------------------------------
# 语言标准：C++17
# ---------------------------------------------------------------------------
# CMAKE_CXX_STANDARD 由 CMake 转换为 MSVC 的 /std:c++17（CMake >= 3.10 起支持）。
# CMAKE_CXX_EXTENSIONS OFF 在 MSVC 下没有对应开关（只影响 GCC/Clang 的 -std=gnu++xx），
# 保留它是为了在换用其它编译器时同样不启用语言扩展。
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_CXX_EXTENSIONS OFF)

# ---------------------------------------------------------------------------
# 编译器约束：必须使用 MSVC 2022（v143，_MSC_VER >= 1930）
# ---------------------------------------------------------------------------
if(NOT MSVC)
    message(FATAL_ERROR "本工程仅支持 MSVC (cl.exe) 编译器，当前编译器：${CMAKE_CXX_COMPILER_ID}")
endif()

if(MSVC_VERSION LESS 1930)
    message(FATAL_ERROR
        "本工程要求 MSVC 2022 (v143) 及以上版本（_MSC_VER >= 1930），"
        "当前 _MSC_VER=${MSVC_VERSION}（编译器：${CMAKE_CXX_COMPILER}）。\n"
        "请先运行 VS2022 的 vcvarsall.bat x64（见 .vscode/scripts/msvc_env.bat）后再配置。")
endif()

message(STATUS "编译器：${CMAKE_CXX_COMPILER}（MSVC _MSC_VER=${MSVC_VERSION}）")
message(STATUS "C++ 标准：C++${CMAKE_CXX_STANDARD}")

# 导出编译命令，便于 VS Code C/C++ 插件 IntelliSense 使用。
# Visual Studio 生成器不支持该选项，因此仅对 Makefile/Ninja 生成器开启。
if(NOT CMAKE_GENERATOR MATCHES "Visual Studio")
    set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
endif()

# ---------------------------------------------------------------------------
# 内置第三方库 spdlog（日志，v1.17.0，见 third_party/spdlog/UPSTREAM.md）
#   以静态库方式编译（SPDLOG_COMPILED_LIB），供 data_process_class_dll 与
#   rdp_record（C++20 静态库，rdp_record.cpp 内也有日志）链接：
#     - PUBLIC 编译定义 SPDLOG_COMPILED_LIB / SPDLOG_ACTIVE_LEVEL 与头文件
#       目录，保证所有使用方（含跨 C++17/C++20 的两目标）配置一致；
#     - SPDLOG_ACTIVE_LEVEL 由上方 RDP_LOG_LEVEL 决定；
#     - 第三方源码以 /w 编译，维持本项目 /W3 零告警约定（见 README 4.5）。
# ---------------------------------------------------------------------------
set(SPDLOG_ROOT "${CMAKE_CURRENT_SOURCE_DIR}/third_party/spdlog")

add_library(spdlog STATIC
    third_party/spdlog/src/spdlog.cpp
    third_party/spdlog/src/async.cpp
    third_party/spdlog/src/cfg.cpp
    third_party/spdlog/src/color_sinks.cpp
    third_party/spdlog/src/file_sinks.cpp
    third_party/spdlog/src/stdout_sinks.cpp
    third_party/spdlog/src/bundled_fmtlib_format.cpp
)
target_include_directories(spdlog PUBLIC ${SPDLOG_ROOT}/include)
target_compile_definitions(spdlog PUBLIC
    SPDLOG_COMPILED_LIB
    ${RDP_LOG_ACTIVE_LEVEL_DEFS}
)
if(MSVC)
    target_compile_options(spdlog PRIVATE /w /utf-8 /Zc:__cplusplus /Zc:wchar_t /FS)
endif()
set_target_properties(spdlog PROPERTIES
    ARCHIVE_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/lib"
)
message(STATUS "第三方日志库：spdlog v1.17.0（静态库，third_party/spdlog）")

set(SOURCES
    data_process_class_dll/rdp_log.cpp
    data_process_class_dll/data_process_class_dll.cpp
    data_process_class_dll/data_process.cpp
    data_process_class_dll/rdp_parameter_loader.cpp
    data_process_class_dll/dot_coh.cpp
    data_process_class_dll/kalman.cpp
    data_process_class_dll/coor_trans.cpp
    data_process_class_dll/track_asso.cpp
    data_process_class_dll/track_init.cpp
    data_process_class_dll/track_index_mangement.cpp
    data_process_class_dll/tas_ctrl.cpp
    data_process_class_dll/track_die.cpp
    data_process_class_dll/dot_coh_tas.cpp
    data_process_class_dll/track_asso_tas.cpp
    data_process_class_dll/track_init_direct_tracking.cpp
    data_process_class_dll/track_die_tas.cpp
)

set(PUBLIC_HEADERS
    data_process_class_dll/data_process_class_dll.h
    data_process_class_dll/data_process_class_dll_global.h
)

add_library(data_process_class_dll SHARED ${SOURCES})

# 日志库（静态库 spdlog）：
#   目标级 PUBLIC 接口提供 spdlog 头文件目录、SPDLOG_COMPILED_LIB 与 SPDLOG_ACTIVE_LEVEL，
#   使 rdp_log.cpp 及各业务模块（RDP_LOG_* 宏）以一致的配置编译。
target_link_libraries(data_process_class_dll PRIVATE spdlog)

# ---------------------------------------------------------------------------
# 数据记录模块（RDP_DATA_RECORD 启用时构建）：独立静态库、C++20（ormpp 要求），
# 与雷达处理代码（C++17）通过 rdp_record.h 的「仅声明 + 显式实例化」跨标准边界。
# ---------------------------------------------------------------------------
if(RDP_RECORD_ENABLED)
    enable_language(C)          # 用于编译 sqlite3 amalgamation

    add_library(rdp_record STATIC
        data_process_class_dll/rdp_record.cpp
        third_party/ormpp/thirdparty/sqlite3/sqlite3.c
    )
    set_target_properties(rdp_record PROPERTIES
        CXX_STANDARD 20
        CXX_STANDARD_REQUIRED ON
        ARCHIVE_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/lib"
    )
    # RDP_RECORD_ENABLE 经 PUBLIC 传递给 DLL：data_process.cpp 等源码中的 RDP_RECORD 宏随之生效
    target_compile_definitions(rdp_record PUBLIC
        RDP_RECORD_ENABLE
        ORMPP_ENABLE_SQLITE3
    )
    target_include_directories(rdp_record
        PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/data_process_class_dll
        PRIVATE
            ${CMAKE_CURRENT_SOURCE_DIR}/third_party/ormpp
            ${CMAKE_CURRENT_SOURCE_DIR}/third_party/ormpp/iguana
            ${CMAKE_CURRENT_SOURCE_DIR}/third_party/ormpp/ormpp
            ${CMAKE_CURRENT_SOURCE_DIR}/third_party/ormpp/thirdparty/sqlite3
    )
    target_compile_options(rdp_record PRIVATE
        # /W3 与 /Zc:__cplusplus 只用于 C++ 源文件：sqlite3.c 由下方源文件级 /w 关闭告警，
        # 若 C 文件也带 /W3，cl 会打印 D9025（overriding '/W3' with '/w'）命令行提示。
        $<$<COMPILE_LANGUAGE:CXX>:/W3>
        /utf-8
        $<$<COMPILE_LANGUAGE:CXX>:/Zc:__cplusplus>
        /FS
    )

    # 第三方 amalgamation 单独降到 /w（源文件级选项在编译命令行中位于目标级之后，优先生效）
    set_source_files_properties(third_party/ormpp/thirdparty/sqlite3/sqlite3.c
        PROPERTIES COMPILE_OPTIONS "/w")

    # rdp_record.cpp 内的 RDP_LOG_* 日志经 rdp_log.h 使用 spdlog：
    # 链接 spdlog 目标以获得其 PUBLIC 头文件目录与 SPDLOG_COMPILED_LIB /
    # SPDLOG_ACTIVE_LEVEL 定义（与 DLL 目标保持一致）。
    target_link_libraries(rdp_record PRIVATE spdlog)
    target_link_libraries(data_process_class_dll PRIVATE rdp_record)
endif()

# 编译 DLL 时定义导出宏；引用 DLL 的工程不应定义该宏。
target_compile_definitions(data_process_class_dll PRIVATE
    DATA_PROCESS_CLASS_DLL_LIBRARY
)

# 与 qmake 工程保持一致的 Windows 编译定义
if(WIN32)
    target_compile_definitions(data_process_class_dll PRIVATE
        WIN32
        _WINDOWS
        _UNICODE
        UNICODE
        NOMINMAX
    )
endif()

# Eigen 头文件位于源码目录下（<Eigen/Dense>）。
# toml++ 为单头文件第三方库（<toml.hpp>，仅 rdp_parameter_loader.cpp 使用）。
target_include_directories(data_process_class_dll PRIVATE
    ${CMAKE_CURRENT_SOURCE_DIR}/data_process_class_dll
    ${CMAKE_CURRENT_SOURCE_DIR}/third_party/toml
)

if(MSVC)
    target_compile_options(data_process_class_dll PRIVATE
        # 显式恢复 /W3 警告级别。
        # CMake 3.15（策略 CMP0092）起不再把 /W3 放进 CMAKE_CXX_FLAGS，
        # 而本工程此前（cmake_minimum_required 3.10）一直以 /W3 构建，
        # README 4.5 记录的全部 C4819/C4018/C4244 告警清理也基于该级别，
        # 因此这里显式指定，保持警告级别不变。
        /W3
        # MSVC 2022 完整支持 /utf-8（VS2015 Update 2 起），
        # 源码本身为 UTF-8 with BOM，显式指定后中文字符串/注释不再受系统代码页影响（消除 C4819）。
        /utf-8
        # 让 __cplusplus 正确报告 201703L（MSVC 默认始终为 199711L）。
        # C++17 下依赖 __cplusplus 判断的第三方库（Eigen 3.3.7 等）据此走标准分支。
        /Zc:__cplusplus
        /Zc:wchar_t
        # NMake 生成器下并行/增量编译时统一 PDB 写入。
        /FS
    )

    # 与原 qmake 生成的编译选项保持一致：Release 配置额外开启严格字符串字面量检查。
    # （原 MSVC 2013 因 Debug 运行库与 /Zc:strictStrings 冲突而仅限 Release，
    #   升级到 MSVC 2022 后该限制已不存在，此处仍沿用“仅 Release”以保持行为不变。）
    target_compile_options(data_process_class_dll PRIVATE
        $<$<CONFIG:Release>:/Zc:strictStrings>
    )
endif()

# 输出目录：build/bin 下为 DLL/PDB，build/lib 下为导入库。
set_target_properties(data_process_class_dll PROPERTIES
    RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/bin"
    ARCHIVE_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/lib"
    PDB_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/bin"
    PUBLIC_HEADER "${PUBLIC_HEADERS}"
)

# 安装规则：DLL/导入库 -> bin/lib，公开头文件 -> include。
include(GNUInstallDirs)
install(TARGETS data_process_class_dll
    RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
    ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
    LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
    PUBLIC_HEADER DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}
)
