###########################################################################
# create a custom target which bundles all python building and copying
###########################################################################
add_custom_target(python-module)


###########################################################################
# copy all python files from:
#    src/python/module
#    src/python/test
# recursively into
#   /blddir/python/
#   /blddir/python/test
###########################################################################
# NOTE: cmake -E copy_if_different creates destination directories itself,
# so no explicit MAKE_DIRECTORY is needed here
file(GLOB_RECURSE  Z5_PYTHON_FILES
    "${CMAKE_SOURCE_DIR}/src/python/module/z5py/*.py"
)
FOREACH(PYTHON_FILENAME   ${Z5_PYTHON_FILES})
    string(REPLACE "${CMAKE_SOURCE_DIR}/src/python/module/" ""
        RELATIVE_FILENAME
        ${PYTHON_FILENAME})
    string(REPLACE "/" "_"
        RELATIVE_FILENAME_TARGET_NAME
        ${RELATIVE_FILENAME})

    add_custom_target(copy_${RELATIVE_FILENAME_TARGET_NAME} ALL
        COMMAND cmake -E copy_if_different
        ${PYTHON_FILENAME}
        ${CMAKE_BINARY_DIR}/python/${RELATIVE_FILENAME})
    add_dependencies(python-module copy_${RELATIVE_FILENAME_TARGET_NAME})
ENDFOREACH()

file(GLOB_RECURSE  Z5_PYTHON_TEST_FILES
    "${CMAKE_SOURCE_DIR}/src/python/test/*.py"
)
FOREACH(PYTHON_FILENAME   ${Z5_PYTHON_TEST_FILES})
    string(REPLACE "${CMAKE_SOURCE_DIR}/src/python/test/" ""
        RELATIVE_FILENAME
        ${PYTHON_FILENAME})
    string(REPLACE "/" "_"
        RELATIVE_FILENAME_TARGET_NAME
        ${RELATIVE_FILENAME})

    add_custom_target(copy_${RELATIVE_FILENAME_TARGET_NAME} ALL
        COMMAND cmake -E copy_if_different
        ${PYTHON_FILENAME}
        ${CMAKE_BINARY_DIR}/python/test/${RELATIVE_FILENAME})
    add_dependencies(python-module copy_${RELATIVE_FILENAME_TARGET_NAME})
ENDFOREACH()


###########################################################################
# Single-source the version: generate z5py/_version.py from the Z5_VERSION_*
# macros parsed in the top-level CMakeLists from include/z5/z5.hxx. This is
# imported by z5py/__init__.py so the runtime z5py.__version__ matches the C++
# source of truth for every build path (wheel, make install, in-place bld/python).
###########################################################################
configure_file(
    ${CMAKE_CURRENT_SOURCE_DIR}/_version.py.in
    ${CMAKE_BINARY_DIR}/python/z5py/_version.py @ONLY)

# Under scikit-build-core the generated file must be installed into the wheel
# (the legacy install(DIRECTORY ...) at the bottom copies it for `make install`).
if(SKBUILD)
    install(FILES ${CMAKE_BINARY_DIR}/python/z5py/_version.py DESTINATION z5py)
endif()


###########################################################################
# macro to simplify the adding of a submodule
###########################################################################
macro(addPythonModule)

    set(options "")
    set(oneValueArgs NESTED_NAME)
    set(multiValueArgs SOURCES LIBRARIES)
    cmake_parse_arguments(ADD_PY_MOD "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN} )

    # get name of the module (the last component of the nested name)
    string(REPLACE "/" ";" MOD_NESTING_LIST "${ADD_PY_MOD_NESTED_NAME}")
    list(GET MOD_NESTING_LIST -1 MODULE_NAME)


    #Create the binding library
    # NOMINSIZE keeps -O3 (nanobind defaults to -Os) since the strided-copy
    # hot loop is compiled into the extension; STABLE_ABI builds against the
    # CPython limited API (3.12+) and is ignored on older versions.
    nanobind_add_module(_${MODULE_NAME}
        STABLE_ABI
        NB_STATIC
        NOMINSIZE
        ${ADD_PY_MOD_SOURCES}
    )

    set_target_properties(_${MODULE_NAME}  PROPERTIES PREFIX "")

    # link additional libraries
    target_link_libraries(_${MODULE_NAME}
        PUBLIC
        ${ADD_PY_MOD_LIBRARIES}
    )

    # NOTE: nanobind_add_module handles the platform link flags itself
    # (-undefined dynamic_lookup on macOS, the python library on Windows)

    # create module dir
    file(MAKE_DIRECTORY ${CMAKE_BINARY_DIR}/python/${ADD_PY_MOD_NESTED_NAME})

    # copy the module file
    ADD_CUSTOM_COMMAND(
        TARGET _${MODULE_NAME}
        POST_BUILD
        COMMAND ${CMAKE_COMMAND} ARGS -E copy_if_different $<TARGET_FILE:_${MODULE_NAME}>
        ${CMAKE_BINARY_DIR}/python/${ADD_PY_MOD_NESTED_NAME}/
        COMMENT "Copying target ${MODULE_NAME} to temporary module directory")

    # Under scikit-build-core (pip wheel builds) install the extension straight
    # into the package directory inside the wheel (e.g. z5py/_z5py...). The
    # pure-Python files are added separately via the wheel.packages setting.
    if(SKBUILD)
        install(TARGETS _${MODULE_NAME}
                LIBRARY DESTINATION ${ADD_PY_MOD_NESTED_NAME})
    endif()

    add_dependencies(python-module _${MODULE_NAME})
endmacro()

add_subdirectory(lib)


###########################
# INSTALL THE PYTHON MODULE
###########################

# Legacy (conda / `make install`) installation: copy the assembled package from
# the build directory into the Python site-packages dir. Under scikit-build-core
# the wheel is assembled instead (extension via install(TARGETS) above,
# pure-Python files via wheel.packages), so this is skipped.
if(NOT SKBUILD)

    # Find the python install dir
    IF(NOT DEFINED PYTHON_MODULE_INSTALL_DIR OR PYTHON_MODULE_INSTALL_DIR MATCHES "^$")

        execute_process(
          COMMAND "${Python_EXECUTABLE}" -c
           "import sysconfig; print(sysconfig.get_path('platlib'))"
          OUTPUT_VARIABLE PYTHON_SITE
          OUTPUT_STRIP_TRAILING_WHITESPACE)

        SET(PYTHON_MODULE_INSTALL_DIR ${PYTHON_SITE})

    ENDIF()


    SET(PYTHON_MODULE_INSTALL_DIR ${PYTHON_MODULE_INSTALL_DIR}
        CACHE PATH "where to install the z5py package" FORCE)

    # this is the install path relative to CMAKE_INSTALL_PREFIX,
    # use this in INSTALL() commands to get packaging right
    FILE(RELATIVE_PATH PYTHON_MODULE_INSTALL_DIR ${CMAKE_INSTALL_PREFIX} ${PYTHON_MODULE_INSTALL_DIR})


    install(DIRECTORY ${CMAKE_BINARY_DIR}/python/z5py
        DESTINATION ${PYTHON_MODULE_INSTALL_DIR})

endif()
