# cpp/okvis2/CMakeLists.txt — OKVIS2 wrapper for C1 VIO (AZ-332).
#
# Builds the vendored OKVIS2 upstream (cpp/okvis2/upstream/, git submodule)
# plus a pybind11 binding that exposes the estimator to the Python facade
# at src/gps_denied_onboard/components/c1_vio/okvis2.py.
#
# Gating: BUILD_OKVIS2=ON only on linux production binaries (deployment +
# research matrix kinds in .github/workflows/ci.yml). macOS dev builds
# default BUILD_OKVIS2=OFF; unit tests use a fake pybind11 binding fixture
# installed at sys.modules boundary (tests/unit/c1_vio/conftest.py).
#
# Bundled OKVIS2 deps (DBoW2, brisk, ceres-solver, opengv) are NOT pulled
# into this clone — see ci.yml step that installs them via apt
# (libceres-dev libsuitesparse-dev etc.) and the USE_SYSTEM_* flags below.

if(NOT BUILD_OKVIS2)
  return()
endif()

message(STATUS "[okvis2] BUILD_OKVIS2=ON — building OKVIS2 upstream + pybind11 binding")

# Tell OKVIS2 to use system-installed dependencies instead of its bundled
# external/ submodules (which we do not initialise — saves ~hundreds of MB
# and matches the Linux apt-deps approach in ci.yml).
set(USE_SYSTEM_BRISK ON CACHE BOOL "AZ-332: use apt libbrisk-dev" FORCE)
set(USE_SYSTEM_DBOW2 ON CACHE BOOL "AZ-332: use apt libdbow2-dev" FORCE)
set(USE_SYSTEM_CERES ON CACHE BOOL "AZ-332: use apt libceres-dev" FORCE)

# Trim OKVIS2's build surface — we link the estimator libs only.
set(BUILD_APPS OFF CACHE BOOL "AZ-332: skip OKVIS2 demo apps" FORCE)
set(BUILD_TESTS OFF CACHE BOOL "AZ-332: skip OKVIS2 gtests" FORCE)
set(BUILD_ROS2 OFF CACHE BOOL "AZ-332: ROS 2 rejected at Plan time (D-C1-1-SUB-A)" FORCE)
set(HAVE_LIBREALSENSE OFF CACHE BOOL "AZ-332: no realsense pipeline" FORCE)
set(USE_NN OFF CACHE BOOL "AZ-332: drop LibTorch dep (keyframe arch OK per Fact #39)" FORCE)
set(DO_TIMING OFF CACHE BOOL "AZ-332: disable per-frame timing prints" FORCE)
set(BUILD_SHARED_LIBS OFF CACHE BOOL "AZ-332: link OKVIS as static into the .so" FORCE)

# pybind11 (vendored at cpp/pybind11/upstream/) — guarded so a sibling
# native binding (gtsam_bindings, faiss_index) cannot double-add the
# subdirectory.
if(NOT TARGET pybind11::module)
  add_subdirectory(
    ${CMAKE_SOURCE_DIR}/cpp/pybind11/upstream
    ${CMAKE_BINARY_DIR}/pybind11_build
  )
endif()

# Vendored OKVIS2 upstream — EXCLUDE_FROM_ALL keeps unused targets out of
# the default build graph; we depend on the okvis_* libs we explicitly
# link below.
add_subdirectory(upstream EXCLUDE_FROM_ALL)

# pybind11 binding source — per module-layout.md rule #4 the binding code
# lives next to the Python facade, not under cpp/.
set(OKVIS2_BINDING_SRC
  ${CMAKE_SOURCE_DIR}/src/gps_denied_onboard/components/c1_vio/_native/okvis2_binding.cpp
)

pybind11_add_module(okvis2_binding ${OKVIS2_BINDING_SRC})

# OKVIS2 export targets — exact list confirmed by walking upstream
# CMakeLists in cpp/okvis2/upstream/okvis_*/. If a target name changes
# upstream, the linker error on first CI run pinpoints which one.
target_link_libraries(okvis2_binding
  PRIVATE
    okvis_ceres
    okvis_frontend
    okvis_multisensor_processing
    okvis_kinematics
    okvis_cv
    okvis_common
    okvis_time
    okvis_util
)

target_compile_features(okvis2_binding PRIVATE cxx_std_17)

# Install the .so next to the Python facade so the lazy import inside
# okvis2.py (`from . import _native; _native.okvis2_binding`) resolves at
# runtime without a sys.path shim.
install(TARGETS okvis2_binding
        LIBRARY DESTINATION
          ${CMAKE_INSTALL_LIBDIR}/gps_denied_onboard/components/c1_vio/_native/
)
