# CMakeLists.txt — Build configuration for the LIN Simulator (C++)
#
# CMAKE BASICS:
# =============
# CMake is a build system generator. It doesn't compile code directly.
# Instead, it generates platform-specific build files:
#   - macOS/Linux: Makefiles (then you run `make`)
#   - Windows: Visual Studio project files
#   - Any platform: Ninja build files (faster than Make)
#
# The workflow is:
#   1. mkdir build && cd build
#   2. cmake ..              ← generates build files from this CMakeLists.txt
#   3. cmake --build .       ← compiles and links the project
#   4. ./lin_simulator       ← run the application
#
# Qt6 integration:
#   CMake has built-in support for Qt via find_package(Qt6).
#   Qt needs special preprocessing for its features:
#     - MOC (Meta-Object Compiler): processes Q_OBJECT macros for signals/slots
#     - UIC: compiles .ui designer files to C++ headers (we don't use these)
#     - RCC: compiles resource files (icons, etc.) into the binary

cmake_minimum_required(VERSION 3.16)

project(LINSimulator
    VERSION 0.1.0
    LANGUAGES CXX
    DESCRIPTION "LIN Simulator using BabyLIN devices"
)

# C++17 standard — required for structured bindings, std::optional, etc.
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)

# ── Qt6 auto-processing ──
# These three lines enable Qt's special preprocessors:
#   AUTOMOC: automatically runs MOC on headers containing Q_OBJECT
#   AUTOUIC: automatically compiles .ui files (if we had any)
#   AUTORCC: automatically compiles .qrc resource files
set(CMAKE_AUTOMOC ON)
set(CMAKE_AUTOUIC ON)
set(CMAKE_AUTORCC ON)

# ── Find Qt6 ──
# find_package searches for Qt6 on the system.
# REQUIRED means CMake will error if Qt6 is not found.
# COMPONENTS lists which Qt modules we need:
#   Widgets: GUI widgets (QMainWindow, QPushButton, etc.)
#   We'll add SerialPort in Step 5 when we need device communication.
find_package(Qt6 REQUIRED COMPONENTS Widgets SerialPort)

# ── Main application target ──
# qt_add_executable is Qt's wrapper around add_executable.
# It handles platform-specific details (macOS app bundle, Windows subsystem, etc.)
qt_add_executable(lin_simulator
    src/main.cpp
    src/main_window.cpp
    src/main_window.h
    src/ldf_parser.cpp
    src/ldf_parser.h
    src/connection_manager.cpp
    src/connection_manager.h
    src/scheduler.cpp
    src/scheduler.h
)

target_link_libraries(lin_simulator PRIVATE Qt6::Widgets Qt6::SerialPort)

# ── Tests ──
# We use Qt's built-in test framework (QTest) instead of GoogleTest
# because it integrates naturally with Qt's event loop and widgets.
# QTest provides:
#   - QVERIFY(condition): assert a condition is true
#   - QCOMPARE(actual, expected): assert two values are equal
#   - QTest::mouseClick(): simulate mouse events
#   - QTest::keyClick(): simulate keyboard events
enable_testing()
find_package(Qt6 REQUIRED COMPONENTS Test)

qt_add_executable(test_main_window
    tests/test_main_window.cpp
    src/main_window.cpp
    src/main_window.h
    src/ldf_parser.cpp
    src/ldf_parser.h
    src/connection_manager.cpp
    src/connection_manager.h
    src/scheduler.cpp
    src/scheduler.h
)

target_link_libraries(test_main_window PRIVATE Qt6::Widgets Qt6::SerialPort Qt6::Test)
target_include_directories(test_main_window PRIVATE src)
add_test(NAME test_main_window COMMAND test_main_window)

