cmake_minimum_required(VERSION 3.24)
project(gdsdiff_native LANGUAGES CXX)

if(DEFINED GDS_GPU_COMPARE_BUILD_TESTS AND NOT DEFINED GDSDIFF_BUILD_TESTS)
    set(GDSDIFF_BUILD_TESTS ${GDS_GPU_COMPARE_BUILD_TESTS})
endif()
if(DEFINED GDS_GPU_COMPARE_BUILD_PYTHON AND NOT DEFINED GDSDIFF_BUILD_PYTHON)
    set(GDSDIFF_BUILD_PYTHON ${GDS_GPU_COMPARE_BUILD_PYTHON})
endif()
if(DEFINED GDS_GPU_COMPARE_ENABLE_CUDA AND NOT DEFINED GDSDIFF_ENABLE_CUDA)
    set(GDSDIFF_ENABLE_CUDA ${GDS_GPU_COMPARE_ENABLE_CUDA})
endif()
if(DEFINED GDS_GPU_COMPARE_CUDA_ARCHITECTURES AND NOT DEFINED GDSDIFF_CUDA_ARCHITECTURES)
    set(GDSDIFF_CUDA_ARCHITECTURES ${GDS_GPU_COMPARE_CUDA_ARCHITECTURES})
endif()

option(GDSDIFF_BUILD_TESTS "Build native tests" OFF)
option(GDSDIFF_BUILD_PYTHON "Build optional Python extensions" OFF)
option(GDSDIFF_ENABLE_CUDA "Build CUDA components" OFF)

add_library(gds_compare_core STATIC core.cpp)
target_compile_features(gds_compare_core PUBLIC cxx_std_20)
target_include_directories(gds_compare_core PUBLIC ${CMAKE_CURRENT_SOURCE_DIR})

if(GDSDIFF_BUILD_TESTS)
    add_executable(gds_compare_core_test test_core.cpp)
    target_link_libraries(gds_compare_core_test PRIVATE gds_compare_core)
endif()

if(GDSDIFF_BUILD_PYTHON)
    find_package(Python COMPONENTS Interpreter Development.Module REQUIRED)
    find_package(pybind11 CONFIG REQUIRED)
    pybind11_add_module(_gds_compare_core python_bindings.cpp)
    target_link_libraries(_gds_compare_core PRIVATE gds_compare_core)
    set_target_properties(_gds_compare_core PROPERTIES
        CXX_VISIBILITY_PRESET hidden
        VISIBILITY_INLINES_HIDDEN ON
    )
endif()

if(GDSDIFF_ENABLE_CUDA)
    enable_language(CUDA)
    find_package(CUDAToolkit REQUIRED)
    if(GDSDIFF_CUDA_ARCHITECTURES)
        set(CMAKE_CUDA_ARCHITECTURES ${GDSDIFF_CUDA_ARCHITECTURES})
    else()
        set(CMAKE_CUDA_ARCHITECTURES native)
    endif()
    add_library(gds_compare_cuda_assignment STATIC cuda_assignment.cu)
    target_link_libraries(gds_compare_cuda_assignment PRIVATE CUDA::cudart gds_compare_core)
    target_include_directories(gds_compare_cuda_assignment PUBLIC ${CMAKE_CURRENT_SOURCE_DIR})
    add_library(gds_compare_cuda_candidates STATIC cuda_candidates.cu)
    target_link_libraries(gds_compare_cuda_candidates PRIVATE gds_compare_cuda_assignment CUDA::cudart gds_compare_core)
    target_include_directories(gds_compare_cuda_candidates PUBLIC ${CMAKE_CURRENT_SOURCE_DIR})
    add_library(gds_compare_cuda_memory STATIC cuda_memory.cu)
    target_link_libraries(gds_compare_cuda_memory PRIVATE gds_compare_cuda_candidates CUDA::cudart gds_compare_core)
    target_include_directories(gds_compare_cuda_memory PUBLIC ${CMAKE_CURRENT_SOURCE_DIR})
    if(GDSDIFF_BUILD_TESTS)
        add_executable(gds_compare_cuda_assignment_test test_cuda_assignment.cu)
        target_link_libraries(gds_compare_cuda_assignment_test PRIVATE gds_compare_cuda_assignment CUDA::cudart)
        add_executable(gds_compare_cuda_candidates_test test_cuda_candidates.cu)
        target_link_libraries(gds_compare_cuda_candidates_test PRIVATE gds_compare_cuda_candidates CUDA::cudart)
        add_executable(gds_compare_cuda_memory_test test_cuda_memory.cu)
        target_link_libraries(gds_compare_cuda_memory_test PRIVATE gds_compare_cuda_memory CUDA::cudart)
    endif()
    if(EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/cuda_bindings.cu)
        find_package(Python COMPONENTS Interpreter Development.Module REQUIRED)
        find_package(pybind11 CONFIG REQUIRED)
        pybind11_add_module(_gds_compare_cuda cuda_bindings.cu)
        target_link_libraries(_gds_compare_cuda PRIVATE CUDA::cudart gds_compare_core gds_compare_cuda_assignment gds_compare_cuda_candidates gds_compare_cuda_memory)
    else()
        message(STATUS "CUDA assignment kernels enabled; Python CUDA binding source is not present yet")
    endif()
endif()
