cmake_minimum_required(VERSION 3.13.0)

cmake_policy(SET CMP0076 NEW)

project(suanPan C CXX)

set(CMAKE_C_STANDARD 11)
set(CMAKE_CXX_STANDARD 20)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_INCLUDE_CURRENT_DIR ON)
set(CMAKE_POSITION_INDEPENDENT_CODE ON)

include(CheckLanguage)
check_language(Fortran)
if (CMAKE_Fortran_COMPILER)
    set(FORTRAN_STATUS TRUE)
    enable_language(Fortran)
else ()
    set(FORTRAN_STATUS FALSE)
endif ()

set(ROOT .)

include(${ROOT}/.config.cmake)

link_directories(${PROJECT_BINARY_DIR}/Libs)

add_executable(${PROJECT_NAME}
        "suanPan.cpp"
        "Constraint/CMakeLists.txt"
        "Database/CMakeLists.txt"
        "Domain/CMakeLists.txt"
        "Load/CMakeLists.txt"
        "Recorder/CMakeLists.txt"
        "Step/CMakeLists.txt"
        "Toolbox/CMakeLists.txt"
        "UnitTest/CMakeLists.txt"
        )

if (CMAKE_CXX_COMPILER_ID MATCHES "GNU") # GNU GCC COMPILER
    target_sources(${PROJECT_NAME} PRIVATE "Resource/suanPan_gcc.rc")
elseif (CMAKE_CXX_COMPILER_ID MATCHES "MSVC") # MSVC COMPILER
    target_sources(${PROJECT_NAME} PRIVATE "Resource/suanPan.rc")
endif ()

add_subdirectory(Constraint)
add_subdirectory(Converger)
add_subdirectory(Database)
add_subdirectory(Domain)
add_subdirectory(Element)
add_subdirectory(Load)
add_subdirectory(Material)
add_subdirectory(Recorder)
add_subdirectory(Section)
add_subdirectory(Solver)
add_subdirectory(Step)
add_subdirectory(Toolbox)
add_subdirectory(UnitTest)

target_link_libraries(${PROJECT_NAME}
        Converger
        Element
        Material
        Section
        Solver
        )

if (FORTRAN_STATUS)
    add_subdirectory(Toolbox/amd-src)
    add_subdirectory(Toolbox/arpack-src)
    add_subdirectory(Toolbox/feast-src)
    add_subdirectory(Toolbox/lapack-ext)
    add_subdirectory(Toolbox/mumps-src)
    target_link_libraries(${PROJECT_NAME} amd arpack spmm mumps feast)
    message("Linking additional amd arpack spmm mumps feast")
elseif (CMAKE_CXX_COMPILER_ID MATCHES "MSVC")
    target_link_libraries(${PROJECT_NAME} libamd libarpack libspmm libmumps libfeast)
    message("Linking precompiled amd arpack spmm mumps feast")
else ()
    message(FATAL_ERROR "Please install a valid FORTRAN compiler.")
endif ()

if (USE_SUPERLUMT)
    add_subdirectory(Toolbox/superlumt-src)
    target_link_libraries(${PROJECT_NAME} superlumt)
else ()
    add_subdirectory(Toolbox/superlu-src)
    target_link_libraries(${PROJECT_NAME} superlu)
endif ()

add_subdirectory(Toolbox/metis-src)
target_link_libraries(${PROJECT_NAME} metis)

set_property(TARGET ${PROJECT_NAME} PROPERTY ENABLE_EXPORTS 1)

