cmake_minimum_required(VERSION 3.15)
project(alspz LANGUAGES C CXX)

find_package(Python COMPONENTS Interpreter Development.Module NumPy REQUIRED)

set(BASE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/src/alspz")
set(CAPI_DIR "${CMAKE_CURRENT_SOURCE_DIR}/src/c")
set(SPZ_DIR "${CMAKE_CURRENT_SOURCE_DIR}/extern/spz")

# ---- zlib / zstd（与 spz 官方构建一致：优先系统库，缺失则 FetchContent） ----
# 全部静态链接：wheel 内不留 DLL 依赖（共享 z.dll 曾导致 ImportError 找不到模块）
set(BUILD_SHARED_LIBS OFF)
find_package(ZLIB QUIET)
if (NOT ZLIB_FOUND)
    include(FetchContent)
    FetchContent_Declare(zlib
        URL https://zlib.net/zlib-1.3.2.tar.gz
        URL_HASH SHA256=bb329a0a2cd0274d05519d61c667c062e06990d72e125ee2dfa8de64f0119d16)
    FetchContent_MakeAvailable(zlib)
    # 静态目标：共享 z.dll 不在 PATH 时 alspz.pyd 导入即失败
    set(zlib_target zlibstatic)
else()
    set(zlib_target ZLIB::ZLIB)
endif()

find_package(zstd QUIET)
if (NOT zstd_FOUND)
    include(FetchContent)
    FetchContent_Declare(zstd
        URL https://github.com/facebook/zstd/releases/download/v1.5.6/zstd-1.5.6.tar.gz
        URL_HASH SHA256=8c29e06cf42aacc1eafc4077ae2ec6c6fcb96a626157e0593d5e82a34fd403c1
        SOURCE_SUBDIR build/cmake)
    set(ZSTD_BUILD_SHARED OFF CACHE BOOL "" FORCE)
    set(ZSTD_BUILD_PROGRAMS OFF CACHE BOOL "" FORCE)
    set(ZSTD_BUILD_TESTS OFF CACHE BOOL "" FORCE)
    FetchContent_MakeAvailable(zstd)
    add_library(zstd::zstd ALIAS libzstd_static)
endif()

# ---- spz 库（vendored，含扩展支持） ----
set(spz_sources
    "${SPZ_DIR}/src/cc/load-spz.cc"
    "${SPZ_DIR}/src/cc/splat-c-types.cc"
    "${SPZ_DIR}/src/cc/splat-types.cc"
    "${SPZ_DIR}/extensions/cc/splat-extensions.cc"
    "${SPZ_DIR}/extensions/cc/safe-orbit-camera-adobe.cc"
    "${SPZ_DIR}/extensions/cc/coordinate-system-adobe.cc")
add_library(spz_objlib OBJECT ${spz_sources})
target_include_directories(spz_objlib PRIVATE
    "${SPZ_DIR}/src/cc"
    "${SPZ_DIR}/extensions/cc")
target_compile_definitions(spz_objlib PRIVATE SPZ_BUILD_EXTENSIONS)
target_link_libraries(spz_objlib PRIVATE ${zlib_target} zstd::zstd)
set_target_properties(spz_objlib PROPERTIES
    CXX_STANDARD 17
    CXX_STANDARD_REQUIRED ON)

set(CYTHON_C "${CMAKE_CURRENT_BINARY_DIR}/alspz.c")
add_custom_command(
    OUTPUT "${CYTHON_C}"
    COMMAND Python::Interpreter -m cython "${BASE_DIR}/alspz.pyx" -3 -o "${CYTHON_C}"
    DEPENDS "${BASE_DIR}/alspz.pyx"
    COMMENT "Transpiling Cython source: alspz.pyx -> alspz.c")

python_add_library(
    alspz
    MODULE
    "${CYTHON_C}"
    "${CAPI_DIR}/alspz_capi.cc"
    $<TARGET_OBJECTS:spz_objlib>)

target_include_directories(alspz PRIVATE
    "${CAPI_DIR}"
    "${SPZ_DIR}/src/cc"
    "${SPZ_DIR}/extensions/cc"
    ${Python_NumPy_INCLUDE_DIRS})

target_link_libraries(alspz PRIVATE ${zlib_target} zstd::zstd)

set_target_properties(alspz PROPERTIES
    CXX_STANDARD 17
    CXX_STANDARD_REQUIRED ON)

target_compile_definitions(alspz PRIVATE
    # 必须与 spz_objlib 一致：GaussianCloud 结构体布局随 SPZ_BUILD_EXTENSIONS
    # 条件编译变化（extensions 成员），跨编译单元不一致 = ABI 错配 = 随机崩溃
    #（实测 extensions.size() 读出 767 → 访问违规）
    SPZ_BUILD_EXTENSIONS
    NPY_NO_DEPRECATED_API=NPY_1_7_API_VERSION)

if(MSVC)
    target_compile_options(alspz PRIVATE "/utf-8" "/O2" "/MP")
    target_compile_options(spz_objlib PRIVATE "/utf-8" "/O2" "/MP")
else()
    target_compile_options(alspz PRIVATE "-O2")
    target_compile_options(spz_objlib PRIVATE "-O2")
endif()

install(TARGETS alspz DESTINATION alspz)
