cmake_minimum_required(VERSION 3.18)
project(adapTQ CXX)

set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)

if(NOT CMAKE_BUILD_TYPE)
  set(CMAKE_BUILD_TYPE Release)
endif()

# ---------------------------------------------------------------------------
# Compiler flags
# ---------------------------------------------------------------------------
if(MSVC)
  add_compile_options(/O2 /W3 /wd4996 /wd4068)
else()
  add_compile_options(-O3 -Wall -Wextra -Wno-unused-parameter)
endif()

# ---------------------------------------------------------------------------
# Core library
# ---------------------------------------------------------------------------
set(CORE_SOURCES
    core/fwht.cpp
    core/codebook.cpp
    core/quantizer.cpp
    core/adaptq_c_api.cpp
    core/adaptq_backend_vtable.cpp
    core/cost_optimizer.cpp
    core/quality_oracle.cpp
    cache/ring_buffer.cpp
    attention/attention.cpp
    utils/timer.cpp
    kernels/scalar/kdot_scalar.cpp
)

add_library(adapTQ_core STATIC ${CORE_SOURCES})
target_include_directories(adapTQ_core PUBLIC
    ${CMAKE_CURRENT_SOURCE_DIR}/include)

if(MSVC)
  target_compile_options(adapTQ_core PRIVATE /openmp /arch:AVX2)
else()
  target_compile_options(adapTQ_core PRIVATE -mavx2 -mfma -fopenmp)
  target_link_options(adapTQ_core PUBLIC -fopenmp)
endif()

if(UNIX AND NOT APPLE)
  target_link_libraries(adapTQ_core PUBLIC m)
endif()

# ---------------------------------------------------------------------------
# Runtime library (V2: RuntimeContext + SessionSnapshot + ReplayEngine)
# ---------------------------------------------------------------------------
# Strategy/storage/kernel implementations are #included directly in
# runtime_context.cpp as a single TU to avoid ODR issues — do NOT list them
# as separate RUNTIME_SOURCES.
set(RUNTIME_SOURCES
    runtime/runtime_context.cpp
    replay/session_snapshot.cpp
    replay/replay_engine.cpp
    cli/cmd_replay.cpp
    cli/cmd_compare.cpp
    cli/cmd_create_strategy.cpp
)

add_library(adapTQ_runtime STATIC ${RUNTIME_SOURCES})
target_include_directories(adapTQ_runtime PUBLIC
    ${CMAKE_CURRENT_SOURCE_DIR}/include
    ${CMAKE_CURRENT_SOURCE_DIR}/storage
    ${CMAKE_CURRENT_SOURCE_DIR}/strategies
    ${CMAKE_CURRENT_SOURCE_DIR}/kernels
    ${CMAKE_CURRENT_SOURCE_DIR}/runtime
    ${CMAKE_CURRENT_SOURCE_DIR}/replay
    ${CMAKE_CURRENT_SOURCE_DIR}/cli
)
target_link_libraries(adapTQ_runtime PUBLIC adapTQ_core)

if(MSVC)
  target_compile_options(adapTQ_runtime PRIVATE /arch:AVX2)
else()
  target_compile_options(adapTQ_runtime PRIVATE -mavx2 -mfma)
endif()

# ---------------------------------------------------------------------------
# Main demo executable
# ---------------------------------------------------------------------------

add_executable(adapTQ_demo main.cpp)
target_link_libraries(adapTQ_demo PRIVATE adapTQ_runtime adapTQ_core)
target_include_directories(adapTQ_demo PRIVATE
    ${CMAKE_CURRENT_SOURCE_DIR}
    ${CMAKE_CURRENT_SOURCE_DIR}/runtime
    ${CMAKE_CURRENT_SOURCE_DIR}/replay
    ${CMAKE_CURRENT_SOURCE_DIR}/cli
    ${CMAKE_CURRENT_SOURCE_DIR}/storage
    ${CMAKE_CURRENT_SOURCE_DIR}/strategies
    ${CMAKE_CURRENT_SOURCE_DIR}/kernels
)

# ---------------------------------------------------------------------------
# Tests — Catch2 via find_package first, then FetchContent fallback
# ---------------------------------------------------------------------------
option(ADAPTQ_BUILD_TESTS "Build unit and conformance tests" ON)

