find_package(MKL CONFIG REQUIRED HINTS "$ENV{MKLROOT}" "$ENV{MKL_ROOT}")
find_package(oneDPL REQUIRED HINTS "$ENV{DPL_ROOT}" "$ENV{DPLROOT}")
# use the parameter from cmake
set(GINKGO_MKL_ROOT "${MKL_DIR}" PARENT_SCOPE)
set(GINKGO_DPL_ROOT "${oneDPL_DIR}" PARENT_SCOPE)

include(${PROJECT_SOURCE_DIR}/cmake/template_instantiation.cmake)
add_instantiation_files(${PROJECT_SOURCE_DIR}/common/unified matrix/dense_kernels.instantiate.cpp DENSE_INSTANTIATE)
add_instantiation_files(. solver/batch_bicgstab_launch.instantiate.dp.cpp BATCH_BICGSTAB_INSTANTIATE)
add_instantiation_files(. solver/batch_cg_launch.instantiate.dp.cpp BATCH_CG_INSTANTIATE)
add_library(ginkgo_dpcpp $<TARGET_OBJECTS:ginkgo_dpcpp_device> "")
target_sources(
    ginkgo_dpcpp
    PRIVATE
        base/batch_multi_vector_kernels.dp.cpp
        base/device_matrix_data_kernels.dp.cpp
        base/executor.dp.cpp
        base/helper.dp.cpp
        base/index_set_kernels.dp.cpp
        base/scoped_device_id.dp.cpp
        base/timer.dp.cpp
        base/version.dp.cpp
        components/prefix_sum_kernels.dp.cpp
        distributed/assembly_kernels.dp.cpp
        distributed/index_map_kernels.dp.cpp
        distributed/matrix_kernels.dp.cpp
        distributed/partition_helpers_kernels.dp.cpp
        distributed/partition_kernels.dp.cpp
        distributed/vector_kernels.dp.cpp
        factorization/cholesky_kernels.dp.cpp
        factorization/elimination_forest_kernels.dp.cpp
        factorization/ic_kernels.dp.cpp
        factorization/ilu_kernels.dp.cpp
        factorization/factorization_kernels.dp.cpp
        factorization/lu_kernels.dp.cpp
        factorization/par_ic_kernels.dp.cpp
        factorization/par_ict_kernels.dp.cpp
        factorization/par_ilu_kernels.dp.cpp
        factorization/par_ilut_approx_filter_kernel.dp.cpp
        factorization/par_ilut_filter_kernel.dp.cpp
        factorization/par_ilut_select_common.dp.cpp
        factorization/par_ilut_select_kernel.dp.cpp
        factorization/par_ilut_spgeam_kernel.dp.cpp
        factorization/par_ilut_sweep_kernel.dp.cpp
        matrix/batch_csr_kernels.dp.cpp
        matrix/batch_dense_kernels.dp.cpp
        matrix/batch_ell_kernels.dp.cpp
        matrix/coo_kernels.dp.cpp
        matrix/csr_kernels.dp.cpp
        matrix/fbcsr_kernels.dp.cpp
        matrix/dense_kernels.dp.cpp
        matrix/diagonal_kernels.dp.cpp
        matrix/ell_kernels.dp.cpp
        matrix/fft_kernels.dp.cpp
        matrix/sellp_kernels.dp.cpp
        matrix/sparsity_csr_kernels.dp.cpp
        multigrid/pgm_kernels.dp.cpp
        preconditioner/batch_jacobi_kernels.dp.cpp
        preconditioner/isai_kernels.dp.cpp
        preconditioner/jacobi_advanced_apply_kernel.dp.cpp
        preconditioner/jacobi_generate_kernel.dp.cpp
        preconditioner/jacobi_kernels.dp.cpp
        preconditioner/jacobi_simple_apply_kernel.dp.cpp
        preconditioner/sor_kernels.dp.cpp
        reorder/rcm_kernels.dp.cpp
        solver/batch_bicgstab_kernels.dp.cpp
        ${BATCH_BICGSTAB_INSTANTIATE}
        solver/batch_cg_kernels.dp.cpp
        ${BATCH_CG_INSTANTIATE}
        solver/cb_gmres_kernels.dp.cpp
        solver/idr_kernels.dp.cpp
        solver/lower_trs_kernels.dp.cpp
        solver/multigrid_kernels.dp.cpp
        solver/upper_trs_kernels.dp.cpp
        stop/criterion_kernels.dp.cpp
        stop/residual_norm_kernels.dp.cpp
        ${GKO_UNIFIED_COMMON_SOURCES}
        ${DENSE_INSTANTIATE}
)

