cmake_minimum_required(VERSION 3.16)
project(musebox-client-cpp VERSION 1.1.4 LANGUAGES CXX)

set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)

if(NOT CMAKE_BUILD_TYPE)
  set(CMAKE_BUILD_TYPE Release CACHE STRING "Build type" FORCE)
endif()

# ── Shared MuseBox helpers (header-only) ─────────────────────────────────────
set(MUSEBOX_COMMON_DIR "${CMAKE_CURRENT_SOURCE_DIR}/../../musebox-common" CACHE PATH
    "Path to the musebox-common folder of the SDK")
get_filename_component(MUSEBOX_COMMON_DIR "${MUSEBOX_COMMON_DIR}" ABSOLUTE)
if(NOT EXISTS "${MUSEBOX_COMMON_DIR}/cpp/CMakeLists.txt")
  message(FATAL_ERROR "musebox-common not found at ${MUSEBOX_COMMON_DIR} (set -DMUSEBOX_COMMON_DIR=<path>)")
endif()
add_subdirectory("${MUSEBOX_COMMON_DIR}/cpp" musebox_common)

# ── Dependencies ─────────────────────────────────────────────────────────────
find_package(OpenCV REQUIRED)
find_package(Boost 1.74 REQUIRED COMPONENTS system thread)
find_package(Threads REQUIRED)
# ZeroMQ, mosquitto and rabbitmq-c: system packages on Linux / macOS, vcpkg on Windows
# (-DCMAKE_TOOLCHAIN_FILE=<vcpkg>/scripts/buildsystems/vcpkg.cmake).
if(WIN32)
  # vcpkg names the ZeroMQ library after its version (libzmq-mt-4_3_5.lib): use its CMake package
  find_package(ZeroMQ CONFIG REQUIRED)
  if(TARGET libzmq)
    set(ZMQ_LIB libzmq)
  else()
    set(ZMQ_LIB libzmq-static)
  endif()
else()
  find_library(ZMQ_LIB zmq REQUIRED)
endif()
find_library(MOSQUITTO_LIB NAMES mosquitto libmosquitto REQUIRED)
find_library(RABBITMQ_LIB NAMES rabbitmq rabbitmq.4 librabbitmq.4 REQUIRED)
find_path(ZMQ_INCLUDE_DIR zmq.h)
find_path(MOSQUITTO_INCLUDE_DIR mosquitto.h REQUIRED)
find_path(RABBITMQ_INCLUDE_DIR amqp.h REQUIRED)

# nlohmann/json: prefer an installed package, fetch otherwise
find_package(nlohmann_json 3.9 QUIET)
if(NOT nlohmann_json_FOUND)
  include(FetchContent)
  FetchContent_Declare(
    nlohmann_json
    # Header-only release archive (~110 KB) instead of a git clone of the full repo (~24 MB)
    URL      https://github.com/nlohmann/json/releases/download/v3.11.3/json.tar.xz
    URL_HASH SHA256=d6c65aca6b1ed68e7a182f4757257b107ae403032760ed6ef121c9d55e81757d
    DOWNLOAD_EXTRACT_TIMESTAMP TRUE
  )
  FetchContent_MakeAvailable(nlohmann_json)
endif()

# ── Optional / auto-detected ROS 2 support ───────────────────────────────────
option(ENABLE_ROS2 "Enable ROS 2 client support" ON)

set(ROS2_SOURCES "")
set(ROS2_LIBS "")
set(ROS2_RPATH_DIR "")

if(ENABLE_ROS2)
  if(DEFINED ENV{AMENT_PREFIX_PATH})
    if(WIN32)
      set(_ament_prefixes "$ENV{AMENT_PREFIX_PATH}")   # already ;-separated (C:\ has a colon)
    else()
      string(REPLACE ":" ";" _ament_prefixes "$ENV{AMENT_PREFIX_PATH}")
    endif()
    list(GET _ament_prefixes 0 _ros2_prefix)
    list(APPEND CMAKE_PREFIX_PATH ${_ament_prefixes})
    set(ROS2_RPATH_DIR "${_ros2_prefix}/lib")
  elseif(NOT WIN32)
    foreach(_distro jazzy iron humble)
      if(EXISTS "/opt/ros/${_distro}")
        set(ENV{AMENT_PREFIX_PATH} "/opt/ros/${_distro}")
        list(APPEND CMAKE_PREFIX_PATH "/opt/ros/${_distro}")
        set(ROS2_RPATH_DIR "/opt/ros/${_distro}/lib")
        message(STATUS "ROS 2 environment not sourced; using /opt/ros/${_distro}")
        break()
      endif()
    endforeach()
  endif()

  find_package(rclcpp QUIET)
  find_package(std_msgs QUIET)

  if(rclcpp_FOUND AND std_msgs_FOUND)
    message(STATUS "ROS 2 found: enabling ROS 2 client support")
    list(APPEND ROS2_SOURCES src/ros2_client.cpp)
    list(APPEND ROS2_LIBS rclcpp::rclcpp ${std_msgs_TARGETS})
  else()
    message(STATUS "ROS 2 (rclcpp / std_msgs) not found: building without ROS 2 client support")
  endif()