if(ADAPTQ_BUILD_TESTS)
  # Try system Catch2 (apt/brew install catch2)
  find_package(Catch2 3 QUIET)

  if(NOT Catch2_FOUND)
    # Fallback: download Catch2 at configure time via FetchContent
    message(STATUS "Catch2 not found via find_package — using FetchContent")
    include(FetchContent)
    FetchContent_Declare(
      Catch2
      GIT_REPOSITORY https://github.com/catchorg/Catch2.git
      GIT_TAG        v3.7.1
      GIT_SHALLOW    TRUE
    )
    FetchContent_MakeAvailable(Catch2)
  endif()

  enable_testing()
  include(CTest)
  include(Catch)

  # --- test_fwht -----------------------------------------------------------
  add_executable(test_fwht
      tests/unit/test_fwht.cpp
      core/fwht.cpp)
  target_include_directories(test_fwht PRIVATE include)
  target_link_libraries(test_fwht PRIVATE Catch2::Catch2WithMain)
  catch_discover_tests(test_fwht)

  # --- test_packing --------------------------------------------------------
  add_executable(test_packing
      tests/unit/test_packing.cpp)
  target_link_libraries(test_packing PRIVATE adapTQ_core Catch2::Catch2WithMain)
  catch_discover_tests(test_packing)

  # --- test_storage --------------------------------------------------------
  # Storage backends are header-only (inline classes in storage/*.h).
  # The .cpp stubs are intentionally NOT listed — they'd be empty TUs anyway.
  add_executable(test_storage
      tests/unit/test_storage.cpp)
  target_include_directories(test_storage PRIVATE include storage)
  target_link_libraries(test_storage PRIVATE Catch2::Catch2WithMain)
  catch_discover_tests(test_storage)

  # --- test_api ------------------------------------------------------------
  add_executable(test_api
      tests/unit/test_api.cpp)
  target_link_libraries(test_api PRIVATE adapTQ_core Catch2::Catch2WithMain)
  catch_discover_tests(test_api)

  # --- test_conformance ----------------------------------------------------
  # Strategy .cpp files are compiled inline within this single TU (not in
  # any library) to avoid ODR violations. The CMakeLists does not list them
  # as separate sources precisely because they are #included by the .cpp.
  add_executable(test_conformance
      tests/conformance/strategy_conformance.cpp)
  target_include_directories(test_conformance PRIVATE
      include storage strategies tests/conformance)
  target_link_libraries(test_conformance PRIVATE adapTQ_core Catch2::Catch2WithMain)
  if(NOT MSVC)
    target_compile_options(test_conformance PRIVATE -mavx2 -mfma)
  else()
    target_compile_options(test_conformance PRIVATE /arch:AVX2)
  endif()
  catch_discover_tests(test_conformance)

  # --- test_runtime_context -----------------------------------------------
  # RuntimeContext #includes strategy/kernel implementations internally.
  add_executable(test_runtime_context
      tests/unit/test_runtime_context.cpp)
  target_include_directories(test_runtime_context PRIVATE
      include storage strategies kernels runtime replay)
  target_link_libraries(test_runtime_context PRIVATE adapTQ_runtime adapTQ_core Catch2::Catch2WithMain)
  if(NOT MSVC)
    target_compile_options(test_runtime_context PRIVATE -mavx2 -mfma)
  else()
    target_compile_options(test_runtime_context PRIVATE /arch:AVX2)
  endif()
  catch_discover_tests(test_runtime_context)

  # --- test_session_snapshot -----------------------------------------------
  add_executable(test_session_snapshot
      tests/unit/test_session_snapshot.cpp)
  target_include_directories(test_session_snapshot PRIVATE
      include storage strategies kernels runtime replay)
  target_link_libraries(test_session_snapshot PRIVATE adapTQ_runtime adapTQ_core Catch2::Catch2WithMain)
  if(NOT MSVC)
    target_compile_options(test_session_snapshot PRIVATE -mavx2 -mfma)
  else()
    target_compile_options(test_session_snapshot PRIVATE /arch:AVX2)
  endif()
  catch_discover_tests(test_session_snapshot)

  # --- test_replay_engine --------------------------------------------------
  add_executable(test_replay_engine
      tests/unit/test_replay_engine.cpp)
  target_include_directories(test_replay_engine PRIVATE
      include storage strategies kernels runtime replay)
  target_link_libraries(test_replay_engine PRIVATE adapTQ_runtime adapTQ_core Catch2::Catch2WithMain)
  if(NOT MSVC)
    target_compile_options(test_replay_engine PRIVATE -mavx2 -mfma)
  else()
    target_compile_options(test_replay_engine PRIVATE /arch:AVX2)
  endif()
  catch_discover_tests(test_replay_engine)

  message(STATUS "Test targets enabled (V1: 38 tests + V2: runtime/snapshot/replay)")
endif()
