cmake_minimum_required(VERSION 3.20)
# os_sync_wait_on_address is part of the supported macOS implementation.
if(CMAKE_HOST_APPLE AND NOT CMAKE_OSX_DEPLOYMENT_TARGET AND
   (NOT CMAKE_SYSTEM_NAME OR CMAKE_SYSTEM_NAME STREQUAL "Darwin"))
  set(CMAKE_OSX_DEPLOYMENT_TARGET "14.4" CACHE STRING "Minimum macOS version" FORCE)
endif()
project(TwoBufQueue VERSION 0.1.0 LANGUAGES CXX)
if(APPLE AND CMAKE_OSX_DEPLOYMENT_TARGET VERSION_LESS "14.4")
  message(FATAL_ERROR "TwoBufQueue requires macOS 14.4 or newer")
endif()

set(brq_standalone OFF)
if(CMAKE_SOURCE_DIR STREQUAL CMAKE_CURRENT_SOURCE_DIR)
  set(brq_standalone ON)
endif()
option(BRQ_BUILD_TESTS "Build regression tests" ${brq_standalone})
option(BRQ_BUILD_EXAMPLES "Build runnable examples" ${brq_standalone})
option(BRQ_INSTALL "Generate installation and find_package support" ON)
option(BRQ_BUILD_BENCHMARKS "Deprecated historical benchmarks" OFF)
option(BRQ_BUILD_SPSC_BENCHMARKS "Build configurable full-pipeline and SPSC performance search" OFF)
option(BRQ_ENABLE_POOL_DEPTH_STATS "Instrument successful pool acquisitions for block-size benchmarks" OFF)
option(BRQ_BUILD_COMPARISON "Deprecated historical comparisons" OFF)
option(BRQ_DISABLE_TSC "Use the portable standard clock" OFF)
if(BRQ_BUILD_BENCHMARKS OR BRQ_BUILD_COMPARISON)
  message(FATAL_ERROR "Historical benchmark APIs are no longer supported. Use BRQ_BUILD_SPSC_BENCHMARKS=ON; see benchmarks/TUNABLE_SPSC.zh-CN.md")
endif()
include(GNUInstallDirs)
find_package(Threads REQUIRED)
file(GLOB_RECURSE component_sources CONFIGURE_DEPENDS "src/*.cpp")
add_library(TwoBufQueue STATIC ${component_sources})
add_library(TwoBufQueue::TwoBufQueue ALIAS TwoBufQueue)
target_compile_features(TwoBufQueue PUBLIC cxx_std_20)
set_target_properties(TwoBufQueue PROPERTIES CXX_EXTENSIONS OFF)
# Forwarders preserve existing include paths without requiring a particular checkout
# directory name or Windows symlink privileges. The original headers remain authoritative.
file(GLOB_RECURSE component_headers CONFIGURE_DEPENDS "src/*.hpp")
foreach(header IN LISTS component_headers)
  file(RELATIVE_PATH relative "${CMAKE_CURRENT_SOURCE_DIR}" "${header}")
  file(GENERATE OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/include/TwoBufQueue/${relative}"
    CONTENT "#pragma once\n#include \"${header}\"\n")
endforeach()
target_include_directories(TwoBufQueue PUBLIC
  "$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>"
  "$<BUILD_INTERFACE:${CMAKE_CURRENT_BINARY_DIR}/include>"
  "$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>")
target_link_libraries(TwoBufQueue PUBLIC Threads::Threads)
if(BRQ_ENABLE_POOL_DEPTH_STATS)
  # Keep all translation units consistent; default library builds omit the
  # fields, branch and counters entirely.
  target_compile_definitions(TwoBufQueue PUBLIC BRQ_ENABLE_POOL_DEPTH_STATS=1)
endif()
if(WIN32)
  target_link_libraries(TwoBufQueue PUBLIC synchronization)
endif()
if(BRQ_DISABLE_TSC)
  target_compile_definitions(TwoBufQueue PRIVATE FAST_LOG_DISABLE_TSC)
