cmake_minimum_required(VERSION 3.15)
project(covalent_base LANGUAGES CXX)

option(
  REFORGE_BUILD_LEGACY_TORCH_SHAPER
  "Build the deprecated LibTorch-backed shaper_interface Python extension"
  OFF
)

if(POLICY CMP0148)
  cmake_policy(SET CMP0148 NEW)
endif()

set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)

# Disable LTO/IPO to avoid link-time issues with mixed static/shared pybind targets
set(CMAKE_INTERPROCEDURAL_OPTIMIZATION OFF)

# manylinux_2_28 currently provides Eigen 3.3.x; with GCC 14 this emits
# deprecation warnings from std::unary_negate/binary_negate in Eigen headers.
if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU" AND CMAKE_CXX_COMPILER_VERSION VERSION_GREATER_EQUAL 14)
  add_compile_options(-Wno-deprecated-declarations)
endif()

# ---------------------------------------------------------------------------
# Dependency location configuration
# ---------------------------------------------------------------------------

set(ROBOT_DEPS_ROOT "" CACHE PATH
    "Root directory that contains manually built third-party libraries (cnpy, iir1)")
if(DEFINED ENV{ROBOT_DEPS_ROOT} AND NOT ROBOT_DEPS_ROOT)
  set(ROBOT_DEPS_ROOT "$ENV{ROBOT_DEPS_ROOT}" CACHE PATH
      "Root directory that contains manually built third-party libraries (cnpy, iir1)" FORCE)
endif()

if(NOT ROBOT_DEPS_ROOT)
  set(_robot_default_deps "$ENV{HOME}/opt/robot-deps")
  if(EXISTS "${_robot_default_deps}")
    set(ROBOT_DEPS_ROOT "${_robot_default_deps}")
  endif()
endif()

if(REFORGE_BUILD_LEGACY_TORCH_SHAPER)
  set(ROBOT_LIBTORCH_ROOT "" CACHE PATH
      "Path to the libtorch installation (directory that contains share/cmake/Torch)")
  if(DEFINED ENV{ROBOT_LIBTORCH_ROOT} AND NOT ROBOT_LIBTORCH_ROOT)
    set(ROBOT_LIBTORCH_ROOT "$ENV{ROBOT_LIBTORCH_ROOT}" CACHE PATH
        "Path to the libtorch installation (directory that contains share/cmake/Torch)" FORCE)
  endif()
  if(NOT ROBOT_LIBTORCH_ROOT AND ROBOT_DEPS_ROOT)
    if(EXISTS "${ROBOT_DEPS_ROOT}/libtorch")
      set(ROBOT_LIBTORCH_ROOT "${ROBOT_DEPS_ROOT}/libtorch")
    endif()
  endif()
endif()

macro(_stb_append_prefix)
  foreach(_dir IN LISTS ARGN)
    if(_dir AND EXISTS "${_dir}")
      list(APPEND CMAKE_PREFIX_PATH "${_dir}")
    endif()
  endforeach()
endmacro()

if(ROBOT_DEPS_ROOT)
  _stb_append_prefix(
    "${ROBOT_DEPS_ROOT}"
    "${ROBOT_DEPS_ROOT}/lib"
    "${ROBOT_DEPS_ROOT}/lib64"
    "${ROBOT_DEPS_ROOT}/lib/cmake"
    "${ROBOT_DEPS_ROOT}/share/cmake")
  message(STATUS "ROBOT_DEPS_ROOT: ${ROBOT_DEPS_ROOT}")
endif()

