cmake_minimum_required(VERSION 3.24)
project(granular_flow LANGUAGES CXX CUDA)

set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CUDA_STANDARD 17)
if (NOT CMAKE_CUDA_ARCHITECTURES)
  set(CMAKE_CUDA_ARCHITECTURES native)
endif()

include(FetchContent)
FetchContent_Declare(
  cuBQL
  GIT_REPOSITORY https://github.com/NVIDIA/cuBQL.git
  GIT_TAG        20f9db19cdb160e2b83d8f05dfdb0437fe11fe24
  # cubql_karras.patch adds the CUBQL_RADIX_KARRAS / CUBQL_RADIX_TIMING code
  # paths to the radix builder; without it the -DCUBQL_RADIX_KARRAS=1 below is
  # a silent no-op. The checkout first undoes any previously applied copy so
  # re-running the patch step stays idempotent.
  PATCH_COMMAND  git checkout -- .
      COMMAND    git apply --ignore-whitespace "${CMAKE_CURRENT_SOURCE_DIR}/cubql_karras.patch"
  UPDATE_DISCONNECTED TRUE
)
FetchContent_MakeAvailable(cuBQL)

find_package(Threads REQUIRED)

# use the Karras-style single-pass tree connection (see cubql_karras.patch)
# instead of cuBQL's level-by-level createNodes() loop in the radix builder
option(KARRAS_RADIX "single-pass Karras tree connection in cuBQL radix builder" ON)

# the native viewer: same simulation and display as granular_flow_server, in a
# local window instead of a browser. Turn this off to keep the build down to
# cuBQL alone, without glfw's build deps or the python glad needs.
option(ENABLE_OPENGL "build granular_flow_window (fetches glfw + glad)" ON)

set(examples granular_flow granular_flow_server)

if (ENABLE_OPENGL)
  # glfw 3.4 and glad 0.1.36 both declare cmake_minimum_required() below 3.5,
  # which cmake 4 refuses outright without this floor
  set(CMAKE_POLICY_VERSION_MINIMUM 3.5)
  set(GLFW_BUILD_DOCS     OFF CACHE BOOL "" FORCE)
  set(GLFW_BUILD_TESTS    OFF CACHE BOOL "" FORCE)
  set(GLFW_BUILD_EXAMPLES OFF CACHE BOOL "" FORCE)
  FetchContent_Declare(
    glfw
    GIT_REPOSITORY https://github.com/glfw/glfw.git
    GIT_TAG        3.4
  )
  # glad generates its loader at build time with python, from the khronos gl.xml
  # it downloads then. Its GLAD_REPRODUCIBLE option would use the copy bundled in
  # the repo instead, but that copy carries a utf-8 BOM and glad opens it in text
  # mode, so the bundled-spec path cannot parse its own file (glad 0.1.36).
  set(GLAD_API          "gl=3.3" CACHE STRING "" FORCE)
  set(GLAD_PROFILE      "core"   CACHE STRING "" FORCE)
  set(GLAD_REPRODUCIBLE OFF      CACHE BOOL   "" FORCE)
  set(GLAD_INSTALL      OFF      CACHE BOOL   "" FORCE)
  FetchContent_Declare(
    glad
    GIT_REPOSITORY https://github.com/Dav1dde/glad.git
    GIT_TAG        v0.1.36
  )
  FetchContent_MakeAvailable(glfw glad)

  add_executable(granular_flow_window src/window.cu)
  target_link_libraries(granular_flow_window PRIVATE glfw glad)
  list(APPEND examples granular_flow_window)
endif()

add_executable(granular_flow src/main.cu)
add_executable(granular_flow_server src/server.cu)

# MSBuild's CudaCompile dependency tracker never records the cuBQL headers (its
# .tlog lists only src/*.h and the CRT), so a re-applied cubql_karras.patch would
# leave every .obj stale and the build would silently keep the old radix builder.
# Name the patched header explicitly so a changed patch forces a recompile.
set_source_files_properties(src/main.cu src/server.cu src/window.cu
  PROPERTIES OBJECT_DEPENDS "${cuBQL_SOURCE_DIR}/cuBQL/builder/cuda/radix.h")
foreach(exe ${examples})
  target_link_libraries(${exe} PRIVATE cuBQL Threads::Threads)
  target_compile_options(${exe} PRIVATE $<$<COMPILE_LANGUAGE:CUDA>:--extended-lambda>)
  if (KARRAS_RADIX)
    target_compile_definitions(${exe} PRIVATE CUBQL_RADIX_KARRAS=1)
  endif()
  if (WIN32)
    target_compile_definitions(${exe} PRIVATE _USE_MATH_DEFINES)  # M_PI on MSVC
  endif()
endforeach()
if (WIN32)
  target_link_libraries(granular_flow_server PRIVATE ws2_32)
endif()
