cmake_minimum_required(VERSION 3.22)

project(rockstream VERSION 0.1.0 LANGUAGES CXX)

if(NOT CMAKE_SYSTEM_NAME STREQUAL "Linux")
    message(FATAL_ERROR "rockstream currently supports Linux only")
endif()

option(ROCKSTREAM_WITH_MPP "Build with the target BSP's Rockchip MPP SDK" OFF)
option(ROCKSTREAM_SANITIZERS "Enable AddressSanitizer and UndefinedBehaviorSanitizer" OFF)
option(ROCKSTREAM_WARNINGS_AS_ERRORS "Treat project compiler warnings as errors" OFF)

list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake")
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
include(CTest)
include(GNUInstallDirs)
find_package(Threads REQUIRED)

add_library(rockstream_options INTERFACE)
target_compile_features(rockstream_options INTERFACE cxx_std_20)
if(CMAKE_CXX_COMPILER_ID MATCHES "GNU|Clang")
    target_compile_options(rockstream_options INTERFACE
        -Wall -Wextra -Wpedantic -Wconversion -Wsign-conversion -Wshadow
        $<$<BOOL:${ROCKSTREAM_WARNINGS_AS_ERRORS}>:-Werror>)
    if(ROCKSTREAM_SANITIZERS)
        include(CheckCXXSourceCompiles)
        include(CMakePushCheckState)
        cmake_push_check_state(RESET)
        set(CMAKE_REQUIRED_FLAGS "-fsanitize=address,undefined")
        set(CMAKE_REQUIRED_LINK_OPTIONS "-fsanitize=address,undefined")
        check_cxx_source_compiles("int main() { return 0; }" ROCKSTREAM_SANITIZERS_LINK)
        cmake_pop_check_state()
        if(NOT ROCKSTREAM_SANITIZERS_LINK)
            message(FATAL_ERROR "Sanitizer runtime compile/link check failed. Install ASan and UBSan runtimes matching your compiler, or disable ROCKSTREAM_SANITIZERS.")
        endif()
        target_compile_options(rockstream_options INTERFACE
            -fsanitize=address,undefined -fno-omit-frame-pointer)
        target_link_options(rockstream_options INTERFACE -fsanitize=address,undefined)
    endif()
elseif(ROCKSTREAM_SANITIZERS OR ROCKSTREAM_WARNINGS_AS_ERRORS)
    message(FATAL_ERROR "Requested compiler options require GCC or Clang")
endif()

