#
# Copyright (c) 2024 Qontrol Authors
#

# --- PYTHON TARGET --- #
set(PYWRAP ${PROJECT_NAME}_pywrap)
set(PYWRAP
    ${PYWRAP}
    PARENT_SCOPE)

function(INSTALL_PYTHON_FILES)
  set(options)
  set(oneValueArgs MODULE)
  set(multiValueArgs FILES)
  cmake_parse_arguments(ARGS "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN})

  string(TOLOWER ${PROJECT_NAME} PROJECT_NAME_LOWER)
  set(SOURCE_PATH ${PROJECT_NAME_LOWER})
  
  if(SKBUILD)
    # In scikit-build-core context, we are in bindings/python, so files are in qontrol/
    # BUT wait, if we are building from root, CMAKE_CURRENT_SOURCE_DIR is bindings/python
    # default PROJECT_NAME is Qontrol. 
    # The files are at bindings/python/qontrol/
    # so SOURCE_PATH should be qontrol
    set(SOURCE_PATH "qontrol")
  endif()
  
  set(INSTALL_PATH ${QONTROL_PYTHON_INSTALL_DIR})
  if(ARGS_MODULE)
    set(SOURCE_PATH ${SOURCE_PATH}/${ARGS_MODULE})
    set(INSTALL_PATH ${INSTALL_PATH}/${ARGS_MODULE})
  endif()

  foreach(f ${ARGS_FILES})
    # Copy to build directory for in-tree use
    # configure_file(${SOURCE_PATH}/${f} ${PROJECT_BINARY_DIR}/bindings/python/${SOURCE_PATH}/${f} COPYONLY)
     configure_file(${SOURCE_PATH}/${f} ${CMAKE_CURRENT_BINARY_DIR}/${SOURCE_PATH}/${f} COPYONLY)
    install(FILES ${SOURCE_PATH}/${f} DESTINATION ${INSTALL_PATH} COMPONENT python)
  endforeach()
endfunction()

if(BUILD_PYTHON_INTERFACE)
  message(STATUS "Building Python bindings with nanobind")
  
  add_custom_target(${PROJECT_NAME}-python)
  set_target_properties(${PROJECT_NAME}-python PROPERTIES EXCLUDE_FROM_DEFAULT_BUILD False)

  # When building from root, the target name logic in root CMake might conflict if we are not careful,
  # but here we define a custom target.
  
  # Note: When building via scikit-build-core from root, SKBUILD is defined.
  
  # Ensure we have Python
  find_package(Python COMPONENTS Interpreter Development.Module REQUIRED)
  
  # Setup Python installation directory
  if(SKBUILD)
    # When using scikit-build-core, install into the package directory
    set(QONTROL_PYTHON_INSTALL_DIR "qontrol")
  else()
    string(TOLOWER ${PROJECT_NAME} PROJECT_NAME_LOWER)
    # FORCE relative install path for colcon/ROS 2 compatibility
    set(QONTROL_PYTHON_INSTALL_DIR "lib/python${Python_VERSION_MAJOR}.${Python_VERSION_MINOR}/site-packages/${PROJECT_NAME_LOWER}")
  endif()
  
  message(STATUS "Python install directory: ${QONTROL_PYTHON_INSTALL_DIR}")

  # Fetch nanobind
  include(FetchContent)
  FetchContent_Declare(
    nanobind
    GIT_REPOSITORY https://github.com/wjakob/nanobind.git
    GIT_TAG v2.0.0
  )
  FetchContent_MakeAvailable(nanobind)

  # Python module
  nanobind_add_module(
    ${PYWRAP}
    STABLE_ABI
    NB_STATIC
    
    # Main binding file
    src/main.cpp
    
    # Model bindings
    src/model/model_bindings.cpp

    # Problem bindings
    src/problem/control_output_bindings.cpp
    src/problem/joint_velocity_bindings.cpp
    src/problem/joint_acceleration_bindings.cpp
    src/problem/joint_torque_bindings.cpp
    src/problem/problem_bindings.cpp
    src/problem/cartesian_mpc_bindings.cpp
    src/problem/resolution_strategy_bindings.cpp
    src/problem/ghc_bindings.cpp
    
    # Task bindings
    src/tasks/task_bindings.cpp
    src/tasks/generic_task_bindings.cpp
    src/tasks/task_set_bindings.cpp
    
    # Constraint bindings
    src/constraints/constraint_bindings.cpp
    src/constraints/generic_constraint_bindings.cpp
    src/constraints/constraint_set_bindings.cpp
    src/constraints/cartesian_plane_bindings.cpp
    
    # Slack bindings
    src/slack/slack_bindings.cpp
    src/slack/slack_constraint_bindings.cpp
    src/slack/slack_set_bindings.cpp
    
    # Solver bindings
    src/solver/solver_bindings.cpp
    src/solver/generic_solver_bindings.cpp
    
    # Utils bindings
    src/utils/utils_bindings.cpp
  )

  # Set module properties
  string(TOLOWER ${PROJECT_NAME} PROJECT_NAME_LOWER)
  set_target_properties(
    ${PYWRAP}
    PROPERTIES 
      PREFIX ""
      OUTPUT_NAME "_qontrol_impl"
      LIBRARY_OUTPUT_DIRECTORY "${PROJECT_BINARY_DIR}/bindings/python/${PROJECT_NAME_LOWER}"
      RUNTIME_OUTPUT_DIRECTORY "${PROJECT_BINARY_DIR}/bindings/python/${PROJECT_NAME_LOWER}")

  # Link against Qontrol library
  target_link_libraries(${PYWRAP} PRIVATE Qontrol::Qontrol)

  # Set RPATH on Unix (only for non-wheel builds where we have absolute paths)
  if(UNIX AND IS_ABSOLUTE "${QONTROL_PYTHON_INSTALL_DIR}")
    # Calculate relative path from Python install dir to library install dir
    file(RELATIVE_PATH PYWRAP_INSTALL_RPATH 
         "${QONTROL_PYTHON_INSTALL_DIR}" 
         "${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_LIBDIR}")
    set_target_properties(${PYWRAP} PROPERTIES 
      INSTALL_RPATH "\$ORIGIN/${PYWRAP_INSTALL_RPATH}")
  endif()

  add_dependencies(${PROJECT_NAME}-python ${PYWRAP})

  # Install the Python module
  install(
    TARGETS ${PYWRAP}
    LIBRARY DESTINATION ${QONTROL_PYTHON_INSTALL_DIR}
    RUNTIME DESTINATION ${QONTROL_PYTHON_INSTALL_DIR}
    COMPONENT python
  )

  # Install Python files
  install_python_files(FILES __init__.py)
  
  # Install stub files if they exist
  string(TOLOWER ${PROJECT_NAME} PROJECT_NAME_LOWER)
  if(EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/${PROJECT_NAME_LOWER}/__init__.pyi")
    install(FILES 
      ${CMAKE_CURRENT_SOURCE_DIR}/${PROJECT_NAME_LOWER}/__init__.pyi 
      DESTINATION ${QONTROL_PYTHON_INSTALL_DIR}
      COMPONENT python
    )
  endif()
  
  if(EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/${PROJECT_NAME_LOWER}/py.typed")
    install(FILES 
      ${CMAKE_CURRENT_SOURCE_DIR}/${PROJECT_NAME_LOWER}/py.typed 
      DESTINATION ${QONTROL_PYTHON_INSTALL_DIR}
      COMPONENT python
    )
  endif()

  # --- PACKAGING --- #
  set(_PKG_CONFIG_PYWRAP_LIBDIR ${QONTROL_PYTHON_INSTALL_DIR})
  set(_PKG_CONFIG_PYWRAP_BINDIR ${QONTROL_PYTHON_INSTALL_DIR})
  set(_PKG_CONFIG_PYWRAP_REQUIRES "${PROJECT_NAME}")
  set(_PKG_CONFIG_PYWRAP_LIBS "-L\${libdir} -l${PYWRAP}")
  set(_PKG_CONFIG_PYWRAP_CFLAGS "-I\${includedir}")
  set(_PKG_CONFIG_PYWRAP_CFLAGS "${_PKG_CONFIG_PYWRAP_CFLAGS} -I${Python_INCLUDE_DIRS}")

  configure_file("${CMAKE_CURRENT_SOURCE_DIR}/qontrolpy.pc.cmake"
                 "${CMAKE_CURRENT_BINARY_DIR}/qontrolpy.pc")

  install(
    FILES "${CMAKE_CURRENT_BINARY_DIR}/qontrolpy.pc"
    DESTINATION ${CMAKE_INSTALL_LIBDIR}/pkgconfig
    PERMISSIONS OWNER_READ GROUP_READ WORLD_READ OWNER_WRITE)

  # Development install target - copies to site-packages for immediate testing
  string(TOLOWER ${PROJECT_NAME} PROJECT_NAME_LOWER)
  add_custom_target(install_dev
    COMMAND ${CMAKE_COMMAND} -E echo "Installing to Python site-packages for development..."
    COMMAND ${Python_EXECUTABLE} -c "import site; import shutil; import os; sp = site.getsitepackages()[0]; os.makedirs(os.path.join(sp, '${PROJECT_NAME_LOWER}'), exist_ok=True); shutil.copy('$<TARGET_FILE:${PYWRAP}>', os.path.join(sp, '${PROJECT_NAME_LOWER}', '_qontrol_impl$<TARGET_FILE_SUFFIX:${PYWRAP}>'));  py_init = os.path.join('${CMAKE_CURRENT_SOURCE_DIR}', '${PROJECT_NAME_LOWER}', '__init__.py'); shutil.copy(py_init, os.path.join(sp, '${PROJECT_NAME_LOWER}'))"
    DEPENDS ${PYWRAP}
    COMMENT "Installing Python module to site-packages"
    VERBATIM
  )

endif(BUILD_PYTHON_INTERFACE)


