cmake_minimum_required(VERSION 3.8)
project(win_ubuntu_bridge)

if(CMAKE_COMPILER_IS_GNUCXX OR CMAKE_CXX_COMPILER_ID MATCHES "Clang")
  add_compile_options(-Wall -Wextra -Wpedantic)
endif()

# ==========================================================
# 1. 寻找 ROS 2 核心库与消息生成器
# ==========================================================
find_package(ament_cmake REQUIRED)
find_package(rclcpp REQUIRED)
find_package(rclcpp_components REQUIRED)
find_package(rclcpp_action REQUIRED)
find_package(std_msgs REQUIRED)
find_package(std_srvs REQUIRED)
find_package(action_msgs REQUIRED)
find_package(rosidl_default_generators REQUIRED)
find_package(behaviortree_cpp_v3 REQUIRED)

# ==========================================================
# 2. 编译 ROS 2 自定义接口 (Msg / Srv / Action)
# ==========================================================
# 🚨 已经严格按照你截图中的目录结构为你写好
set(interface_files
  "msg/HardwareState.msg"
  "msg/ControlTelemetry.msg"
  "msg/TrajectoryPoint.msg"
  "msg/CameraIntrinsic.msg"
  "msg/SensorExtrinsic.msg"

  # chassis
  "srv/CommitKinematics.srv"
  "srv/ExecuteRawDrive.srv"
  "srv/ExecuteRawSteer.srv"
  "srv/SetDiagnosticMode.srv"

  # control
  "srv/ExecuteOpenLoopCmd.srv"
  "srv/ExecuteStepResponse.srv"
  "srv/FollowTestTrajectory.srv"
  "srv/InjectTuningParams.srv"
  "srv/SetControlMode.srv"
  
  # sensor
  "srv/CommitCalibrationResults.srv"
  "srv/MoveToObservationPose.srv"
  "srv/TriggerSyncCapture.srv"

  "action/DownloadSensorData.action"
)

rosidl_generate_interfaces(${PROJECT_NAME}
  ${interface_files}
  DEPENDENCIES std_msgs std_srvs action_msgs
)

# ==========================================================
# 3. 寻找系统底层的 gRPC 并编译 .proto 文件
# ==========================================================
find_package(Protobuf REQUIRED)      
find_package(PkgConfig REQUIRED)     
pkg_check_modules(GRPC REQUIRED IMPORTED_TARGET grpc++) 

set(PROTO_DIR "${CMAKE_CURRENT_SOURCE_DIR}/proto")
file(GLOB PROTO_FILES "${PROTO_DIR}/*.proto") 
set(PROTO_OUT_DIR "${CMAKE_CURRENT_BINARY_DIR}/grpc_gen")
file(MAKE_DIRECTORY ${PROTO_OUT_DIR})

find_program(GRPC_CPP_PLUGIN_EXECUTABLE grpc_cpp_plugin REQUIRED)

set(PROTO_SRCS "")
set(PROTO_HDRS "")
foreach(PROTO_FILE ${PROTO_FILES})
    get_filename_component(FIL_WE ${PROTO_FILE} NAME_WE)
    list(APPEND PROTO_SRCS "${PROTO_OUT_DIR}/${FIL_WE}.pb.cc" "${PROTO_OUT_DIR}/${FIL_WE}.grpc.pb.cc")
    list(APPEND PROTO_HDRS "${PROTO_OUT_DIR}/${FIL_WE}.pb.h" "${PROTO_OUT_DIR}/${FIL_WE}.grpc.pb.h")
    
    add_custom_command(
        OUTPUT "${PROTO_OUT_DIR}/${FIL_WE}.pb.cc" "${PROTO_OUT_DIR}/${FIL_WE}.pb.h"
               "${PROTO_OUT_DIR}/${FIL_WE}.grpc.pb.cc" "${PROTO_OUT_DIR}/${FIL_WE}.grpc.pb.h"
        COMMAND ${Protobuf_PROTOC_EXECUTABLE}
        "--experimental_allow_proto3_optional"
        "--proto_path=${PROTO_DIR}"
        "--cpp_out=${PROTO_OUT_DIR}" 
        "--grpc_out=${PROTO_OUT_DIR}"
        "--plugin=protoc-gen-grpc=${GRPC_CPP_PLUGIN_EXECUTABLE}"
        "${PROTO_FILE}"
        DEPENDS "${PROTO_FILE}"
        COMMENT "🚀 正在翻译 ${FIL_WE}.proto 到 C++ 源码..."
    )
