cmake_minimum_required(VERSION 3.15)
project(lvgl_sim C)

#=============================================================================
# 路径配置（相对于 simulator/ 目录）
#=============================================================================
set(PROJECT_ROOT    "${CMAKE_CURRENT_SOURCE_DIR}/..")
set(LVGL_DIR        "${PROJECT_ROOT}/Lib/lvgl")
set(UI_DIR          "${PROJECT_ROOT}/Drivers/BSP/UI")
set(NET_DIR         "${PROJECT_ROOT}/Drivers/BSP/NET")
set(NETCFG_DIR      "${PROJECT_ROOT}/Drivers/BSP/NETCFG")
set(SDL2_DIR       "${CMAKE_CURRENT_SOURCE_DIR}/SDL2/SDL2-2.32.10")

#=============================================================================
# 编译标准
#=============================================================================
set(CMAKE_C_STANDARD 99)
set(CMAKE_C_STANDARD_REQUIRED ON)

# Windows 控制台输出（显示 printf）
if(WIN32)
    add_compile_definitions(_CRT_SECURE_NO_WARNINGS)
endif()

# MSVC：源码为 UTF-8 编码（含中文注释），强制按 UTF-8 解析消除 C4819 警告
if(MSVC)
    add_compile_options(/utf-8)
endif()

#=============================================================================
# SDL2 头文件路径（需先于 add_subdirectory 定义，供 LVGL SDL 驱动使用）
#=============================================================================
# LVGL SDL 驱动内部 #include "SDL2/SDL.h"，include 目录下已有 SDL2/ 子目录
set(SDL2_INCLUDE_DIR "${SDL2_DIR}/include" CACHE PATH "SDL2 include directory for LVGL")

#=============================================================================
# LVGL 子目录（引用项目自带的 LVGL v9.3.0 源码）
#=============================================================================
# LVGL 内部会自动检测 lv_conf.h（通过 LV_CONF_INCLUDE_SIMPLE 宏）
# 需要将 simulator 目录加入 LVGL 的包含路径，使其能找到 lv_conf.h
set(LV_CONF_DIR "${CMAKE_CURRENT_SOURCE_DIR}" CACHE PATH "Directory containing lv_conf.h for LVGL")
add_subdirectory("${LVGL_DIR}" lvgl_build)

#=============================================================================
# 包含路径
#=============================================================================
include_directories(
    ${CMAKE_CURRENT_SOURCE_DIR}          # lv_conf.h (LVGL 找), dbg_log.h
    ${CMAKE_CURRENT_SOURCE_DIR}/sim     # sim_port.h 等模拟器平台层
    ${LVGL_DIR}                          # lvgl.h
    ${LVGL_DIR}/src                      # lvgl 内部头文件
    ${UI_DIR}                            # ui_menu.h, ui_font_cn_12.h
    ${NET_DIR}                           # net_socket.h / net_types.h（net_* API 双端共享核心）
    ${NETCFG_DIR}                        # net_cfg.h（网络配置，双端共享核心）
    ${PROJECT_ROOT}/test/board      # test_config.h / net_latency_test.h（共享核心
                                    # ui_net_sink.c、net_socket.c 引含；探针默认关闭，
                                    # 声明宿主安全；2026-09-21 修复模拟器构建断链；
                                    # 2026-10-02 测试目录重构后头文件移入 test/board/）
    ${PROJECT_ROOT}/App/cfg            # cfg_file.h / cfg_file_store.h（通用配置文件引擎，双端共享核心）
    ${PROJECT_ROOT}/App/protocol/iec104  # iec104_comm.h（[comm] 段契约头，供 IEC104_SOURCES 编译；道G task-8）
    ${PROJECT_ROOT}/App/log             # sys_log.h（运行/自检记录引擎，双端共享核心）
    ${PROJECT_ROOT}/App/algo            # prot_set.h（定值模型纯算法，双端共享核心；见 ALGO_SOURCES）
    ${PROJECT_ROOT}/Drivers/BSP          # dbg_log.h（使用 simulator 的 dbg_cfg.h 配置）
    ${SDL2_DIR}/include                  # SDL2 头文件
)

#=============================================================================
# SDL2 库（使用本地预编译版本）
#=============================================================================
set(SDL2_INCLUDE_PARENT "${SDL2_DIR}/include")
set(SDL2_LIB_X64        "${SDL2_DIR}/lib/x64")