if ((COMPILER_IDENTIFIER MATCHES "gcc-linux") OR (COMPILER_IDENTIFIER MATCHES "gcc-mac"))
    if (USE_MKL)
        if (LINK_DYNAMIC_MKL)
            if (USE_INTEL_OPENMP)
                target_link_libraries(${PROJECT_NAME} mkl_intel_lp64 mkl_intel_thread mkl_core iomp5 m)
            else ()
                target_link_libraries(${PROJECT_NAME} mkl_intel_lp64 mkl_gnu_thread mkl_core gomp m)
            endif ()
        else ()
            # cyclic dependency
            if (USE_INTEL_OPENMP)
                target_link_libraries(${PROJECT_NAME}
                        libmkl_intel_lp64.a libmkl_intel_thread.a libmkl_core.a
                        libmkl_intel_lp64.a libmkl_intel_thread.a libmkl_core.a
                        libmkl_intel_lp64.a libmkl_intel_thread.a libmkl_core.a
                        iomp5 m)
            else ()
                target_link_libraries(${PROJECT_NAME}
                        libmkl_intel_lp64.a libmkl_gnu_thread.a libmkl_core.a
                        libmkl_intel_lp64.a libmkl_gnu_thread.a libmkl_core.a
                        libmkl_intel_lp64.a libmkl_gnu_thread.a libmkl_core.a
                        gomp m)
            endif ()
        endif ()
    else ()
        target_link_libraries(${PROJECT_NAME} openblas)
    endif ()
elseif (COMPILER_IDENTIFIER MATCHES "gcc-win")
    if (USE_MKL)
        target_link_libraries(${PROJECT_NAME} mkl_rt)
    else ()
        target_link_libraries(${PROJECT_NAME} openblas)
    endif ()
elseif (COMPILER_IDENTIFIER MATCHES "vs")
    if (USE_MKL)
        set(DSUFIX "")
        if (BUILD_SHARED)
            set(DSUFIX "_dll")
        endif ()
        target_link_libraries(${PROJECT_NAME} mkl_intel_lp64${DSUFIX} mkl_intel_thread${DSUFIX} mkl_core${DSUFIX} libiomp5md)
    else ()
        target_link_libraries(${PROJECT_NAME} libopenblas)
    endif ()
endif ()

if (BUILD_DLL_EXAMPLE)
    add_subdirectory(Developer/Element)
    add_subdirectory(Developer/Material)
    add_subdirectory(Developer/Modifier)
    add_subdirectory(Developer/ModuleBundle)
    add_subdirectory(Developer/Section)
    add_dependencies(ElementExample ${PROJECT_NAME})
    add_dependencies(MaterialExample ${PROJECT_NAME})
    add_dependencies(ModifierExample ${PROJECT_NAME})
    add_dependencies(ModuleBundle ${PROJECT_NAME})
    add_dependencies(SectionExample ${PROJECT_NAME})
endif ()

# need further work
install(TARGETS ${PROJECT_NAME} DESTINATION bin)
install(FILES
        CHANGELOG.md
        Enhancement/suanPan.sublime-completions
        Enhancement/suanPan.sublime-syntax
        LICENSE
        README.md
        Resource/suanPan-ua.svg
        Resource/suanPan.svg
        DESTINATION bin)