if(REFORGE_BUILD_LEGACY_TORCH_SHAPER AND ROBOT_LIBTORCH_ROOT)
  _stb_append_prefix(
    "${ROBOT_LIBTORCH_ROOT}"
    "${ROBOT_LIBTORCH_ROOT}/lib"
    "${ROBOT_LIBTORCH_ROOT}/lib64"
    "${ROBOT_LIBTORCH_ROOT}/share/cmake"
    "${ROBOT_LIBTORCH_ROOT}/share/cmake/Torch")
  if(EXISTS "${ROBOT_LIBTORCH_ROOT}/share/cmake/Torch/TorchConfig.cmake")
    set(Torch_DIR "${ROBOT_LIBTORCH_ROOT}/share/cmake/Torch" CACHE PATH
        "Directory containing TorchConfig.cmake" FORCE)
  endif()
  message(STATUS "ROBOT_LIBTORCH_ROOT: ${ROBOT_LIBTORCH_ROOT}")
endif()

list(REMOVE_DUPLICATES CMAKE_PREFIX_PATH)

set(PYBIND11_FINDPYTHON ON CACHE BOOL "Use CMake FindPython for pybind11" FORCE)

if(NOT Python3_Interpreter_FOUND)
  find_package(Python3 COMPONENTS Interpreter QUIET)
endif()
find_package(Python COMPONENTS Interpreter Development.Module QUIET)
if(Python3_Interpreter_FOUND)
  execute_process(
    COMMAND ${Python3_EXECUTABLE} -c "import pybind11; print(pybind11.get_cmake_dir())"
    OUTPUT_VARIABLE _pybind11_cmake_dir
    OUTPUT_STRIP_TRAILING_WHITESPACE
    ERROR_QUIET
  )
  if(_pybind11_cmake_dir AND EXISTS "${_pybind11_cmake_dir}/pybind11Config.cmake")
    set(pybind11_DIR "${_pybind11_cmake_dir}" CACHE PATH
        "pybind11 CMake directory detected from Python" FORCE)
  endif()
endif()

find_package(pybind11 QUIET)
if(NOT pybind11_FOUND)
  message(STATUS "pybind11 not found; Python modules will not be built")
endif()

if(REFORGE_BUILD_LEGACY_TORCH_SHAPER)
  # The compatibility target may use Torch from the active Python environment.
  if(NOT Torch_DIR AND Python3_Interpreter_FOUND)
    execute_process(
      COMMAND ${Python3_EXECUTABLE} -c "import importlib.util, pathlib; spec=importlib.util.find_spec('torch'); print((pathlib.Path(spec.origin).parent/'share/cmake/Torch').as_posix() if spec and spec.origin else '')"
      OUTPUT_VARIABLE _torch_cmake_from_python
      OUTPUT_STRIP_TRAILING_WHITESPACE
    )
    if(_torch_cmake_from_python AND EXISTS "${_torch_cmake_from_python}/TorchConfig.cmake")
      set(Torch_DIR "${_torch_cmake_from_python}" CACHE PATH
          "Directory containing TorchConfig.cmake (auto-detected from Python torch)" FORCE)
      list(APPEND CMAKE_PREFIX_PATH "${_torch_cmake_from_python}")
      message(STATUS "Torch CMake dir auto-detected from Python: ${_torch_cmake_from_python}")
    endif()
  endif()

  find_package(Torch QUIET)
  if(NOT Torch_FOUND AND ROBOT_LIBTORCH_ROOT)
    find_package(Torch QUIET
                 PATHS
                   "${ROBOT_LIBTORCH_ROOT}"
                   "${ROBOT_LIBTORCH_ROOT}/share/cmake/Torch"
                 NO_DEFAULT_PATH)
  endif()

  if(NOT Torch_FOUND)
    message(FATAL_ERROR
      "REFORGE_BUILD_LEGACY_TORCH_SHAPER=ON requires LibTorch. Install the "
      "supported torch==2.3.1 package in the active Python environment or set "
      "Torch_DIR to the directory containing TorchConfig.cmake."
    )
  endif()
  message(STATUS "Torch found; linking against: ${TORCH_LIBRARIES}")
endif()

find_package(Eigen3 QUIET)

add_subdirectory(control)