endforeach()

# 打包 gRPC 协议库
add_library(${PROJECT_NAME}_grpc_lib SHARED ${PROTO_SRCS})
target_include_directories(${PROJECT_NAME}_grpc_lib PUBLIC "${PROTO_OUT_DIR}")
target_link_libraries(${PROJECT_NAME}_grpc_lib PkgConfig::GRPC protobuf::libprotobuf)

# ==========================================================
# 4. 编译三大 Gateway 组件 (ROS 2 Plugins)
# ==========================================================
# 💡 定义一个宏来简化三个网关的编译代码，让 CMake 极度整洁！
macro(build_gateway_component target_name source_file plugin_class)
    add_library(${target_name} SHARED ${source_file})
    target_include_directories(${target_name} PUBLIC 
        "$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>"
        "$<BUILD_INTERFACE:${PROTO_OUT_DIR}>"
    )
    ament_target_dependencies(${target_name} rclcpp rclcpp_components rclcpp_action std_msgs std_srvs action_msgs)
    target_link_libraries(${target_name} ${PROJECT_NAME}_grpc_lib)
    
    # 🚨 极度关键：因为接口在同一个包里，必须让 C++ 代码等待 Msg 编译完成后才能编译！
    rosidl_target_interfaces(${target_name} ${PROJECT_NAME} "rosidl_typesupport_cpp")

    rclcpp_components_register_node(${target_name}
        PLUGIN "${plugin_class}"
        EXECUTABLE ${target_name}_exe 
    )
endmacro()

# ⚠️ 注意：基于你截图的拼写 gatways。如果你改名了，记得同步改掉这里的路径！
build_gateway_component(chassis_gateway_node "gateways/chassis_gateway_node.cpp" "win_ubuntu_bridge::ChassisGatewayNode")
build_gateway_component(control_gateway_node "gateways/control_gateway_node.cpp" "win_ubuntu_bridge::ControlGatewayNode")
build_gateway_component(sensor_gateway_node "gateways/sensor_gateway_node.cpp" "win_ubuntu_bridge::SensorGatewayNode")

# ==========================================================
# 5. 编译其他主节点 (master_node 和 brain_node)
# ==========================================================
add_executable(master_node src/master_node.cpp) 
target_include_directories(master_node PUBLIC "$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>" "${PROTO_OUT_DIR}")
ament_target_dependencies(master_node rclcpp behaviortree_cpp_v3)
target_link_libraries(master_node ${PROJECT_NAME}_grpc_lib)
rosidl_target_interfaces(master_node ${PROJECT_NAME} "rosidl_typesupport_cpp")

add_executable(brain_node src/brain_node.cpp) 
target_include_directories(brain_node PUBLIC "$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>" "${PROTO_OUT_DIR}")
ament_target_dependencies(brain_node rclcpp)
target_link_libraries(brain_node ${PROJECT_NAME}_grpc_lib)
rosidl_target_interfaces(brain_node ${PROJECT_NAME} "rosidl_typesupport_cpp")

# ==========================================================
# 6. 安装规则 (Install)
# ==========================================================
install(TARGETS 
    ${PROJECT_NAME}_grpc_lib
    chassis_gateway_node chassis_gateway_node_exe
    control_gateway_node control_gateway_node_exe
    sensor_gateway_node sensor_gateway_node_exe
    master_node brain_node
  ARCHIVE DESTINATION lib
  LIBRARY DESTINATION lib/
  RUNTIME DESTINATION lib/${PROJECT_NAME}
)

install(DIRECTORY proto/ DESTINATION share/${PROJECT_NAME}/proto)
# 若有 launch 或 behavior_trees 文件，随时解开下面这行的注释
install(DIRECTORY launch/ config/ DESTINATION share/${PROJECT_NAME}/launch)

ament_package()