if (COMPILER_IDENTIFIER MATCHES "gcc-linux")
    install(PROGRAMS Enhancement/suanPan.sh DESTINATION bin)
    if (BUILD_MULTITHREAD)
        file(GLOB TBB_FILES Libs/gcc-linux/libtbb*)
        install(PROGRAMS ${TBB_FILES} DESTINATION lib)
    endif ()
    if (USE_MKL)
        if (MKLROOT MATCHES "(oneapi|oneAPI)")
            if (LINK_DYNAMIC_MKL)
                file(GLOB MKL_FILES ${MKLROOT}/lib/intel64/libmkl_core.so*)
                install(PROGRAMS ${MKL_FILES} DESTINATION lib)
                file(GLOB MKL_FILES ${MKLROOT}/lib/intel64/libmkl_intel_lp64.so*)
                install(PROGRAMS ${MKL_FILES} DESTINATION lib)
                file(GLOB MKL_FILES ${MKLROOT}/lib/intel64/libmkl_def.so*)
                install(PROGRAMS ${MKL_FILES} DESTINATION lib)
                file(GLOB MKL_FILES ${MKLROOT}/lib/intel64/libmkl_avx.so*)
                install(PROGRAMS ${MKL_FILES} DESTINATION lib)
                file(GLOB MKL_FILES ${MKLROOT}/lib/intel64/libmkl_avx2.so*)
                install(PROGRAMS ${MKL_FILES} DESTINATION lib)
                file(GLOB MKL_FILES ${MKLROOT}/lib/intel64/libmkl_avx512.so*)
                install(PROGRAMS ${MKL_FILES} DESTINATION lib)
                if (USE_INTEL_OPENMP)
                    file(GLOB MKL_FILES ${MKLROOT}/lib/intel64/libmkl_intel_thread.so*)
                    install(PROGRAMS ${MKL_FILES} DESTINATION lib)
                else ()
                    file(GLOB MKL_FILES ${MKLROOT}/lib/intel64/libmkl_gnu_thread.so*)
                    install(PROGRAMS ${MKL_FILES} DESTINATION lib)
                endif ()
            endif ()
            if (USE_INTEL_OPENMP)
                install(PROGRAMS ${IOMPPATH}/libiomp5.so DESTINATION lib)
            endif ()
        else ()
            if (LINK_DYNAMIC_MKL)
                install(PROGRAMS ${MKLROOT}/lib/intel64/libmkl_core.so DESTINATION lib)
                install(PROGRAMS ${MKLROOT}/lib/intel64/libmkl_intel_lp64.so DESTINATION lib)
                install(PROGRAMS ${MKLROOT}/lib/intel64/libmkl_def.so DESTINATION lib)
                install(PROGRAMS ${MKLROOT}/lib/intel64/libmkl_avx.so DESTINATION lib)
                install(PROGRAMS ${MKLROOT}/lib/intel64/libmkl_avx2.so DESTINATION lib)
                install(PROGRAMS ${MKLROOT}/lib/intel64/libmkl_avx512.so DESTINATION lib)
                if (USE_INTEL_OPENMP)
                    install(PROGRAMS ${MKLROOT}/lib/intel64/libmkl_intel_thread.so DESTINATION lib)
                else ()
                    install(PROGRAMS ${MKLROOT}/lib/intel64/libmkl_gnu_thread.so DESTINATION lib)
                endif ()
            endif ()
            if (USE_INTEL_OPENMP)
                install(PROGRAMS ${IOMPPATH}/libiomp5.so DESTINATION lib)
            endif ()
        endif ()
    endif ()
elseif (COMPILER_IDENTIFIER MATCHES "gcc-mac")
    install(PROGRAMS Enhancement/suanPan.sh DESTINATION bin)
    if (BUILD_MULTITHREAD)
        file(GLOB TBB_FILES Libs/gcc-mac/libtbb*.dylib)
        install(PROGRAMS ${TBB_FILES} DESTINATION lib)
    endif ()
elseif (COMPILER_IDENTIFIER MATCHES "gcc-win")
    install(FILES Enhancement/AddAssociation.bat DESTINATION bin)
    if (BUILD_MULTITHREAD)
        file(GLOB TBB_FILES Libs/gcc-win/libtbb*.dll)
        install(FILES ${TBB_FILES} DESTINATION bin)
    endif ()
    if (USE_MKL)
        if (MKLROOT MATCHES "(oneapi|oneAPI)")
            file(GLOB MKL_FILES ${MKLROOT}/redist/intel64/mkl_rt*)
            install(FILES ${MKL_FILES} DESTINATION bin)
            file(GLOB MKL_FILES ${MKLROOT}/redist/intel64/mkl_core*)
            install(FILES ${MKL_FILES} DESTINATION bin)
            file(GLOB MKL_FILES ${MKLROOT}/redist/intel64/mkl_def*)
            install(FILES ${MKL_FILES} DESTINATION bin)
            file(GLOB MKL_FILES ${MKLROOT}/redist/intel64/mkl_avx*)
            install(FILES ${MKL_FILES} DESTINATION bin)
            file(GLOB MKL_FILES ${MKLROOT}/redist/intel64/mkl_avx2*)
            install(FILES ${MKL_FILES} DESTINATION bin)
            file(GLOB MKL_FILES ${MKLROOT}/redist/intel64/mkl_avx512*)
            install(FILES ${MKL_FILES} DESTINATION bin)
            if (USE_INTEL_OPENMP)
                file(GLOB MKL_FILES ${MKLROOT}/redist/intel64/mkl_intel*)
                install(FILES ${MKL_FILES} DESTINATION bin)
            else ()
                file(GLOB MKL_FILES ${MKLROOT}/redist/intel64/mkl_intel*)
                install(FILES ${MKL_FILES} DESTINATION bin)
            endif ()
        else ()
            message(WARNING "Old MKL versions are not supported, you may want to collect all dependencies manually.")
        endif ()
    else ()
        install(FILES Libs/gcc-win/libopenblas.dll DESTINATION bin)
    endif ()
