# Copyright 2020 Intermodalics BVBA
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

if(NOT COMMAND string_camel_case_to_lower_case_underscore)
  find_package(rosidl_cmake REQUIRED)
  include("${rosidl_cmake_DIR}/string_camel_case_to_lower_case_underscore.cmake")
endif()

#
# Generate RTT typekits from ROS IDL .msg, .srv and .action files
# and the C++ headers and introspection libraries generated by packages
# rosidl_generator_cpp and rtt_ros2_idl.
#
function(_rtt_ros2_generate_typekit _package)
  cmake_parse_arguments(ARG "" "BUILD_TYPE;TARGET;EXPORT" "MESSAGES;SERVICES;ACTIONS;EXCLUDE_MESSAGES;EXCLUDE_SERVICES;EXCLUDE_ACTIONS" ${ARGN})

  # Find the requested package if it was not found before
  if(NOT ${_package}_FOUND)
    find_package(${_package} REQUIRED)
  endif()

  # The target name of the typekit library
  set(_target "${ARG_TARGET}")

  # The export set
  if(ARG_EXPORT)
    set(_export ${ARG_EXPORT})
  else()
    set(_export ${_target})
  endif()

  # Set CMAKE_BUILD_TYPE
  if(ARG_BUILD_TYPE)
    set(CMAKE_BUILD_TYPE ${ARG_BUILD_TYPE})
  elseif(NOT CMAKE_BUILD_TYPE)
    set(CMAKE_BUILD_TYPE MinSizeRel)
    message(STATUS "[rtt_ros2_idl] Setting CMAKE_BUILD_TYPE to ${CMAKE_BUILD_TYPE} for target ${_target}")
  endif()

  # Add plugin dependencies (other typekit packages)
  # Run-time dependencies of ${_package} become RTT plugin dependencies of the generated typekit
  # package.
  rtt_ros2_export_plugin_depend(rtt_ros2_primitives_typekit)
  ament_index_get_resource(${_package}_RUN_DEPENDENCIES "package_run_dependencies" "${_package}")
  ament_index_get_resources(_interface_packages "rosidl_interfaces")
  foreach(_dep ${${_package}_RUN_DEPENDENCIES})
    # Does package ${_dep} provides interfaces? If yes, add plugin dependency rtt_ros2_${_dep}.
    if(_dep IN_LIST _interface_packages)
      rtt_ros2_export_plugin_depend(rtt_ros2_${_dep})
    endif()
  endforeach()
  set(${PROJECT_NAME}_EXEC_DEPENDS "${${PROJECT_NAME}_EXEC_DEPENDS}" PARENT_SCOPE)
  set(${PROJECT_NAME}_RTT_ROS2_PLUGIN_DEPENDS "${${PROJECT_NAME}_RTT_ROS2_PLUGIN_DEPENDS}" PARENT_SCOPE)

  # Find all (selected) types in the given package.
  set(_include_dir "${${_package}_DIR}/../../../include/${_package}")
  normalize_path(_include_dir "${_include_dir}")
  set(_output_dir "${CMAKE_CURRENT_BINARY_DIR}/rtt_ros2_idl/${_package}/typekit")
  set(_messages "")
  set(_services "")
  set(_actions "")
  set(_cpp_headers "")
  set(_generated_header_files "")
  set(_generated_source_files "")
  foreach(_file ${${_package}_INTERFACE_FILES})
    get_filename_component(_path "${_file}" DIRECTORY)
    get_filename_component(_extension "${_file}" EXT)
    get_filename_component(_type "${_file}" NAME_WE)
    string_camel_case_to_lower_case_underscore("${_type}" _header_name)
    if(_path STREQUAL "msg" AND _extension STREQUAL ".msg")
      if((NOT DEFINED ARG_MESSAGES OR ${_type} IN_LIST ARG_MESSAGES)
          AND NOT ${_type} IN_LIST ARG_EXCLUDE_MESSAGES)
        list(APPEND _messages ${_type})
        list(APPEND _cpp_headers "${_include_dir}/msg/${_header_name}.hpp")
        list(APPEND _generated_header_files "${_output_dir}/msg/${_header_name}_Types.hpp")
        list(APPEND _generated_source_files "${_output_dir}/msg/${_header_name}_typekit.cpp")
      endif()
    endif()
    if(_path STREQUAL "srv" AND _extension STREQUAL ".srv")
      if((NOT DEFINED ARG_SERVICES OR ${_type} IN_LIST ARG_SERVICES)
          AND NOT ${_type} IN_LIST ARG_EXCLUDE_SERVICES)
        list(APPEND _services ${_type})
        list(APPEND _cpp_headers "${_include_dir}/srv/${_header_name}.hpp")
        list(APPEND _generated_header_files "${_output_dir}/srv/${_header_name}_Types.hpp")
        list(APPEND _generated_source_files "${_output_dir}/srv/${_header_name}_typekit.cpp")
      endif()
    endif()
    if(_path STREQUAL "action" AND _extension STREQUAL ".action")
      if((NOT DEFINED ARG_ACTIONS OR ${_type} IN_LIST ARG_ACTIONS)
          AND NOT ${_type} IN_LIST ARG_EXCLUDE_ACTIONS)
        list(APPEND _actions ${_type})
        list(APPEND _cpp_headers "${_include_dir}/action/${_header_name}.hpp")
        list(APPEND _generated_header_files "${_output_dir}/action/${_header_name}_Types.hpp")
        list(APPEND _generated_source_files "${_output_dir}/action/${_header_name}_typekit.cpp")
      endif()
    endif()
  endforeach()
  list(APPEND _generated_header_files "${_output_dir}/Types.hpp")
  list(APPEND _generated_source_files "${_output_dir}/typekit_plugin.cpp")

  # Generate source and header files
  add_custom_command(
    OUTPUT ${_generated_header_files} ${_generated_source_files}
    COMMAND ${PYTHON_EXECUTABLE} -m rtt_ros2_idl
      --package "${_package}"
      --messages ${_messages}
      --services ${_services}
      --actions ${_actions}
      --output-dir "${_output_dir}"
    DEPENDS
      ${rtt_ros2_idl_GENERATOR_FILES}
      "${rtt_ros2_idl_TEMPLATE_DIR}/msg_Types.hpp.em"
      "${rtt_ros2_idl_TEMPLATE_DIR}/msg_typekit.cpp.em"
      "${rtt_ros2_idl_TEMPLATE_DIR}/Types.hpp.em"
      "${rtt_ros2_idl_TEMPLATE_DIR}/typekit_plugin.cpp.em"
      "${_cpp_headers}"
    COMMENT "Generating RTT typekit plugin for ${_package}"
    VERBATIM
  )

  # Build the typekit library target
  orocos_typekit(${_target}
    ${_generated_header_files}
    ${_generated_source_files}
    EXPORT ${_export}
    INCLUDES DESTINATION include/orocos
  )
  target_include_directories(${_target}
    PRIVATE
      ${_output_dir}
  )
  ament_target_dependencies(${_target}
    ${_package}
    rtt_ros2_idl
  )

  # Install headers
  install(
    DIRECTORY ${_output_dir}/
    DESTINATION include/orocos/${_package}/typekit
    FILES_MATCHING PATTERN "*.hpp"
  )
  ament_export_include_directories(include/orocos)

  # Set variables in PARENT_SCOPE necessary for .pc file generation (orocos_generate_package())
  set(OROCOS_DEFINED_TYPES ${OROCOS_DEFINED_TYPES} PARENT_SCOPE)
  set(${PROJECT_NAME}_EXPORTED_LIBRARIES ${${PROJECT_NAME}_EXPORTED_LIBRARIES} PARENT_SCOPE)
  set(${PROJECT_NAME}_EXPORTED_LIBRARY_DIRS ${${PROJECT_NAME}_EXPORTED_LIBRARY_DIRS} PARENT_SCOPE)
endfunction()

macro(rtt_ros2_generate_typekit _package)
  cmake_parse_arguments(ARG "" "TARGET" "" ${ARGN})

  # Target name defaults to ${_package}_typekit.
  if(NOT ARG_TARGET)
    set(_target "rtt_${_package}_typekit")
  else()
    set(_target ${ARG_TARGET})
  endif()

  _rtt_ros2_generate_typekit(${_package}
    TARGET ${_target}
    ${ARG_UNPARSED_ARGUMENTS}
  )

  # Export dependencies, include directories and interface
  if(TARGET ${_target})
    ament_export_dependencies(${_package})
    ament_export_dependencies(rtt_ros2_idl)
    ament_export_include_directories(include/orocos)
    if(COMMAND ament_export_targets)
      ament_export_targets(${_target} HAS_LIBRARY_TARGET)
    else()
      ament_export_interfaces(${_target} HAS_LIBRARY_TARGET)
    endif()
  endif()
endmacro()
