#CMakeLists.txt

#A) 声明 project/languages、
#B) 找 Python/nanobind/CUDA、
#C) 添加子目录。

cmake_minimum_required(VERSION 3.26...3.31)

option(OPTFUNC_WITH_CUDA
        "Build native cone CUDA code instead of CPU-only stubs"
        OFF
)

# 会让 CMake/NVCC 生成对应 GPU 架构代码。
# Do not default to "native": CNB release builds and non-GPU Linux builders may
# compile CUDA code without a visible GPU. Keep this list portable; local
# performance builds can override CMAKE_CUDA_ARCHITECTURES explicitly.
if (OPTFUNC_WITH_CUDA AND NOT DEFINED CMAKE_CUDA_ARCHITECTURES AND NOT DEFINED ENV{CUDAARCHS})
  set(CMAKE_CUDA_ARCHITECTURES "75;80;86;89;90" CACHE STRING
          "CUDA architectures for optfuncs native cone wheels"
  )
endif()

if (OPTFUNC_WITH_CUDA)
  project(optfuncs_native LANGUAGES CXX CUDA)
else()
  project(optfuncs_native LANGUAGES CXX)
endif()

# MSVC
if (MSVC)
  add_compile_options("$<$<COMPILE_LANGUAGE:CXX>:/utf-8>")
  add_compile_options("$<$<COMPILE_LANGUAGE:CUDA>:-Xcompiler=/utf-8>")
endif()

# scikit-build-core 调用 CMake 时会定义 SKBUILD。
# CLion 直接打开工程时 SKBUILD 不存在，但仍可作为普通 CMake 工程 configure。
if (NOT SKBUILD)
  message(STATUS
          "This CMake project is intended to be driven by scikit-build-core. "
          "Direct CMake/CLion mode is supported for IDE indexing and native debugging."
  )
endif()

if (NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES)
  set(CMAKE_BUILD_TYPE Release CACHE STRING "Build type" FORCE)
  set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS
          Debug Release RelWithDebInfo MinSizeRel
  )
endif()

set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)

if (OPTFUNC_WITH_CUDA)
  set(CMAKE_CUDA_STANDARD 17)
  set(CMAKE_CUDA_STANDARD_REQUIRED ON)
endif()

set(CMAKE_POSITION_INDEPENDENT_CODE ON)
if (OPTFUNC_WITH_CUDA)
  set(CMAKE_CUDA_SEPARABLE_COMPILATION ON)
endif()

# CLion / direct CMake configure may not inherit uv's Python shim. If the
# caller did not choose a Python executable, prefer the project .venv because it
# carries nanobind/scikit-build dev dependencies from pyproject.toml.
if (NOT DEFINED Python_EXECUTABLE)
  if (WIN32 AND EXISTS "${CMAKE_SOURCE_DIR}/.venv/Scripts/python.exe")
    set(Python_EXECUTABLE
            "${CMAKE_SOURCE_DIR}/.venv/Scripts/python.exe"
            CACHE FILEPATH "Python executable for direct CMake/CLion configure"
    )
  elseif (EXISTS "${CMAKE_SOURCE_DIR}/.venv/bin/python")
    set(Python_EXECUTABLE
            "${CMAKE_SOURCE_DIR}/.venv/bin/python"
            CACHE FILEPATH "Python executable for direct CMake/CLion configure"
    )
  endif()
endif()

find_package(Python 3.12 COMPONENTS Interpreter Development.Module REQUIRED)

# CLion 直接跑 CMake 时，用当前 Python 环境查询 nanobind 的 CMake config。
# scikit-build-core 驱动时通常已有 build-system.requires 提供 nanobind。
if (NOT SKBUILD AND NOT nanobind_DIR)
  execute_process(
          COMMAND "${Python_EXECUTABLE}" -m nanobind --cmake_dir
          OUTPUT_VARIABLE _nanobind_cmake_dir
          OUTPUT_STRIP_TRAILING_WHITESPACE
          RESULT_VARIABLE _nanobind_cmake_dir_result
  )

  if (_nanobind_cmake_dir_result EQUAL 0 AND EXISTS "${_nanobind_cmake_dir}")
    set(nanobind_DIR "${_nanobind_cmake_dir}" CACHE PATH "nanobind CMake config directory")
    message(STATUS "nanobind_DIR inferred from Python: ${nanobind_DIR}")
  else()
    message(FATAL_ERROR
            "nanobind is not importable from Python_EXECUTABLE=${Python_EXECUTABLE}. "
            "Run: uv sync --group dev"
    )
  endif()
endif()

find_package(nanobind CONFIG REQUIRED)
if (OPTFUNC_WITH_CUDA)
  find_package(CUDAToolkit REQUIRED)
endif()
find_package(OpenMP COMPONENTS CXX)

# Host BLAS is optional. Native cone code may use it for dense linear algebra
# paths, but the package must still build with the pure C++/OpenMP fallback when
# no BLAS is available. Use OPTFUNC_BLAS=OPENBLAS or SYSTEM to make absence a
# configure-time error for local performance builds.
set(OPTFUNC_BLAS "AUTO" CACHE STRING
        "Host BLAS selection for native cones: AUTO, OFF, SYSTEM, or OPENBLAS"
)
set_property(CACHE OPTFUNC_BLAS PROPERTY STRINGS AUTO OFF SYSTEM OPENBLAS)
string(TOUPPER "${OPTFUNC_BLAS}" OPTFUNC_BLAS_UPPER)
if (OPTFUNC_BLAS_UPPER STREQUAL "OFF")
  message(STATUS "optfunc native cones: host BLAS disabled")
elseif (OPTFUNC_BLAS_UPPER STREQUAL "OPENBLAS")
  set(BLA_VENDOR OpenBLAS)
  find_package(BLAS REQUIRED)
elseif (OPTFUNC_BLAS_UPPER STREQUAL "SYSTEM")
  find_package(BLAS REQUIRED)
elseif (OPTFUNC_BLAS_UPPER STREQUAL "AUTO")
  find_package(BLAS QUIET)
else()
  message(FATAL_ERROR
          "OPTFUNC_BLAS must be one of AUTO, OFF, SYSTEM, or OPENBLAS; got ${OPTFUNC_BLAS}"
  )
endif()

# CUDA is mandatory for Stage 2 validation, but cuBLAS use remains a selectable
# native backend policy so future CUDA dense helpers can be benchmarked against
# custom kernels without changing Python APIs.
option(OPTFUNC_USE_CUBLAS
        "Link cuBLAS for native cone CUDA dense-helper implementations"
        ON
)
option(OPTFUNC_USE_CUSOLVER
        "Link cuSolver for native cone CUDA PSD large-dimension matrix powers"
        ON
)

add_subdirectory(src/optfunc/cvxs/cones/cpp)