# TODO: adjust it when dpcpp jacobi supports more block size
set(GKO_DPCPP_JACOBI_BLOCK_SIZES 32)
set(GKO_DPCPP_JACOBI_SOURCES)
foreach(GKO_JACOBI_BLOCK_SIZE IN LISTS GKO_DPCPP_JACOBI_BLOCK_SIZES)
    configure_file(
        preconditioner/jacobi_generate_instantiate.inc.dp.cpp
        preconditioner/jacobi_generate_instantiate.${GKO_JACOBI_BLOCK_SIZE}.dp.cpp
    )
    configure_file(
        preconditioner/jacobi_simple_apply_instantiate.inc.dp.cpp
        preconditioner/jacobi_simple_apply_instantiate.${GKO_JACOBI_BLOCK_SIZE}.dp.cpp
    )
    configure_file(
        preconditioner/jacobi_advanced_apply_instantiate.inc.dp.cpp
        preconditioner/jacobi_advanced_apply_instantiate.${GKO_JACOBI_BLOCK_SIZE}.dp.cpp
    )
    list(
        APPEND
        GKO_DPCPP_JACOBI_SOURCES
        ${CMAKE_CURRENT_BINARY_DIR}/preconditioner/jacobi_generate_instantiate.${GKO_JACOBI_BLOCK_SIZE}.dp.cpp
        ${CMAKE_CURRENT_BINARY_DIR}/preconditioner/jacobi_simple_apply_instantiate.${GKO_JACOBI_BLOCK_SIZE}.dp.cpp
        ${CMAKE_CURRENT_BINARY_DIR}/preconditioner/jacobi_advanced_apply_instantiate.${GKO_JACOBI_BLOCK_SIZE}.dp.cpp
    )
endforeach()
target_sources(ginkgo_dpcpp PRIVATE ${GKO_DPCPP_JACOBI_SOURCES})
string(
    REPLACE
    ";"
    ","
    GKO_DPCPP_JACOBI_BLOCK_SIZES_CODE
    "${GKO_DPCPP_JACOBI_BLOCK_SIZES}"
)
configure_file(
    preconditioner/jacobi_common.hpp.in
    preconditioner/jacobi_common.hpp
)

ginkgo_compile_features(ginkgo_dpcpp)
target_compile_definitions(
    ginkgo_dpcpp
    PRIVATE
        GKO_COMPILING_DPCPP
        GKO_DEVICE_NAMESPACE=dpcpp
        _ONEDPL_COMPILE_KERNEL=0
)

set(GINKGO_DPCPP_FLAGS ${GINKGO_DPCPP_FLAGS} PARENT_SCOPE)
target_compile_options(ginkgo_dpcpp PRIVATE "${GINKGO_DPCPP_FLAGS}")
# all file in target ginkgo_dpcpp are necessarily compiled with sycl, so we can ignore the warning.
# If we would like to use SOURCES, please use the new files copied from GKO_UNIFIED_COMMON_SOURCES.
# Otherwise, the source's properties will be changed by add_sycl_to_target
gko_add_sycl_to_target(TARGET ginkgo_dpcpp)
# Note: add MKL as PRIVATE not PUBLIC (MKL example shows) to avoid propagating
# find_package(MKL) everywhere when linking ginkgo (see the MKL example
# https://software.intel.com/content/www/us/en/develop/documentation/onemkl-windows-developer-guide/top/getting-started/cmake-config-for-onemkl.html)
target_link_options(ginkgo_dpcpp PRIVATE -fsycl-device-lib=all)
# When building ginkgo as a static library, we need to use dpcpp and per_kernel
# link option when the program uses a dpcpp related function.
if(BUILD_SHARED_LIBS)
    target_link_options(
        ginkgo_dpcpp
        PRIVATE -fsycl-device-code-split=per_kernel
    )
else()
    target_link_options(ginkgo_dpcpp PUBLIC -fsycl-device-code-split=per_kernel)
endif()
# include path for generated headers like jacobi_common.hpp
target_include_directories(ginkgo_dpcpp PRIVATE ${CMAKE_CURRENT_BINARY_DIR}/..)
target_link_libraries(ginkgo_dpcpp PUBLIC ginkgo_device)
target_link_libraries(ginkgo_dpcpp PRIVATE oneDPL)
# MKL 2024.0+ provides the MKL::MKL_SYCL target, while older versions provide *_DPCPP
if(MKL_VERSION_MAJOR VERSION_GREATER_EQUAL 2024)
    target_link_libraries(ginkgo_dpcpp PRIVATE MKL::MKL_SYCL)
else()
    target_link_libraries(ginkgo_dpcpp PRIVATE MKL::MKL_DPCPP)
endif()
if(GINKGO_DPCPP_SINGLE_MODE)
    target_compile_definitions(ginkgo_dpcpp PRIVATE GINKGO_DPCPP_SINGLE_MODE=1)
endif()

ginkgo_default_includes(ginkgo_dpcpp)
ginkgo_install_library(ginkgo_dpcpp)

if(GINKGO_CHECK_CIRCULAR_DEPS)
    ginkgo_check_headers(ginkgo_dpcpp "GKO_COMPILING_DPCPP;GKO_DEVICE_NAMESPACE=dpcpp")
endif()

if(GINKGO_BUILD_TESTS)
    add_subdirectory(test)
endif()