elseif (COMPILER_IDENTIFIER MATCHES "vs")
    install(FILES Enhancement/AddAssociation.bat DESTINATION bin)
    if (BUILD_MULTITHREAD)
        file(GLOB TBB_FILES Libs/vs/tbb*.dll)
        install(FILES ${TBB_FILES} DESTINATION bin)
    endif ()
    if (USE_MKL)
        if (MKLROOT MATCHES "(oneapi|oneAPI)")
            if (LINK_DYNAMIC_MKL)
                file(GLOB MKL_FILES ${MKLROOT}/redist/intel64/mkl_core*)
                install(FILES ${MKL_FILES} DESTINATION bin)
                file(GLOB MKL_FILES ${MKLROOT}/redist/intel64/mkl_def*)
                install(FILES ${MKL_FILES} DESTINATION bin)
                file(GLOB MKL_FILES ${MKLROOT}/redist/intel64/mkl_avx*)
                install(FILES ${MKL_FILES} DESTINATION bin)
                file(GLOB MKL_FILES ${MKLROOT}/redist/intel64/mkl_avx2*)
                install(FILES ${MKL_FILES} DESTINATION bin)
                file(GLOB MKL_FILES ${MKLROOT}/redist/intel64/mkl_avx512*)
                install(FILES ${MKL_FILES} DESTINATION bin)
                if (USE_INTEL_OPENMP)
                    file(GLOB MKL_FILES ${MKLROOT}/redist/intel64/mkl_intel*)
                    install(FILES ${MKL_FILES} DESTINATION bin)
                else ()
                    file(GLOB MKL_FILES ${MKLROOT}/redist/intel64/mkl_intel*)
                    install(FILES ${MKL_FILES} DESTINATION bin)
                endif ()
            endif ()
            if (USE_INTEL_OPENMP)
                install(FILES ${MKLROOT}/../../compiler/latest/windows/redist/intel64_win/compiler/libifcoremd.dll DESTINATION bin)
                install(FILES ${MKLROOT}/../../compiler/latest/windows/redist/intel64_win/compiler/libiomp5md.dll DESTINATION bin)
                install(FILES ${MKLROOT}/../../compiler/latest/windows/redist/intel64_win/compiler/libmmd.dll DESTINATION bin)
                install(FILES ${MKLROOT}/../../compiler/latest/windows/redist/intel64_win/compiler/svml_dispmd.dll DESTINATION bin)
            endif ()
        else ()
            message(WARNING "Old MKL versions are not supported, you may want to collect all dependencies manually.")
        endif ()
    else ()
        file(GLOB DLL_FILES Libs/gcc-win/lib*.dll)
        install(FILES ${DLL_FILES} DESTINATION bin)
    endif ()
endif ()

if (HAVE_VTK)
    target_link_libraries(${PROJECT_NAME} ${VTK_LIBRARIES})
    if (VTK_VERSION VERSION_LESS "8.90.0")
        include(${VTK_USE_FILE})
    else ()
        vtk_module_autoinit(TARGETS ${PROJECT_NAME} MODULES ${VTK_LIBRARIES})
    endif ()
endif ()

message("suanPan C_FLAGS: ${CMAKE_C_FLAGS}")
message("suanPan CXX_FLAGS: ${CMAKE_CXX_FLAGS}")
if (FORTRAN_STATUS)
    message("suanPan Fortran_FLAGS: ${CMAKE_Fortran_FLAGS}")
endif ()

message("Link Dir:")
get_property(SDIR DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} PROPERTY LINK_DIRECTORIES)
foreach (SDIRA ${SDIR})
    message("${SDIRA}")
endforeach ()

message("Include Dir:")
get_property(SDIR DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} PROPERTY INCLUDE_DIRECTORIES)
foreach (SDIRA ${SDIR})
    message("${SDIRA}")
endforeach ()