#=============================================================================
# 源文件
#=============================================================================
set(SIM_SOURCES
    main.c
    sim/sim_port.c
    sim/sim_latency_stub.c    # 探针符号宿主空实现（ui_net_sink.c 引用，见文件头注释）
)

# UI 源文件（与 MCU 端共用，直接编译）
set(UI_SOURCES
    ${UI_DIR}/ui_menu.c
    ${UI_DIR}/ui_menu_view.c
    ${UI_DIR}/ui_content_page.c
    ${UI_DIR}/ui_deco.c
    ${UI_DIR}/ui_page_device_info.c
    ${UI_DIR}/ui_page_ac.c
    ${UI_DIR}/ui_page_dc.c
    ${UI_DIR}/ui_page_password.c
    ${UI_DIR}/ui_page_net_cfg.c
    ${UI_DIR}/ui_page_runlog.c
    ${UI_DIR}/ui_page_selftest.c
    ${UI_DIR}/ui_standby.c
    ${UI_DIR}/ui_menu_tab.c
    ${UI_DIR}/ui_font_cn_12.c
    ${UI_DIR}/ui_net_sink.c
    ${UI_DIR}/ui_page_registry.c
    ${UI_DIR}/ui_style.c
    ${UI_DIR}/ui_w_records.c
    ${UI_DIR}/ui_w_editor.c
    ${UI_DIR}/ui_w_setlist.c
    ${UI_DIR}/ui_page_action.c
    ${UI_DIR}/ui_data.c      # 数据快照存侧（道E task-14；写侧 App/app/ui_data_feed.c 不进模拟器，见 scripts/check_registration.py）
    ${UI_DIR}/ui_w_soe.c   # SOE 记录组件（道E task-14；reader 已在批 2 两端绑定）
    ${UI_DIR}/ui_w_setview.c   # 定值查看视图模型（道E task-15 批 3：四张视图表 + Q10 换算 + 越界反显）
    ${UI_DIR}/ui_w_ytview.c   # 遥测整定视图模型（道F task-7：死区/系数两页共用注入表视图 + 落盘确认状态机）
    ${UI_DIR}/ui_page_soe.c
    ${UI_DIR}/ui_page_view_param.c
    ${UI_DIR}/ui_page_view_plate.c
    ${UI_DIR}/ui_page_view_set.c
    ${UI_DIR}/ui_page_view_word.c
    ${UI_DIR}/ui_page_yx.c
    ${UI_DIR}/ui_page_clear.c
    ${UI_DIR}/ui_page_clock.c
    ${UI_DIR}/ui_page_reset.c
    ${UI_DIR}/ui_page_calib.c
    ${UI_DIR}/ui_page_deadband.c
    ${UI_DIR}/ui_page_comm.c
    ${UI_DIR}/ui_page_accident.c
    ${UI_DIR}/ui_page_alarm.c
    ${UI_DIR}/ui_page_oper.c
    ${UI_DIR}/ui_page_runreport.c
    ${UI_DIR}/ui_page_start.c
    ${UI_DIR}/ui_page_yk.c
    ${UI_DIR}/ui_page_wave_manual.c
    ${UI_DIR}/ui_page_wave_play.c
)

# 运行/自检记录引擎（双端共享核心 + 平台存储后端；MCU 端编 sys_log_mcu.c，见 Keil 工程）
set(SYSLOG_SOURCES
    sim/sim_syslog.c
)

# 网络配置（双端共享核心 + cfg_file 引擎 + 平台存储后端；MCU 端编 cfg_file_store_mcu.c，见 Keil 工程）
set(NETCFG_SOURCES
    ${NETCFG_DIR}/net_cfg.c
    ${NETCFG_DIR}/ftp_cfg.c
    ${PROJECT_ROOT}/App/cfg/cfg_file.c
    sim/cfg_file_store_sim.c
)

# 保护定值模型（App/algo/prot_set.c 零平台依赖，双端共享核心）。
# 模拟器只需要"出厂默认定值"来发布 UI 快照帧（main.c 的 sim_publish_data_frames），
# 不在这里复制一份 40 项数字——抄表就等于造第二真相（同 scripts/check_font_charset.py
# "单一规则源"的同一治理口径）。
set(ALGO_SOURCES
    ${PROJECT_ROOT}/App/algo/prot_set.c
    # 录波段格式与录波环（故障录波 P1/P2）：两份都是零平台依赖纯逻辑——不 include HAL/CMSIS/RTOS、
    # 不调 FatFs、不建 RTOS 对象、无 malloc。模拟器必须一起编译，因为「录波回放」页要在模拟器里
    # 出真波形（峰值换算走 wave_store_code_to_milli，取数走 wave_ring_read_ch），数据由
    # simulator/main.c 的合成节拍喂进 ring —— 与 prot_set.c 同一条「双端共享核心、不造第二真相」
    # 口径，因此两份都**不进** scripts/check_registration.py 的 SIM_EXEMPT。
    ${PROJECT_ROOT}/App/algo/wave_store.c
    ${PROJECT_ROOT}/App/algo/wave_ring.c
)

