#
# Copyright (c) 2018 CNRS Authors: Joseph Mirabel
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are met:

# 1. Redistributions of source code must retain the above copyright notice, this
#   list of conditions and the following disclaimer.

# 1. Redistributions in binary form must reproduce the above copyright notice,
#   this list of conditions and the following disclaimer in the documentation
#   and/or other materials provided with the distribution.

# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

# ADD_PYTHON_LIBRARY ( module FILES file1.cc file2.cc ... LINK_LIBRARIES
# hpp-core::hpp-core ...)
macro(ADD_PYTHON_LIBRARY MODULE)
    set(options)
    set(oneValueArgs)
    set(multiValueArgs FILES PY_FILES LINK_LIBRARIES)
    cmake_parse_arguments(
        ADD_PYTHON_LIBRARY
        "${options}"
        "${oneValueArgs}"
        "${multiValueArgs}"
        ${ARGN}
    )

    file(MAKE_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/${MODULE})

    set(GENERATED_FILES)
    foreach(F ${ADD_PYTHON_LIBRARY_FILES})
        add_custom_command(
            OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/${F}
            COMMAND
                ${PYTHON_EXECUTABLE}
                ${CMAKE_SOURCE_DIR}/doc/configure.py
                ${CMAKE_CURRENT_SOURCE_DIR}/${F}
                ${CMAKE_CURRENT_BINARY_DIR}/${F}
            DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/${F}
            COMMENT "Generate with documentation for ${F}"
        )
        list(APPEND GENERATED_FILES ${CMAKE_CURRENT_BINARY_DIR}/${F})
    endforeach()

    string(REGEX REPLACE "[/-]" "_" TARGET_NAME "${MODULE}")
    set(LIBNAME "bindings")
    add_library(${TARGET_NAME} SHARED ${GENERATED_FILES})
    set_target_properties(
        ${TARGET_NAME}
        PROPERTIES
            PREFIX ""
            LIBRARY_OUTPUT_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/${MODULE}
            LIBRARY_OUTPUT_NAME ${LIBNAME}
    )

    target_link_boost_python(${TARGET_NAME} PUBLIC)
    target_link_libraries(
        ${TARGET_NAME}
        PUBLIC eigenpy::eigenpy ${ADD_PYTHON_LIBRARY_LINK_LIBRARIES}
    )

    install(TARGETS ${TARGET_NAME} DESTINATION ${PYTHON_SITELIB}/${MODULE})

    # Write __init__.py file
    file(
        WRITE ${CMAKE_CURRENT_BINARY_DIR}/${MODULE}/__init__.py
        "from .${LIBNAME} import *"
    )
    install(
        FILES ${CMAKE_CURRENT_BINARY_DIR}/${MODULE}/__init__.py
        DESTINATION ${PYTHON_SITELIB}/${MODULE}
    )

    set_property(
        GLOBAL
        APPEND
        PROPERTY PYHPP_ALL_BINDING_TARGETS ${TARGET_NAME}
    )

    if(GENERATE_PYTHON_STUBS AND PYBIND11_STUBGEN_EXECUTABLE)
        string(REPLACE "/" "." MODULE_DOT_PATH "${MODULE}")
        set(STUB_OUTPUT_DIR ${CMAKE_BINARY_DIR}/stubs)

        add_custom_command(
            OUTPUT ${STUB_OUTPUT_DIR}/${MODULE}/${LIBNAME}.pyi
            COMMAND
                ${CMAKE_COMMAND} -E env
                "PYTHONPATH=${CMAKE_BINARY_DIR}/src:$ENV{PYTHONPATH}"
                ${PYBIND11_STUBGEN_EXECUTABLE} ${MODULE_DOT_PATH}.${LIBNAME}
                --output-dir ${STUB_OUTPUT_DIR}
            DEPENDS all_pyhpp_bindings
            COMMENT "Generating Python stubs for ${MODULE_DOT_PATH}.${LIBNAME}"
            VERBATIM
        )

        add_custom_target(
            stubs_${TARGET_NAME}
            ALL
            DEPENDS ${STUB_OUTPUT_DIR}/${MODULE}/${LIBNAME}.pyi
        )

        install(
            FILES ${STUB_OUTPUT_DIR}/${MODULE}/${LIBNAME}.pyi
            DESTINATION ${PYTHON_SITELIB}/${MODULE}
        )
    endif()
endmacro()

python_install_on_site(pyhpp __init__.py)

