cmake_minimum_required(VERSION 3.13.0)

cmake_policy(SET CMP0076 NEW)

project(suanPan C CXX)

set(CMAKE_CXX_STANDARD 17)
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()

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 README.md CHANGELOG.md LICENSE Enhancement/suanPan.sublime-completions Enhancement/suanPan.sublime-syntax 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()