cmake_minimum_required(VERSION 3.24)
project(vtour_mlp_lab LANGUAGES CXX)

# This project is configured only by the independent build-mlp entrypoints.
option(MLP_CUDA "Build the copied Matrix CUDA backend" OFF)
option(MLP_BUILD_TESTS "Build independent Matrix checks" OFF)
if(MLP_CUDA)
    if(NOT DEFINED CMAKE_CUDA_ARCHITECTURES)
        set(CMAKE_CUDA_ARCHITECTURES native CACHE STRING "CUDA GPU architectures")
    endif()
    enable_language(CUDA)
    find_package(CUDAToolkit 11.0 REQUIRED)
    message(STATUS "MLP CUDA toolkit: ${CUDAToolkit_VERSION} (${CUDAToolkit_BIN_DIR})")
    message(STATUS "MLP CUDA compiler: ${CMAKE_CUDA_COMPILER}; host: ${CMAKE_CUDA_HOST_COMPILER}")
    message(STATUS "MLP CUDA architectures: ${CMAKE_CUDA_ARCHITECTURES}")
endif()

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

add_library(mlp_matrix OBJECT core/Matrix.cpp)
target_compile_features(mlp_matrix PUBLIC cxx_std_17)
target_include_directories(mlp_matrix PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}")
set_target_properties(mlp_matrix PROPERTIES POSITION_INDEPENDENT_CODE ON
    CXX_EXTENSIONS OFF CXX_VISIBILITY_PRESET hidden VISIBILITY_INLINES_HIDDEN YES)
if(MLP_CUDA)
    target_sources(mlp_matrix PRIVATE core/MatrixCUDA.cu core/MemoryManager.cu)
    target_compile_definitions(mlp_matrix PUBLIC USE_CUDA USE_CUBLAS)
    target_link_libraries(mlp_matrix PUBLIC CUDA::cudart CUDA::cublas)
    set_target_properties(mlp_matrix PROPERTIES CUDA_STANDARD 17 CUDA_STANDARD_REQUIRED ON
        CUDA_EXTENSIONS OFF CUDA_RUNTIME_LIBRARY Shared)
endif()

add_library(mlp SHARED playground.cpp)
target_link_libraries(mlp PRIVATE mlp_matrix)
set_target_properties(mlp PROPERTIES PREFIX "" OUTPUT_NAME "mlp" CXX_EXTENSIONS OFF
    CXX_VISIBILITY_PRESET hidden VISIBILITY_INLINES_HIDDEN YES)
if(MLP_CUDA)
    # Preserve the exact selected Toolkit without redistributing its DLLs.
    # Windows loader policy ignores PATH; Lua adds these directories only for
    # the duration of a native session. CUDA releases may use bin or bin/x64.
    file(TO_CMAKE_PATH "${CUDAToolkit_BIN_DIR}" mlp_cuda_bin)
    string(REPLACE "\"" "\\\"" mlp_cuda_bin "${mlp_cuda_bin}")
    set(mlp_runtime "{\"cuda\":true,\"toolkit\":\"${CUDAToolkit_VERSION}\",\"runtime_paths\":[\"${mlp_cuda_bin}\"")
    if(IS_DIRECTORY "${CUDAToolkit_BIN_DIR}/x64")
        string(APPEND mlp_runtime ",\"${mlp_cuda_bin}/x64\"")
    endif()
    string(APPEND mlp_runtime "]}\n")
    set_target_properties(mlp PROPERTIES INSTALL_RPATH_USE_LINK_PATH TRUE)
else()
    set(mlp_runtime "{\"cuda\":false,\"runtime_paths\":[]}\n")
endif()
# The plugin verifies that this particular Debug invocation reached its runtime.
set(MLP_BUILD_TOKEN "" CACHE STRING "Calling plugin build token")
if(NOT MLP_BUILD_TOKEN MATCHES "^[a-zA-Z0-9]*$")
    message(FATAL_ERROR "Invalid MLP build token")
endif()
string(JSON mlp_runtime SET "${mlp_runtime}" build_token "\"${MLP_BUILD_TOKEN}\"")
file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/mlp-runtime.json" "${mlp_runtime}")
install(TARGETS mlp RUNTIME DESTINATION . LIBRARY DESTINATION .)
install(FILES "${CMAKE_CURRENT_BINARY_DIR}/mlp-runtime.json" DESTINATION .)
if(MLP_BUILD_TESTS)
    enable_testing()
    add_executable(mlp_matrix_tests tests/matrix.cpp)
    target_link_libraries(mlp_matrix_tests PRIVATE mlp_matrix)
    add_test(NAME mlp_matrix_cpu COMMAND mlp_matrix_tests)
    if(MLP_CUDA)
        add_test(NAME mlp_matrix_cuda COMMAND mlp_matrix_tests --cuda)
    endif()
endif()