if(ROCKSTREAM_WITH_MPP)
    find_package(RockchipMPP REQUIRED)
    # Compile and link only: this check never executes hardware code, including
    # when cross compiling. Catch missing public headers or an unusable library.
    include(CheckCXXSourceCompiles)
    include(CMakePushCheckState)
    cmake_push_check_state(RESET)
    set(CMAKE_REQUIRED_LIBRARIES RockchipMPP::MPP)
    unset(ROCKSTREAM_MPP_SDK_LINKS CACHE)
    check_cxx_source_compiles("
        #include <rk_mpi.h>
        #include <mpp_buffer.h>
        #include <mpp_frame.h>
        #include <mpp_packet.h>
        #include <rk_venc_cfg.h>
        #include <rk_venc_rc.h>
        int main() {
            MppCtx ctx = nullptr;
            MppApi* api = nullptr;
            MppEncCfg cfg = nullptr;
            if (mpp_enc_cfg_init(&cfg) != MPP_OK) return 1;
            mpp_enc_cfg_deinit(cfg);
            if (mpp_create(&ctx, &api) != MPP_OK) return 1;
            return mpp_destroy(ctx);
        }" ROCKSTREAM_MPP_SDK_LINKS)
    cmake_pop_check_state()
    if(NOT ROCKSTREAM_MPP_SDK_LINKS)
        message(FATAL_ERROR "MPP SDK compile/link check failed. Use matching BSP headers and libraries; see CMakeFiles/CMakeError.log or CMakeConfigureLog.yaml.")
    endif()
endif()

configure_file(cmake/build_config.hpp.in generated/rockstream/build_config.hpp @ONLY)

add_library(rockstream_core STATIC src/build_info.cpp src/probe.cpp src/latency.cpp)
target_include_directories(rockstream_core PUBLIC
    "${CMAKE_CURRENT_SOURCE_DIR}/include"
    "${CMAKE_CURRENT_BINARY_DIR}/generated")
target_link_libraries(rockstream_core PRIVATE rockstream_options PUBLIC Threads::Threads)
set_target_properties(rockstream_core PROPERTIES CXX_EXTENSIONS OFF)
if(ROCKSTREAM_WITH_MPP)
    target_sources(rockstream_core PRIVATE src/mpp_probe.cpp)
    target_link_libraries(rockstream_core PRIVATE RockchipMPP::MPP)
else()
    # Host development gets public API declarations, never a pretend MPP runtime.
    add_library(rockstream_mpp_headers INTERFACE)
    target_include_directories(rockstream_mpp_headers SYSTEM INTERFACE
        "${CMAKE_CURRENT_SOURCE_DIR}/third_party/mpp/include")
    target_link_libraries(rockstream_core PRIVATE rockstream_mpp_headers)
endif()

add_executable(rockstream src/main.cpp)
target_link_libraries(rockstream PRIVATE rockstream_core rockstream_options)
set_target_properties(rockstream PROPERTIES CXX_EXTENSIONS OFF)
install(TARGETS rockstream RUNTIME DESTINATION "${CMAKE_INSTALL_BINDIR}")

if(BUILD_TESTING)
    # Compile only: exercise SDK declarations without requiring device access or
    # linking undefined MPP calls. Board builds use their installed SDK instead.
    add_library(mpp_headers_check OBJECT tests/mpp_headers_check.cpp)
    if(NOT ROCKSTREAM_WITH_MPP)
        # Compile the complete native probe against pinned headers on the host.
        # Its objects are deliberately never linked into a host executable.
        target_sources(mpp_headers_check PRIVATE src/mpp_probe.cpp)
        target_include_directories(mpp_headers_check PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}/include")
    endif()
    target_link_libraries(mpp_headers_check PRIVATE rockstream_options)
    set_target_properties(mpp_headers_check PROPERTIES CXX_EXTENSIONS OFF)
    if(ROCKSTREAM_WITH_MPP)
        target_link_libraries(mpp_headers_check PRIVATE RockchipMPP::MPP)
    else()
        target_link_libraries(mpp_headers_check PRIVATE rockstream_mpp_headers)
    endif()
    add_test(NAME cli_help COMMAND rockstream --help)
    add_test(NAME cli_build_info COMMAND rockstream --build-info)
    set_tests_properties(cli_build_info PROPERTIES PASS_REGULAR_EXPRESSION "MPP SDK: (enabled|disabled)")
    add_test(NAME cli_reject_unknown COMMAND rockstream --invalid-option)
    set_tests_properties(cli_reject_unknown PROPERTIES WILL_FAIL TRUE)
    add_test(NAME probe_dry_run COMMAND rockstream encode-probe --dry-run --tiles off --split-ctus 60)
    set_tests_properties(probe_dry_run PROPERTIES PASS_REGULAR_EXPRESSION "Dry run: no encoder initialized")
    add_executable(probe_tests tests/probe_tests.cpp)
    target_link_libraries(probe_tests PRIVATE rockstream_core rockstream_options)
    set_target_properties(probe_tests PROPERTIES CXX_EXTENSIONS OFF)
    add_test(NAME probe_logic COMMAND probe_tests)
endif()

message(STATUS "rockstream ${PROJECT_VERSION}: ${CMAKE_SYSTEM_PROCESSOR}, MPP=${ROCKSTREAM_WITH_MPP}, sanitizers=${ROCKSTREAM_SANITIZERS}")