endif()
if(BRQ_INSTALL)
  include(CMakePackageConfigHelpers)
  set(brq_package_dir "${CMAKE_INSTALL_LIBDIR}/cmake/TwoBufQueue")
  install(TARGETS TwoBufQueue EXPORT TwoBufQueueTargets
    ARCHIVE DESTINATION "${CMAKE_INSTALL_LIBDIR}"
    LIBRARY DESTINATION "${CMAKE_INSTALL_LIBDIR}"
    RUNTIME DESTINATION "${CMAKE_INSTALL_BINDIR}")
  install(DIRECTORY include/ DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}"
    FILES_MATCHING PATTERN "*.hpp")
  # Install real headers, never build-tree forwarders containing absolute paths.
  install(DIRECTORY src/ DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}/TwoBufQueue/src"
    FILES_MATCHING PATTERN "*.hpp")
  install(EXPORT TwoBufQueueTargets NAMESPACE TwoBufQueue::
    DESTINATION "${brq_package_dir}")
  configure_package_config_file(cmake/TwoBufQueueConfig.cmake.in
    "${CMAKE_CURRENT_BINARY_DIR}/TwoBufQueueConfig.cmake"
    INSTALL_DESTINATION "${brq_package_dir}")
  write_basic_package_version_file("${CMAKE_CURRENT_BINARY_DIR}/TwoBufQueueConfigVersion.cmake"
    VERSION "${PROJECT_VERSION}" COMPATIBILITY SameMinorVersion)
  install(FILES "${CMAKE_CURRENT_BINARY_DIR}/TwoBufQueueConfig.cmake"
    "${CMAKE_CURRENT_BINARY_DIR}/TwoBufQueueConfigVersion.cmake"
    DESTINATION "${brq_package_dir}")
  install(FILES LICENSE NOTICE DESTINATION "${CMAKE_INSTALL_DATADIR}/TwoBufQueue")
endif()
function(brq_warnings target)
  if(MSVC)
    target_compile_options(${target} PRIVATE /W4 /utf-8)
  else()
    target_compile_options(${target} PRIVATE -Wall -Wextra -Werror)
  endif()
endfunction()
brq_warnings(TwoBufQueue)
if(BRQ_BUILD_TESTS OR BRQ_BUILD_EXAMPLES)
  enable_testing()
endif()
if(BRQ_BUILD_TESTS)
  add_executable(test_scan_current test/test_scan_current.cpp)
  target_link_libraries(test_scan_current PRIVATE TwoBufQueue)
  brq_warnings(test_scan_current)
  add_test(NAME scan_current_options COMMAND test_scan_current)
  set_tests_properties(scan_current_options PROPERTIES TIMEOUT 30 LABELS "functional;integration;read_ctrl")
  add_executable(test_sync_writer test/test_sync_writer.cpp)
  target_link_libraries(test_sync_writer PRIVATE TwoBufQueue)
  brq_warnings(test_sync_writer)
  add_test(NAME sync_writer_callbacks COMMAND test_sync_writer)
  set_tests_properties(sync_writer_callbacks PROPERTIES TIMEOUT 30 LABELS "functional;integration")
  add_executable(test_two_buf_queue_options test/test_two_buf_queue_options.cpp)
  target_link_libraries(test_two_buf_queue_options PRIVATE TwoBufQueue)
  brq_warnings(test_two_buf_queue_options)
  add_test(NAME two_buf_queue_output_options COMMAND test_two_buf_queue_options)
  set_tests_properties(two_buf_queue_output_options PROPERTIES TIMEOUT 30 LABELS "functional;integration")
  add_executable(brq_live_handoff_tests test/live_handoff_tests.cpp)
  target_link_libraries(brq_live_handoff_tests PRIVATE TwoBufQueue)
  brq_warnings(brq_live_handoff_tests)
  if(MSVC)
    target_compile_options(brq_live_handoff_tests PRIVATE /UNDEBUG)
  else()
    target_compile_options(brq_live_handoff_tests PRIVATE -UNDEBUG)
  endif()
  foreach(case roles capacity current_snapshot single_pool fifo_concurrent resource_failures finish_publish_window)
    add_test(NAME live_handoff_${case} COMMAND brq_live_handoff_tests ${case})
    set_tests_properties(live_handoff_${case} PROPERTIES TIMEOUT 60 LABELS "functional;live_handoff")
  endforeach()
  set(read_ctrl_targets test_read_item test_read_service test_read_active_completion test_reader_cohesion test_read_notifications test_single_reader)
  foreach(name IN ITEMS writer_queue_tests test_writer test_write_session test_read_item test_read_service test_read_active_completion
      test_reader_cohesion test_read_notifications test_single_reader test_unified_data test_fixed_slice_bitmap test_slice_pool test_slice_release_check)
    add_executable(${name} "test/${name}.cpp")
    target_link_libraries(${name} PRIVATE TwoBufQueue)
    brq_warnings(${name})
    if(MSVC)
      target_compile_options(${name} PRIVATE /UNDEBUG)
    else()
      target_compile_options(${name} PRIVATE -UNDEBUG)
    endif()
    if(NOT name IN_LIST read_ctrl_targets)
      add_test(NAME ${name} COMMAND ${name})
      set_tests_properties(${name} PROPERTIES TIMEOUT 60)
    endif()
  endforeach()
  function(brq_read_cases target group)
    foreach(case IN LISTS ARGN)
      add_test(NAME read_ctrl_${group}_${case} COMMAND ${target} ${case})
      set_tests_properties(read_ctrl_${group}_${case} PROPERTIES TIMEOUT 30 LABELS "functional;read_ctrl;${group}")
    endforeach()
  endfunction()
  brq_read_cases(test_read_item item empty_backoff early_scan data_resets force_and_independent saturation)
  brq_read_cases(test_read_service service empty_records completed_priority completed_rejection current_rejection
    retry_reclassified fixed_prefix current_snapshot scan_timing cancelled_or_expired begin_current_stop
    stop_after_success registration writer_lifetime generation_stats)
  brq_read_cases(test_read_active_completion handoff accepted_current rejected_current prefix_append single_pool_reacquire)
  brq_read_cases(test_reader_cohesion worker immediate_concurrent batched_concurrent single_block_concurrent
    current_without_send notify_retry fast_stop_restart slow_stop_timeout interrupt_slow_stop validation)
  brq_read_cases(test_read_notifications notification selective_and_coalesced completed_before_current
    full_scan_with_notification notify_during_read multiple_words rejected_and_cancelled
    full_scan_rejection_retains_notifications batched_tail_flush worker_flush_and_memory_retry worker_selective_and_restart)
  brq_read_cases(test_single_reader single complete_then_current rejection_and_transfer current_snapshot_and_notification
    cancelled_and_current_rejection accepted_current rejected_current prefix_append immediate_concurrent
    single_pool_batched worker_flush_finish current_without_send notify_retry fast_stop_restart
    slow_stop_timeout interrupt_slow_stop restart_current begin_stop_and_deadline validation)
  set_tests_properties(writer_queue_tests test_unified_data PROPERTIES LABELS "functional;read_ctrl;integration")