python_install_on_site(pyhpp/pinocchio utils.py)
python_install_on_site(pyhpp/manipulation constraint_graph_factory.py)
python_install_on_site(pyhpp/core static_stability_constraint_factory.py)
python_install_on_site(pyhpp/manipulation security_margins.py)

add_python_library(
  pyhpp/pinocchio
  FILES
  pyhpp/pinocchio/device.cc
  pyhpp/pinocchio/liegroup.cc
  pyhpp/pinocchio/bindings.cc
  LINK_LIBRARIES
  hpp-manipulation::hpp-manipulation
)

add_python_library(
  pyhpp/pinocchio/urdf FILES pyhpp/pinocchio/urdf/util.cc
  pyhpp/pinocchio/urdf/bindings.cc LINK_LIBRARIES hpp-pinocchio::hpp-pinocchio
)

add_python_library(
  pyhpp/constraints
  FILES
  pyhpp/constraints/differentiable-function.cc
  pyhpp/constraints/explicit.cc
  pyhpp/constraints/explicit-constraint-set.cc
  pyhpp/constraints/generic-transformation.cc
  pyhpp/constraints/implicit.cc
  pyhpp/constraints/iterative-solver.cc
  pyhpp/constraints/by-substitution.cc
  pyhpp/constraints/locked-joint.cc
  pyhpp/constraints/relative-com.cc
  pyhpp/constraints/bindings.cc
  LINK_LIBRARIES
  hpp-constraints::hpp-constraints
)

add_python_library(
  pyhpp/core
  FILES
  pyhpp/core/node.cc
  pyhpp/core/roadmap.cc
  pyhpp/core/config-validation.cc
  pyhpp/core/configuration-shooter.cc
  pyhpp/core/constraint.cc
  pyhpp/core/reports.cc
  pyhpp/core/steering-method.cc
  pyhpp/core/path.cc
  pyhpp/core/path-optimizer.cc
  pyhpp/core/path-projector.cc
  pyhpp/core/path-validation.cc
  pyhpp/core/path-planner.cc
  pyhpp/core/connected-component.cc
  pyhpp/core/distance.cc
  pyhpp/core/problem-target.cc
  pyhpp/core/parameter.cc
  pyhpp/core/problem.cc
  pyhpp/core/bindings.cc
  LINK_LIBRARIES
  hpp-manipulation::hpp-manipulation
)

add_python_library(
  pyhpp/core/problem_target FILES
  pyhpp/core/problem_target/goal-configurations.cc
  pyhpp/core/problem_target/bindings.cc LINK_LIBRARIES hpp-core::hpp-core
)

add_python_library(
  pyhpp/core/path
  FILES
  pyhpp/core/path/spline.cc
  pyhpp/core/path/vector.cc
  pyhpp/core/path/bindings.cc
  LINK_LIBRARIES
  hpp-core::hpp-core
)

add_python_library(
  pyhpp/core/path_optimization FILES
  pyhpp/core/path_optimization/spline-gradient-based-abstract.cc
  pyhpp/core/path_optimization/bindings.cc LINK_LIBRARIES hpp-core::hpp-core
)

add_python_library(
  pyhpp/manipulation
  FILES
  pyhpp/manipulation/bindings.cc
  pyhpp/manipulation/device.cc
  pyhpp/manipulation/graph.cc
  pyhpp/manipulation/problem.cc
  pyhpp/manipulation/path-planner.cc
  pyhpp/manipulation/path-planner.hh
  pyhpp/manipulation/path-projector.cc
  pyhpp/manipulation/steering-method.cc
  pyhpp/manipulation/path-optimizer.cc
  LINK_LIBRARIES
  hpp-manipulation::hpp-manipulation
)

add_python_library(
  pyhpp/manipulation/steering_method
  FILES
  pyhpp/manipulation/steering_method/cartesian.cc
  pyhpp/manipulation/steering_method/bindings.cc
  LINK_LIBRARIES
  hpp-manipulation::hpp-manipulation
)

add_python_library(
  pyhpp/manipulation/urdf FILES pyhpp/manipulation/urdf/util.cc
  pyhpp/manipulation/urdf/bindings.cc LINK_LIBRARIES
  hpp-manipulation-urdf::hpp-manipulation-urdf
)

get_property(PYHPP_ALL_BINDINGS GLOBAL PROPERTY PYHPP_ALL_BINDING_TARGETS)
add_custom_target(all_pyhpp_bindings DEPENDS ${PYHPP_ALL_BINDINGS})

# Install tool submodule
python_install_on_site(pyhpp/tools __init__.py)
python_install_on_site(pyhpp/tools xacro.py)
python_install_on_site(pyhpp/tools constraint_error.py)
