# SPDX-License-Identifier: BSD-3-Clause
set(ernie_package_dir "${CMAKE_BINARY_DIR}/python/ernie")

nanobind_add_module(
    _ernie
    STABLE_ABI
    NB_STATIC
    module.cpp
    render.cpp
    scene.cpp
    scene_json.cpp
    schema.cpp
    tensor.cpp)
target_link_libraries(_ernie PRIVATE ernie ernie_build_options)
if(TARGET ernie_app)
    target_sources(_ernie PRIVATE app.cpp demo.cpp)
    target_link_libraries(_ernie PRIVATE ernie_app)
    target_compile_definitions(_ernie PRIVATE ERNIE_HAS_APP)
endif()
target_compile_features(_ernie PRIVATE cxx_std_20)
target_include_directories(_ernie SYSTEM PRIVATE "${NB_DIR}/include" ${Python_INCLUDE_DIRS})
foreach(nanobind_library nanobind-static nanobind-static-abi3)
    if(TARGET ${nanobind_library})
        set_target_properties(${nanobind_library} PROPERTIES COMPILE_WARNING_AS_ERROR OFF)
    endif()
endforeach()
set_target_properties(_ernie PROPERTIES LIBRARY_OUTPUT_DIRECTORY "${ernie_package_dir}")
# Type stubs beside the module, for IDEs, type checkers and the Python API docs: _ernie/ holds a
# .pyi per module (the extension still imports first), and py.typed marks the package as typed.
set(ernie_stubs "${ernie_package_dir}/_ernie/__init__.pyi" "${ernie_package_dir}/_ernie/tokens.pyi")
if(TARGET ernie_app)
    list(APPEND ernie_stubs "${ernie_package_dir}/_ernie/app.pyi")
endif()
nanobind_add_stub(
    _ernie_stub
    RECURSIVE
    MODULE
    ernie._ernie
    OUTPUT_PATH
    "${ernie_package_dir}"
    OUTPUT
    ${ernie_stubs}
    MARKER_FILE
    "${ernie_package_dir}/py.typed"
    PYTHON_PATH
    "${CMAKE_BINARY_DIR}/python"
    DEPENDS
    _ernie)
configure_file(ernie/__init__.py "${ernie_package_dir}/__init__.py" COPYONLY)
configure_file(ernie/app.py "${ernie_package_dir}/app.py" COPYONLY)
configure_file(ernie/batch.py "${ernie_package_dir}/batch.py" COPYONLY)
configure_file(ernie/demo.py "${ernie_package_dir}/demo.py" COPYONLY)
configure_file(ernie/schema.py "${ernie_package_dir}/schema.py" COPYONLY)
configure_file(ernie/cli.py "${ernie_package_dir}/cli.py" COPYONLY)
configure_file(ernie/__main__.py "${ernie_package_dir}/__main__.py" COPYONLY)
# ernie.demos: the Python demos (demos/<category>/<name>.py) and the ISDs they read, run by
# `python -m ernie.demos`.
file(
    GLOB ernie_demo_sources CONFIGURE_DEPENDS
    RELATIVE "${PROJECT_SOURCE_DIR}/demos"
    "${PROJECT_SOURCE_DIR}/demos/*/*.py")
set(ernie_demo_data "${PROJECT_SOURCE_DIR}/ernie/test/fixtures/isd/toughLroNacLineScan.json"
                    "${PROJECT_SOURCE_DIR}/ernie/test/fixtures/isd/lroWacPushFrame.json")
foreach(source ernie/demos/__init__.py ernie/demos/__main__.py)
    get_filename_component(name "${source}" NAME)
    configure_file("${source}" "${ernie_package_dir}/demos/${name}" COPYONLY)
endforeach()
foreach(source ${ernie_demo_sources})
    configure_file("${PROJECT_SOURCE_DIR}/demos/${source}" "${ernie_package_dir}/demos/${source}"
                   COPYONLY)
endforeach()
foreach(source ${ernie_demo_data})
    get_filename_component(name "${source}" NAME)
    configure_file("${source}" "${ernie_package_dir}/demos/data/${name}" COPYONLY)
endforeach()
# Stubs of the typed prim classes, which ernie.schema builds from the prim type registry at import
# where nanobind's stubgen cannot see them; -P keeps this directory's ernie/ off the path.
add_custom_command(
    OUTPUT "${ernie_package_dir}/schema.pyi"
    COMMAND ${CMAKE_COMMAND} -E env "PYTHONPATH=${CMAKE_BINARY_DIR}/python" ${Python_EXECUTABLE} -P
            "${CMAKE_CURRENT_SOURCE_DIR}/stub_schema.py" "${ernie_package_dir}/schema.pyi"
    DEPENDS _ernie stub_schema.py ernie/schema.py ernie/__init__.py
    COMMENT "Generating the typed prim classes' stubs"
    VERBATIM)
add_custom_target(_ernie_schema_stub ALL DEPENDS "${ernie_package_dir}/schema.pyi")
# A wheel (pyproject.toml, scikit-build-core): the package, with Slang's compiler and the glslang
# library it loads at run time beside the module, found through $ORIGIN; the `python` component
# only, not dependencies' own install rules.
if(SKBUILD)
    install(TARGETS _ernie LIBRARY DESTINATION ernie COMPONENT python)
    install(
        FILES ernie/__init__.py
              ernie/app.py
              ernie/batch.py
              ernie/demo.py
              ernie/schema.py
              ernie/cli.py
              ernie/__main__.py
              "${ernie_package_dir}/schema.pyi"
              "${ernie_package_dir}/py.typed"
        DESTINATION ernie
        COMPONENT python)
    install(
        FILES ${ernie_stubs}
        DESTINATION ernie/_ernie
        COMPONENT python)
    install(
        FILES ernie/demos/__init__.py ernie/demos/__main__.py
        DESTINATION ernie/demos
        COMPONENT python)
    foreach(source ${ernie_demo_sources})
        get_filename_component(category "${source}" DIRECTORY)
        install(
            FILES "${PROJECT_SOURCE_DIR}/demos/${source}"
            DESTINATION ernie/demos/${category}
            COMPONENT python)
    endforeach()
    install(
        FILES ${ernie_demo_data}
        DESTINATION ernie/demos/data
        COMPONENT python)
    set_target_properties(_ernie PROPERTIES INSTALL_RPATH "$ORIGIN")
    include("${PROJECT_SOURCE_DIR}/cmake/ErnieLicenses.cmake")
    ernie_install_licenses(ernie/licenses python)
    install(
        FILES ${ERNIE_SLANG_RUNTIME}
        DESTINATION ernie
        COMPONENT python)
endif()