endif()
if(BRQ_BUILD_EXAMPLES)
  foreach(name IN ITEMS 01_basic)
    add_executable(brq_${name} "examples/${name}.cpp")
    target_link_libraries(brq_${name} PRIVATE TwoBufQueue)
    brq_warnings(brq_${name})
    add_test(NAME example_${name} COMMAND brq_${name})
    set_tests_properties(example_${name} PROPERTIES TIMEOUT 30)
  endforeach()
  # 子目录示例：components/ 为组件用法，two_buf_queue/ 为 TwoBufQueue 用法。
  foreach(name IN ITEMS 11_warm_pool 12_warm_pool_factory 13_spsc_live_handoff_queue 14_spsc_live_handoff_queue_claim 15_handoff_queue 16_handoff_queue_mpmc)
    add_executable(brq_component_${name} "examples/components/${name}.cpp")
    target_link_libraries(brq_component_${name} PRIVATE TwoBufQueue)
    brq_warnings(brq_component_${name})
    add_test(NAME example_component_${name} COMMAND brq_component_${name})
    set_tests_properties(example_component_${name} PROPERTIES TIMEOUT 30)
  endforeach()
  foreach(name IN ITEMS 02_batch_callback 03_multi_thread_writer 04_layer1_callback 05_seq_writer 06_sync_writer 07_quick_write_muti_msg 08_all_parameters 09_async_logger)
    add_executable(brq_tbq_${name} "examples/two_buf_queue/${name}.cpp")
    target_link_libraries(brq_tbq_${name} PRIVATE TwoBufQueue)
    brq_warnings(brq_tbq_${name})
    add_test(NAME example_tbq_${name} COMMAND brq_tbq_${name})
    set_tests_properties(example_tbq_${name} PROPERTIES TIMEOUT 30)
  endforeach()
  foreach(policy IN ITEMS block drop)
    set(logger_args "${CMAKE_CURRENT_BINARY_DIR}/logger-${policy}.log" --slow-sink)
    if(policy STREQUAL "drop")
      list(APPEND logger_args --drop)
    endif()
    add_test(NAME example_logger_${policy} COMMAND brq_tbq_09_async_logger ${logger_args})
    set_tests_properties(example_logger_${policy} PROPERTIES TIMEOUT 30 LABELS "example;integration")
  endforeach()
endif()

option(BRQ_BUILD_FUNCTIONAL_TESTS "Build detailed contract and functional cases (requires Python)" ON)
if(BRQ_BUILD_TESTS AND BRQ_BUILD_FUNCTIONAL_TESTS)
  add_subdirectory(test/func)
endif()

if(BRQ_BUILD_SPSC_BENCHMARKS)
  include(benchmarks/spsc.cmake)
endif()