# ── Step 2: LDF Parser tests ──
# These test the custom LDF parser (ldf_parser.cpp) in isolation,
# without any GUI. C++ equivalent of python/tests/test_ldf_handler.py.
#
# COMPILE DEFINITIONS:
# target_compile_definitions() injects #define macros at compile time.
# We use it to pass the sample LDF file path to the test code:
#
#   CMake:   target_compile_definitions(... LDF_SAMPLE_PATH="/absolute/path/sample.ldf")
#   Effect:  #define LDF_SAMPLE_PATH "/absolute/path/sample.ldf"  ← injected into source
#   Usage:   parseLdf(QString(LDF_SAMPLE_PATH));  ← in test code
#
# This is how C++ projects pass configuration to code at build time.
# Python doesn't need this because it resolves paths at runtime with __file__.
#
# CMAKE_CURRENT_SOURCE_DIR is a built-in CMake variable pointing to the
# directory containing this CMakeLists.txt file (/path/to/cpp/).
set(LDF_SAMPLE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/../resources/sample.ldf")

# Parser unit tests — only needs ldf_parser, no main_window
qt_add_executable(test_ldf_parser
    tests/test_ldf_parser.cpp
    src/ldf_parser.cpp
    src/ldf_parser.h
)

target_link_libraries(test_ldf_parser PRIVATE Qt6::Widgets Qt6::SerialPort Qt6::Test)
target_include_directories(test_ldf_parser PRIVATE src)
target_compile_definitions(test_ldf_parser PRIVATE
    LDF_SAMPLE_PATH="${LDF_SAMPLE_PATH}"
)
add_test(NAME test_ldf_parser COMMAND test_ldf_parser)

# ── Step 2: LDF Loading GUI integration tests ──
# These test the full pipeline: parse LDF → populate MainWindow tables.
# C++ equivalent of python/tests/test_ldf_loading.py.
#
# This target needs BOTH ldf_parser AND main_window because it tests
# how the GUI responds to loading an LDF file. Each test target in CMake
# is a separate executable — it needs all the source files it depends on.
#
# WHY DUPLICATE SOURCE FILES IN MULTIPLE TARGETS:
# Unlike Python where importing a module shares code, each C++ executable
# is compiled independently. test_ldf_loading needs main_window.cpp because
# it creates MainWindow objects. We could avoid this with a shared library,
# but for a small project, listing sources per target is simpler.
qt_add_executable(test_ldf_loading
    tests/test_ldf_loading.cpp
    src/main_window.cpp
    src/main_window.h
    src/ldf_parser.cpp
    src/ldf_parser.h
    src/connection_manager.cpp
    src/connection_manager.h
    src/scheduler.cpp
    src/scheduler.h
)

target_link_libraries(test_ldf_loading PRIVATE Qt6::Widgets Qt6::SerialPort Qt6::Test)
target_include_directories(test_ldf_loading PRIVATE src)
target_compile_definitions(test_ldf_loading PRIVATE
    LDF_SAMPLE_PATH="${LDF_SAMPLE_PATH}"
)
add_test(NAME test_ldf_loading COMMAND test_ldf_loading)

# ── Step 3: Signal editing tests ──
qt_add_executable(test_signal_editing
    tests/test_signal_editing.cpp
    src/main_window.cpp
    src/main_window.h
    src/ldf_parser.cpp
    src/ldf_parser.h
    src/connection_manager.cpp
    src/connection_manager.h
    src/scheduler.cpp
    src/scheduler.h
)

target_link_libraries(test_signal_editing PRIVATE Qt6::Widgets Qt6::SerialPort Qt6::Test)
target_include_directories(test_signal_editing PRIVATE src)
target_compile_definitions(test_signal_editing PRIVATE
    LDF_SAMPLE_PATH="${LDF_SAMPLE_PATH}"
)
add_test(NAME test_signal_editing COMMAND test_signal_editing)

# ── Step 4: Rx real-time display tests ──
qt_add_executable(test_rx_realtime
    tests/test_rx_realtime.cpp
    src/main_window.cpp
    src/main_window.h
    src/ldf_parser.cpp
    src/ldf_parser.h
    src/connection_manager.cpp
    src/connection_manager.h
    src/scheduler.cpp
    src/scheduler.h
)

target_link_libraries(test_rx_realtime PRIVATE Qt6::Widgets Qt6::SerialPort Qt6::Test)
target_include_directories(test_rx_realtime PRIVATE src)
target_compile_definitions(test_rx_realtime PRIVATE
    LDF_SAMPLE_PATH="${LDF_SAMPLE_PATH}"
)
add_test(NAME test_rx_realtime COMMAND test_rx_realtime)
