cmake_minimum_required(VERSION 3.16)

# RM 是独立机械臂子工程，和仓库根目录的 LinkerHand 灵巧手工程分开构建。
# 这样后续更换机械臂品牌时，不会影响灵巧手 SDK 的 CMake 目标。
project(RMJsonControl
    VERSION 0.1.0
    LANGUAGES CXX
    DESCRIPTION "Standalone JSON-command RealMan robotic arm control project"
)

set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_CXX_EXTENSIONS OFF)

option(RM_BUILD_TESTING "Build RM JSON command parser tests" ON)

# core 库只包含 JSON 解析和命令执行流程，不链接任何真实硬件 SDK。
# 这样 parser/runner 的单元测试可以在没有机械臂的机器上运行。
add_library(rm_json_core
    src/config/simple_json.cpp
    src/config/json_command.cpp
    src/core/json_command_runner.cpp
)

target_include_directories(rm_json_core
    PUBLIC
        ${CMAKE_CURRENT_SOURCE_DIR}/include
)

target_compile_features(rm_json_core PUBLIC cxx_std_17)

add_library(rm_trajectory_core
    src/modules/trajectory/trajectory_file_store.cpp
    src/core/drag_teach_recorder.cpp
)

target_include_directories(rm_trajectory_core
    PUBLIC
        ${CMAKE_CURRENT_SOURCE_DIR}/include
)

target_compile_features(rm_trajectory_core PUBLIC cxx_std_17)
target_compile_definitions(rm_trajectory_core
    PUBLIC
        RM_PROJECT_ROOT="${CMAKE_CURRENT_SOURCE_DIR}"
)

add_executable(rm_trajectory_convert
    src/trajectory_convert_main.cpp
)
target_link_libraries(rm_trajectory_convert
    PRIVATE
        rm_trajectory_core
)

set(REALMAN_RM_API2_ROOT "" CACHE PATH "Path to RealMan RM_API2 C++ SDK root, for example /home/mashiro/RM_API2/C++")
set(REALMAN_ARM_ROOT "")
set(REALMAN_ARM_INCLUDE_DIR "")
set(REALMAN_ARM_LIB_DIR "")
set(REALMAN_ARM_LIB "")

set(REALMAN_ARM_CANDIDATE_ROOTS)
if(REALMAN_RM_API2_ROOT)
    list(APPEND REALMAN_ARM_CANDIDATE_ROOTS ${REALMAN_RM_API2_ROOT})
endif()
list(APPEND REALMAN_ARM_CANDIDATE_ROOTS
    /home/mashiro/RM_API2/C++
    ${CMAKE_CURRENT_LIST_DIR}/../third_party/Robotic_Arm
)
list(REMOVE_DUPLICATES REALMAN_ARM_CANDIDATE_ROOTS)

