function(elfes_make_includeable input_file output_file)
    file(READ "${input_file}" hex_content HEX)
    string(LENGTH "${hex_content}" hex_length)
    math(EXPR byte_count "${hex_length} / 2")
    set(result "")
    set(line "")
    set(line_count 0)
    if(byte_count GREATER 0)
        math(EXPR last_index "${byte_count} - 1")
        foreach(index RANGE 0 ${last_index})
            math(EXPR position "${index} * 2")
            string(SUBSTRING "${hex_content}" ${position} 2 byte)
            string(TOUPPER "${byte}" byte)
            if(line STREQUAL "")
                set(line "0x${byte}")
            else()
                string(APPEND line ", 0x${byte}")
            endif()
            math(EXPR line_count "${line_count} + 1")
            math(EXPR line_remainder "${line_count} % 13")
            if(line_remainder EQUAL 0)
                string(APPEND result "${line},\n")
                set(line "")
            endif()
        endforeach()
    endif()
    if(NOT line STREQUAL "")
        string(APPEND result "${line}, 0x00\n")
    else()
        string(APPEND result "0x00\n")
    endif()
    file(WRITE "${output_file}" "/* Generated CUDA source; do not edit. */\n${result}\n")
endfunction()

function(elfes_inline_sphericart_headers input_file output_file)
    file(READ "${input_file}" content)
    string(REPLACE "\n" ";" content_lines "${content}")
    foreach(line IN LISTS content_lines)
        string(REGEX MATCH "#include \"([^\"]+)\"" _ "${line}")
        if(NOT "${CMAKE_MATCH_1}" STREQUAL "")
            file(READ "${CMAKE_CURRENT_SOURCE_DIR}/include/${CMAKE_MATCH_1}" header_content)
            string(REPLACE "${line}" "${header_content}" content "${content}")
        endif()
    endforeach()
    file(WRITE "${output_file}" "${content}")
endfunction()

set(sphericart_core_sources
    src/sphericart.cpp
)

if(BUILD_CUDA)
    file(MAKE_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/generated")
    elfes_inline_sphericart_headers(
        "${CMAKE_CURRENT_SOURCE_DIR}/src/sphericart_impl.cu"
        "${CMAKE_CURRENT_BINARY_DIR}/generated/sphericart_impl.cu"
    )
    elfes_make_includeable(
        "${CMAKE_CURRENT_BINARY_DIR}/generated/sphericart_impl.cu"
        "${CMAKE_CURRENT_BINARY_DIR}/generated/wrapped_sphericart_impl.cu"
    )
    list(APPEND sphericart_core_sources
        src/cuda_base.cpp
        src/sphericart_cuda.cpp
    )
else()
    list(APPEND sphericart_core_sources
        src/cuda_stub.cpp
        src/sphericart_cuda_stub.cpp
    )
endif()

add_library(elfes_sphericart_core STATIC ${sphericart_core_sources})
set_target_properties(elfes_sphericart_core PROPERTIES POSITION_INDEPENDENT_CODE ON)
target_compile_features(elfes_sphericart_core PUBLIC cxx_std_17)
target_include_directories(
    elfes_sphericart_core
    PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/include"
    PRIVATE "${CMAKE_CURRENT_BINARY_DIR}"
)
find_package(OpenMP)
if(OpenMP_CXX_FOUND)
    target_link_libraries(elfes_sphericart_core PUBLIC OpenMP::OpenMP_CXX)
endif()
if(BUILD_CUDA)
    target_link_libraries(elfes_sphericart_core PRIVATE elfes_gpu_lite)
endif()

add_library(elfes_sphericart_torch SHARED src/torch.cpp)
target_compile_features(elfes_sphericart_torch PRIVATE cxx_std_17)
if(BUILD_CUDA)
    target_compile_definitions(elfes_sphericart_torch PRIVATE ELFES_SPHERICART_CUDA)
endif()
target_include_directories(
    elfes_sphericart_torch
    PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}/include" ${ELFES_TORCH_INCLUDE_DIRS}
)
target_link_libraries(
    elfes_sphericart_torch
    PRIVATE
        elfes_sphericart_core
        ${ELFES_TORCH_LIBRARY}
        ${ELFES_TORCH_CPU_LIBRARY}
        ${ELFES_C10_LIBRARY}
)
if(NOT MSVC)
    target_compile_definitions(
        elfes_sphericart_torch
        PRIVATE _GLIBCXX_USE_CXX11_ABI=${ELFES_TORCH_CXX11_ABI}
    )
endif()
set_target_properties(
    elfes_sphericart_torch
    PROPERTIES INSTALL_RPATH "$ORIGIN"
)

add_library(elfes_sphericart_torch_cuda_stream SHARED src/streams.cpp)
target_compile_features(elfes_sphericart_torch_cuda_stream PRIVATE cxx_std_17)
target_include_directories(
    elfes_sphericart_torch_cuda_stream
    PRIVATE ${ELFES_TORCH_INCLUDE_DIRS}
)
target_link_libraries(
    elfes_sphericart_torch_cuda_stream
    PRIVATE
        ${ELFES_TORCH_LIBRARY}
        ${ELFES_TORCH_CPU_LIBRARY}
        ${ELFES_C10_LIBRARY}
)
if(NOT MSVC)
    target_compile_definitions(
        elfes_sphericart_torch_cuda_stream
        PRIVATE _GLIBCXX_USE_CXX11_ABI=${ELFES_TORCH_CXX11_ABI}
    )
endif()
if(BUILD_CUDA)
    target_compile_definitions(
        elfes_sphericart_torch_cuda_stream
        PRIVATE CUDA_AVAILABLE C10_CUDA_NO_CMAKE_CONFIGURE_FILE
    )
    target_include_directories(
        elfes_sphericart_torch_cuda_stream
        PRIVATE "${CMAKE_CUDA_TOOLKIT_INCLUDE_DIRECTORIES}"
    )
    target_link_libraries(
        elfes_sphericart_torch_cuda_stream
        PRIVATE ${ELFES_TORCH_CUDA_LIBRARY} ${ELFES_C10_CUDA_LIBRARY}
    )
endif()
