add_library(
    fesa_solver
    STATIC
    analysis/analysis_model.cpp
    analysis/analysis_state.cpp
    analysis/linear_static_analysis.cpp
    app/fesa_application.cpp
    assembly/load_assembler.cpp
    assembly/parallel_for.cpp
    assembly/sparse_assembler.cpp
    build_info.cpp
    constraints/essential_constraint_policy.cpp
    constraints/prescribed_displacement.cpp
    core/ascii.cpp
    core/diagnostic.cpp
    core/status.cpp
    elements/element_factory.cpp
    elements/euler_beam_3d.cpp
    elements/mitc4_shell.cpp
    fem/dof_manager.cpp
    io/abaqus/domain_builder.cpp
    io/abaqus/domain_mapping_context.cpp
    io/abaqus/domain_mapper.cpp
    io/abaqus/input_reader.cpp
    io/abaqus/material_property_mapper.cpp
    io/abaqus/step_definition_mapper.cpp
    io/abaqus/topology_mapper.cpp
    io/hdf5/hdf5_results_writer.cpp
    loads/concentrated_nodal_load.cpp
    materials/isotropic_linear_elastic_material.cpp
    math/dense_blas_internal.cpp
    math/matrix.cpp
    math/sparse_matrix.cpp
    math/vector.cpp
    model/domain.cpp
    model/shell_geometry.cpp
    model/source_target_resolver.cpp
    properties/general_beam_section.cpp
    properties/shell_section.cpp
    results/result_recovery.cpp
    solvers/linear/mkl_pardiso_solver.cpp
)

target_include_directories(
    fesa_solver
    PUBLIC
        "${PROJECT_SOURCE_DIR}/include"
    PRIVATE
        "${PROJECT_SOURCE_DIR}/src/fesa"
)

target_link_libraries(
    fesa_solver
    PRIVATE
        Fesa::MKL
        Fesa::TBB
        Fesa::HDF5
)

# Product warnings are strict without imposing FESA policy on external targets.
target_compile_options(
    fesa_solver
    PRIVATE
        $<$<CXX_COMPILER_ID:MSVC>:/W4 /WX>
)

add_executable(fesa_cli app/main.cpp)
set_target_properties(fesa_cli PROPERTIES OUTPUT_NAME fesa)
target_link_libraries(fesa_cli PRIVATE fesa_solver)
target_compile_options(
    fesa_cli
    PRIVATE
        $<$<CXX_COMPILER_ID:MSVC>:/W4 /WX>
)

if(WIN32)
    if(NOT TARGET MKL::mkl_intel_thread OR NOT TARGET MKL::mkl_core OR
       NOT OMP_DLL_DIR OR NOT OMP_DLLNAME)
        message(FATAL_ERROR "The oneMKL runtime files required by fesa.exe were not resolved")
    endif()
    if(NOT EXISTS "${OMP_DLL_DIR}/libmmd.dll")
        message(FATAL_ERROR "The Intel runtime required by the selected HDF5 DLL was not resolved")
    endif()

    file(GLOB _fesa_cli_mkl_dispatch_dlls "${MKL_ROOT}/bin/mkl_def.*.dll")
    if(NOT _fesa_cli_mkl_dispatch_dlls)
        message(FATAL_ERROR "The oneMKL default dispatch runtime was not resolved")
    endif()
    list(SORT _fesa_cli_mkl_dispatch_dlls COMPARE NATURAL ORDER DESCENDING)
    list(GET _fesa_cli_mkl_dispatch_dlls 0 _fesa_cli_mkl_dispatch_dll)

    # Keep the installed CLI runnable without assuming oneAPI or HDF5 on PATH.
    add_custom_command(
        TARGET fesa_cli
        POST_BUILD
        COMMAND "${CMAKE_COMMAND}" -E copy_if_different
                "$<TARGET_FILE:TBB::tbb>"
                "$<TARGET_FILE_DIR:fesa_cli>"
        COMMAND "${CMAKE_COMMAND}" -E copy_if_different
                "$<TARGET_FILE:MKL::mkl_intel_thread>"
                "$<TARGET_FILE_DIR:fesa_cli>"
        COMMAND "${CMAKE_COMMAND}" -E copy_if_different
                "$<TARGET_FILE:MKL::mkl_core>"
                "$<TARGET_FILE_DIR:fesa_cli>"
        COMMAND "${CMAKE_COMMAND}" -E copy_if_different
                "${OMP_DLL_DIR}/${OMP_DLLNAME}"
                "$<TARGET_FILE_DIR:fesa_cli>"
        COMMAND "${CMAKE_COMMAND}" -E copy_if_different
                "${OMP_DLL_DIR}/libmmd.dll"
                "$<TARGET_FILE_DIR:fesa_cli>"
        COMMAND "${CMAKE_COMMAND}" -E copy_if_different
                "${_fesa_cli_mkl_dispatch_dll}"
                "$<TARGET_FILE_DIR:fesa_cli>"
        COMMAND "${CMAKE_COMMAND}" -E copy_if_different
                "$<TARGET_RUNTIME_DLLS:fesa_cli>"
                "$<TARGET_FILE_DIR:fesa_cli>"
        COMMAND_EXPAND_LISTS
    )

    unset(_fesa_cli_mkl_dispatch_dll)
    unset(_fesa_cli_mkl_dispatch_dlls)
endif()