foreach(CANDIDATE_ROOT ${REALMAN_ARM_CANDIDATE_ROOTS})
    # 兼容本机 SDK 路径和仓库 third_party 备份路径。
    if(REALMAN_ARM_LIB)
        break()
    endif()

    set(CANDIDATE_INCLUDE_DIR ${CANDIDATE_ROOT}/include)
    if(NOT EXISTS ${CANDIDATE_INCLUDE_DIR}/rm_interface.h)
        continue()
    endif()

    set(CANDIDATE_LIB_DIRS)
    if(CMAKE_SYSTEM_PROCESSOR MATCHES "x86_64|amd64|AMD64")
        file(GLOB CANDIDATE_RELEASE_LIB_DIRS CONFIGURE_DEPENDS ${CANDIDATE_ROOT}/linux/linux_x86_c++_vv*)
        file(GLOB CANDIDATE_DEBUG_LIB_DIRS CONFIGURE_DEPENDS ${CANDIDATE_ROOT}/linux/linux_x86_c++_debug*)
        list(APPEND CANDIDATE_LIB_DIRS
            ${CANDIDATE_RELEASE_LIB_DIRS}
            ${CANDIDATE_DEBUG_LIB_DIRS}
            ${CANDIDATE_ROOT}/linux/lib
        )
    elseif(CMAKE_SYSTEM_PROCESSOR MATCHES "aarch64|arm64")
        file(GLOB CANDIDATE_RELEASE_LIB_DIRS CONFIGURE_DEPENDS ${CANDIDATE_ROOT}/linux/linux_arm64_c++_vv*)
        file(GLOB CANDIDATE_DEBUG_LIB_DIRS CONFIGURE_DEPENDS ${CANDIDATE_ROOT}/linux/linux_arm64_c++_debug*)
        list(APPEND CANDIDATE_LIB_DIRS
            ${CANDIDATE_RELEASE_LIB_DIRS}
            ${CANDIDATE_DEBUG_LIB_DIRS}
            ${CANDIDATE_ROOT}/linux/lib
        )
    else()
        file(GLOB CANDIDATE_LIB_DIRS CONFIGURE_DEPENDS
            ${CANDIDATE_ROOT}/linux/*c++*
            ${CANDIDATE_ROOT}/linux/lib
        )
    endif()

    unset(REALMAN_ARM_LIB_CANDIDATE CACHE)
    find_library(REALMAN_ARM_LIB_CANDIDATE
        NAMES api_cpp libapi_cpp
        PATHS ${CANDIDATE_LIB_DIRS}
        NO_DEFAULT_PATH
    )

    if(REALMAN_ARM_LIB_CANDIDATE)
        set(REALMAN_ARM_ROOT ${CANDIDATE_ROOT})
        set(REALMAN_ARM_INCLUDE_DIR ${CANDIDATE_INCLUDE_DIR})
        get_filename_component(REALMAN_ARM_LIB_DIR ${REALMAN_ARM_LIB_CANDIDATE} DIRECTORY)
        set(REALMAN_ARM_LIB ${REALMAN_ARM_LIB_CANDIDATE})
    endif()
endforeach()

if(REALMAN_ARM_LIB AND EXISTS ${REALMAN_ARM_INCLUDE_DIR}/rm_interface.h)
    # 不同版本 RM_API2 的销毁函数可能拼写不同：
    # 有的版本是 rm_destroy，有的版本是 rm_destory。
    # 这里在配置阶段读取头文件，给 C++ 代码注入对应宏。
    file(READ ${REALMAN_ARM_INCLUDE_DIR}/rm_interface.h REALMAN_INTERFACE_HEADER)
    if(REALMAN_INTERFACE_HEADER MATCHES "rm_destroy")
        set(REALMAN_DESTROY_COMPILE_DEFINITION REALMAN_RM_API_HAS_RM_DESTROY=1)
    else()
        set(REALMAN_DESTROY_COMPILE_DEFINITION REALMAN_RM_API_HAS_RM_DESTORY=1)
    endif()

    add_library(realman_api_cpp SHARED IMPORTED GLOBAL)
    set_target_properties(realman_api_cpp PROPERTIES
        IMPORTED_LOCATION ${REALMAN_ARM_LIB}
        INTERFACE_INCLUDE_DIRECTORIES ${REALMAN_ARM_INCLUDE_DIR}
        INTERFACE_COMPILE_DEFINITIONS ${REALMAN_DESTROY_COMPILE_DEFINITION}
    )

    add_library(rm_realman_driver
        src/drivers/realman/realman_robot_arm.cpp
    )
    target_link_libraries(rm_realman_driver
        PUBLIC
            rm_json_core
            realman_api_cpp
        PRIVATE
            pthread
    )

    add_executable(rm_json_runner
        src/main.cpp
    )
    target_link_libraries(rm_json_runner
        PRIVATE
            rm_json_core
            rm_realman_driver
    )
    set_target_properties(rm_json_runner PROPERTIES
        BUILD_RPATH ${REALMAN_ARM_LIB_DIR}
        INSTALL_RPATH ${REALMAN_ARM_LIB_DIR}
    )

    add_executable(rm_drag_teach_record
        src/drag_teach_record_main.cpp
    )
    target_link_libraries(rm_drag_teach_record
        PRIVATE
            rm_trajectory_core
            rm_realman_driver
    )
    set_target_properties(rm_drag_teach_record PROPERTIES
        BUILD_RPATH ${REALMAN_ARM_LIB_DIR}
        INSTALL_RPATH ${REALMAN_ARM_LIB_DIR}
    )

    message(STATUS "Found RealMan RM_API2 root: ${REALMAN_ARM_ROOT}")
    message(STATUS "Found RealMan RM_API2 library: ${REALMAN_ARM_LIB}")
    message(STATUS "Found RealMan RM_API2 headers: ${REALMAN_ARM_INCLUDE_DIR}")
else()
    message(WARNING "RealMan RM_API2 library or headers not found; rm_json_runner will not be built.")
endif()

if(RM_BUILD_TESTING)
    # 当前测试只测 JSON/命令解析，不访问真实机械臂。
    enable_testing()
    add_executable(rm_json_command_tests
        tests/test_json_command_parser.cpp
    )
    target_link_libraries(rm_json_command_tests
        PRIVATE
            rm_json_core
    )
    add_test(NAME rm_json_command_tests COMMAND rm_json_command_tests)

    add_executable(rm_trajectory_file_store_tests
        tests/test_trajectory_file_store.cpp
    )
    target_link_libraries(rm_trajectory_file_store_tests
        PRIVATE
            rm_trajectory_core
    )
    target_compile_definitions(rm_trajectory_file_store_tests
        PRIVATE
            RM_TEST_OUTPUT_DIR="${CMAKE_CURRENT_BINARY_DIR}/test_outputs"
    )
    add_test(NAME rm_trajectory_file_store_tests COMMAND rm_trajectory_file_store_tests)
endif()