# IEC 104 [comm] 配置段（道G task-8）：模拟器运行期不跑 104 任务、无消费路径，
# 本 TU 仅为双端注册事实一致而编译（iec104 其余 .c 走 scripts 白名单逐个点名，
# 白名单是门禁地盘不在这里扩——新配对模块按 new-source-file 口径进显式源列表）。
set(IEC104_SOURCES
    ${PROJECT_ROOT}/App/protocol/iec104/iec104_comm.c
)

# 网络后端（net_* 核心 API 的 Winsock 实现 + UI 推流平台后端；MCU 端另编 net_socket.c 与 _mcu 后端）
set(NET_SOURCES
    sim/net_socket_sim.c
    sim/ui_net_sink_backend_sim.c
)

#=============================================================================
# 构建可执行文件
#=============================================================================
add_executable(lvgl_sim WIN32 ${SIM_SOURCES} ${UI_SOURCES} ${NETCFG_SOURCES} ${SYSLOG_SOURCES} ${NET_SOURCES} ${ALGO_SOURCES} ${IEC104_SOURCES})

target_compile_definitions(lvgl_sim PRIVATE
    LV_CONF_INCLUDE_SIMPLE       # 让 lvgl.h 找到 lv_conf.h
    LV_LVGL_H_INCLUDE_SIMPLE     # ui_font_cn_12.c 内部 include 简化
    WIN32_LEAN_AND_MEAN          # 防止 Windows.h 拉入 winsock.h（与 winsock2.h 冲突）
    NET_NO_OS                    # 剥离 net_socket.h 的 FreeRTOS 相关接口（纯 BSD Socket）
    UI_REMO_ALLOW_GOTO           # 模拟器构建启用 GOTO(5) 直开页（#69，2026-11-02；台架 MCU 门控 DTU_BENCH，见 ui_net_sink.c 注释）
)

target_include_directories(lvgl_sim PRIVATE
    ${SDL2_INCLUDE_PARENT}        # SDL2/SDL.h 路径
)

target_link_directories(lvgl_sim PRIVATE
    ${SDL2_LIB_X64}
)

target_link_libraries(lvgl_sim
    lvgl
    SDL2
    SDL2main
    ws2_32
)

# 数学库（非 MSVC）
if(NOT MSVC)
    target_link_libraries(lvgl_sim m pthread)
endif()

# Windows 子系统：控制台（显示 printf 输出）
if(WIN32)
    if(MSVC)
        target_link_options(lvgl_sim PRIVATE "/SUBSYSTEM:CONSOLE")
    else()
        target_link_options(lvgl_sim PRIVATE "-mconsole")
    endif()
endif()

# 复制 SDL2.dll 到输出目录
add_custom_command(TARGET lvgl_sim POST_BUILD
    COMMAND ${CMAKE_COMMAND} -E copy_if_different
        "${SDL2_LIB_X64}/SDL2.dll"
        "$<TARGET_FILE_DIR:lvgl_sim>/SDL2.dll"
    COMMENT "Copying SDL2.dll to output directory"
)

#=============================================================================
# 自定义目标：运行模拟器
#=============================================================================
add_custom_target(run COMMAND ${CMAKE_BINARY_DIR}/lvgl_sim DEPENDS lvgl_sim)

#=============================================================================
# 调试模式额外警告
#=============================================================================
if(CMAKE_BUILD_TYPE STREQUAL "Debug")
    message(STATUS "Debug mode enabled for simulator")
    if(MSVC)
        target_compile_options(lvgl_sim PRIVATE /W4)
    else()
        target_compile_options(lvgl_sim PRIVATE -Wall -Wextra -Wpedantic)
    endif()
endif()

message(STATUS "=== LVGL Simulator ===")
message(STATUS "Screen: 160x160, 1bpp monochrome")
message(STATUS "LVGL dir: ${LVGL_DIR}")
message(STATUS "UI dir:   ${UI_DIR}")
message(STATUS "SDL2:     ${SDL2_DIR}")