endif()

# ── Target ───────────────────────────────────────────────────────────────────
set(SOURCES
  src/client_base.cpp
  src/flow_parser.cpp
  src/websocket_client.cpp
  src/zmq_client.cpp
  src/mqtt_client.cpp
  src/rabbitmq_client.cpp
  src/pipeline_runner.cpp
  src/main.cpp
  ${ROS2_SOURCES}
)

add_executable(${PROJECT_NAME} ${SOURCES})

target_include_directories(${PROJECT_NAME} PRIVATE
  ${CMAKE_CURRENT_SOURCE_DIR}/include
  ${OpenCV_INCLUDE_DIRS}
  ${MOSQUITTO_INCLUDE_DIR}
  ${RABBITMQ_INCLUDE_DIR}
)
if(ZMQ_INCLUDE_DIR)
  target_include_directories(${PROJECT_NAME} PRIVATE ${ZMQ_INCLUDE_DIR})
endif()

# Compiler settings shared by the targets
function(musebox_client_settings target)
  if(MSVC)
    # /bigobj: Boost.Beast; Windows 10 API for Asio; no min/max macros (std::min, cv::min)
    target_compile_options(${target} PRIVATE /W3 /bigobj /utf-8)
    target_compile_definitions(${target} PRIVATE _WIN32_WINNT=0x0A00 NOMINMAX WIN32_LEAN_AND_MEAN _CRT_SECURE_NO_WARNINGS)
  else()
    target_compile_options(${target} PRIVATE -Wall -Wextra $<$<CONFIG:Release>:-O2>)
  endif()
  if(WIN32)
    target_link_libraries(${target} PRIVATE ws2_32 mswsock)
  endif()
endfunction()
musebox_client_settings(${PROJECT_NAME})
target_compile_definitions(${PROJECT_NAME} PRIVATE
  MUSEBOX_COMMON_DIR="${MUSEBOX_COMMON_DIR}"
  MUSEBOX_CLIENT_VERSION="${PROJECT_VERSION}"
)
if(ROS2_SOURCES)
  target_compile_definitions(${PROJECT_NAME} PRIVATE MUSEBOX_ENABLE_ROS2=1)
endif()

target_link_libraries(${PROJECT_NAME} PRIVATE
  musebox_common
  ${OpenCV_LIBS}
  ${ZMQ_LIB}
  ${MOSQUITTO_LIB}
  ${RABBITMQ_LIB}
  Boost::system
  Boost::thread
  Threads::Threads
  nlohmann_json::nlohmann_json
  ${ROS2_LIBS}
)

if(ROS2_SOURCES AND ROS2_RPATH_DIR AND NOT WIN32)
  set_target_properties(${PROJECT_NAME} PROPERTIES
    BUILD_RPATH "${ROS2_RPATH_DIR}"
    INSTALL_RPATH "${ROS2_RPATH_DIR}"
    INSTALL_RPATH_USE_LINK_PATH TRUE
    LINK_FLAGS "-Wl,--disable-new-dtags"
  )
endif()

# ── ROS 2 client test (only when ROS 2 is available) ─────────────────────────
if(ROS2_SOURCES)
  add_executable(test_ros2_client
    tests/test_ros2_client.cpp
    src/ros2_client.cpp
    src/client_base.cpp
  )
  target_include_directories(test_ros2_client PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/include ${OpenCV_INCLUDE_DIRS})
  musebox_client_settings(test_ros2_client)
  target_compile_definitions(test_ros2_client PRIVATE MUSEBOX_ENABLE_ROS2=1)
  target_link_libraries(test_ros2_client PRIVATE
    musebox_common
    ${OpenCV_LIBS}
    Threads::Threads
    nlohmann_json::nlohmann_json
    ${ROS2_LIBS}
  )
  if(ROS2_RPATH_DIR AND NOT WIN32)
    set_target_properties(test_ros2_client PROPERTIES
      BUILD_RPATH "${ROS2_RPATH_DIR}"
      INSTALL_RPATH "${ROS2_RPATH_DIR}"
      INSTALL_RPATH_USE_LINK_PATH TRUE
      LINK_FLAGS "-Wl,--disable-new-dtags"
    )
  endif()
endif()

install(TARGETS ${PROJECT_NAME} DESTINATION bin)
