cmake_minimum_required(VERSION 3.18)
project(gdalcubes_py LANGUAGES CXX C)

set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
if(NOT CMAKE_BUILD_TYPE)
    set(CMAKE_BUILD_TYPE Release)
endif()

find_package(Python 3.10 COMPONENTS Interpreter Development.Module REQUIRED)

execute_process(
    COMMAND "${Python_EXECUTABLE}" -m nanobind --cmake_dir
    OUTPUT_STRIP_TRAILING_WHITESPACE
    OUTPUT_VARIABLE nanobind_ROOT
)
find_package(nanobind CONFIG REQUIRED)

# native dependencies (same set as the core's own build) ---
if(EMSCRIPTEN)
    # Pyodide: GDAL is the libgdal shared library Pyodide ships, with headers
    # generated from the same GDAL release. SQLite comes from libgdal, curl is a stub
    # (the browser has no sockets), and netcdf is a static classic-format build
    set(WASM_DEPS_DIR "${CMAKE_CURRENT_SOURCE_DIR}/build/wasm/deps"
        CACHE PATH "include/, lib/ and share/ prepared by tools/wasm/build.sh")
    
    if(NOT EXISTS "${WASM_DEPS_DIR}/lib/libgdal.so")
        message(FATAL_ERROR "no dependencies in ${WASM_DEPS_DIR}: run tools/wasm/build.sh --deps-only")
    endif()

    # the GDAL and PROJ data the script prepared go into the wheel by default
    set(GDALCUBES_GDAL_DATA "${WASM_DEPS_DIR}/share/gdal" CACHE PATH "GDAL data directory to bundle into the wheel")
    set(GDALCUBES_PROJ_DATA "${WASM_DEPS_DIR}/share/proj" CACHE PATH "PROJ data directory to bundle into the wheel")
    add_library(wasm_deps INTERFACE)
    
    # Boost.Regex is header-only, and standalone mode needs no other Boost
    # netcdf is classic-only here (no HDF5), so the core writes classic files
    target_compile_definitions(wasm_deps INTERFACE BOOST_REGEX_STANDALONE USE_NCDF4=0)
    target_include_directories(wasm_deps INTERFACE
        ${WASM_DEPS_DIR}/include
        ${WASM_DEPS_DIR}/include/gdal
    )
    target_link_libraries(wasm_deps INTERFACE
        ${WASM_DEPS_DIR}/lib/libgdal.so
        ${WASM_DEPS_DIR}/lib/libnetcdf.a
    )
    
    set(NETCDF_INCLUDE_DIR ${WASM_DEPS_DIR}/include)

else()
    find_package(GDAL REQUIRED)
    find_package(CURL REQUIRED)
    find_package(SQLite3 REQUIRED)

    find_path(NETCDF_INCLUDE_DIR "netcdf.h" HINTS $ENV{NETCDF_INCLUDEDIR})
    find_library(NETCDF_LIBRARY netcdf HINTS $ENV{NETCDF_LIBDIR})
    if(NOT NETCDF_INCLUDE_DIR OR NOT NETCDF_LIBRARY)
        message(FATAL_ERROR "netcdf C library not found (set NETCDF_INCLUDEDIR / NETCDF_LIBDIR)")
    endif()
endif()

# vendored gdalcubes core
set(CORE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/src/gdalcubes)
file(GLOB CORE_SOURCES ${CORE_DIR}/*.cpp)

list(APPEND CORE_SOURCES
    ${CORE_DIR}/external/tinyexpr/tinyexpr.c
    ${CORE_DIR}/external/json11/json11.cpp
    ${CORE_DIR}/external/tiny-process-library/process.cpp
)

if(WIN32)
    list(APPEND CORE_SOURCES ${CORE_DIR}/external/tiny-process-library/process_win.cpp)
else()
    list(APPEND CORE_SOURCES ${CORE_DIR}/external/tiny-process-library/process_unix.cpp)
endif()

# the binding layer is split by concern into several translation units
file(GLOB BINDING_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/src/bindings/*.cpp)
nanobind_add_module(_core ${BINDING_SOURCES} ${CORE_SOURCES})

target_compile_definitions(_core PRIVATE GDALCUBES_NO_SWARM)

# The vendored core uses std::shared_ptr in some headers (e.g. progress_virt.h)
# without including <memory>. libc++ (macOS) pulls it in transitively, but
# libstdc++ (Linux/GCC) does not, so those translation units fail to compile.
# Force-include <memory> for every C++ unit (not the C sources like tinyexpr.c,
# where <memory> does not exist) instead of patching the vendored sources, which
# tools/sync_core.sh would overwrite on the next re-vendor.
if(MSVC)
    target_compile_options(_core PRIVATE $<$<COMPILE_LANGUAGE:CXX>:/FImemory>)
else()
    target_compile_options(_core PRIVATE "$<$<COMPILE_LANGUAGE:CXX>:SHELL:-include memory>")
endif()

target_include_directories(_core PRIVATE
    ${CMAKE_CURRENT_SOURCE_DIR}/src
    ${NETCDF_INCLUDE_DIR}
)

if(EMSCRIPTEN)
    # SQLite is not compiled in: libgdal and libproj each export one, and the
    # loader binds symbols across modules, so a second copy here gets mixed with
    # theirs. The core uses the copy they export.
    target_link_libraries(_core PRIVATE wasm_deps)
else()
    target_link_libraries(
        _core PRIVATE GDAL::GDAL CURL::libcurl SQLite::SQLite3 ${NETCDF_LIBRARY}
    )
endif()

install(TARGETS _core LIBRARY DESTINATION gdalcubes)

# wheel builds bundle the GDAL/PROJ runtime data files into the package.
# gdalcubes/__init__.py points GDAL_DATA / PROJ_DATA at them when present
set(GDALCUBES_GDAL_DATA "" CACHE PATH "GDAL data directory to bundle into the wheel")
set(GDALCUBES_PROJ_DATA "" CACHE PATH "PROJ data directory to bundle into the wheel")

if(GDALCUBES_GDAL_DATA)
    install(DIRECTORY ${GDALCUBES_GDAL_DATA}/ DESTINATION gdalcubes/gdal_data)
endif()

if(GDALCUBES_PROJ_DATA)
    install(DIRECTORY ${GDALCUBES_PROJ_DATA}/ DESTINATION gdalcubes/proj_data)
endif()
