# Требуем CMake не старее версии 3.20.
cmake_minimum_required(VERSION 3.20)

# Создаём проект на языке C++.
project(mmps_homework_1 LANGUAGES CXX)

# Используем стандарт C++17. Он нужен хосту для std::filesystem.
set(CMAKE_CXX_STANDARD 17)
# Запрещаем CMake использовать более старый стандарт.
set(CMAKE_CXX_STANDARD_REQUIRED ON)
# Отключаем нестандартные расширения конкретного компилятора.
set(CMAKE_CXX_EXTENSIONS OFF)

# Создаём основную программу. Она ищет и загружает плагины во время выполнения.
add_executable(mmps_host src/main.cpp)

# Позволяем подключать общий интерфейс как #include "api.hpp".
target_include_directories(mmps_host PRIVATE include)

# В Linux функции dlopen, dlsym и dlclose могут требовать библиотеку libdl.
# В Windows LoadLibraryW/GetProcAddress предоставляются системой, поэтому
# дополнительная библиотека для компоновки не нужна.
if(UNIX AND NOT APPLE)
    target_link_libraries(mmps_host PRIVATE ${CMAKE_DL_LIBS})
endif()

# Складываем исполняемый файл в build/bin.
set_target_properties(mmps_host PROPERTIES
    RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/bin"
)

# Функция создаёт один плагин из переданного исходного файла.
function(add_mmps_plugin target source)
    # MODULE означает динамически загружаемый модуль:
    # DLL в Windows, SO в Linux и DYLIB в macOS.
    add_library(${target} MODULE ${source})

    # Плагину доступны общие структуры, типы функций и имена символов из api.hpp,
    # а также вспомогательные функции численных методов из state.hpp.
    target_include_directories(${target} PRIVATE include)

    # PLUGIN_BUILD включает __declspec(dllexport) при сборке DLL в Windows.
    # Определение PRIVATE и потому не передаётся другим целям.
    target_compile_definitions(${target} PRIVATE PLUGIN_BUILD)

    # PREFIX "" убирает стандартный префикс lib у имени плагина.
    # Все плагины складываются в общий каталог build/plugins.
    set_target_properties(${target} PROPERTIES
        PREFIX ""
        LIBRARY_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/plugins"
        RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/plugins"
    )
endfunction()

# Каждый решатель и способ вывода собирается в отдельную библиотеку.
add_mmps_plugin(
    runge_kutta_plugin
    plugins/runge_kutta_plugin.cpp
)

add_mmps_plugin(
    adams_bashforth_plugin
    plugins/adams_bashforth_plugin.cpp
)

add_mmps_plugin(
    console_output_plugin
    plugins/console_output_plugin.cpp
)

add_mmps_plugin(
    csv_output_plugin
    plugins/csv_output_plugin.cpp
)


# ЧТО ПРОИСХОДИТ ПРИ СБОРКЕ
#   src/main.cpp                         -> mmps_host.exe
#   plugins/runge_kutta_plugin.cpp       -> runge_kutta_plugin.dll
#   plugins/adams_bashforth_plugin.cpp   -> adams_bashforth_plugin.dll
#   plugins/console_output_plugin.cpp     -> console_output_plugin.dll
#   plugins/csv_output_plugin.cpp         -> csv_output_plugin.dll
#
# Хост и плагины собираются независимо. Здесь намеренно нет команд:
#   target_link_libraries(mmps_host PRIVATE runge_kutta_plugin)
#   target_link_libraries(mmps_host PRIVATE adams_bashforth_plugin)
#
# Поэтому в mmps_host не записываются обязательные зависимости от конкретных
# решателей. При запуске хост просматривает заданный каталог, загружает каждый
# подходящий файл через LoadLibraryW/dlopen и ищет функции по контракту api.hpp.
# Добавление ещё одного совместимого решателя не требует изменения main.cpp.
#
# ПРИМЕР СБОРКИ
#   cmake -S . -B build
#   cmake --build build --config Release
#
# ПРИМЕР ЗАПУСКА ИЗ КАТАЛОГА build
#   bin/Release/mmps_host.exe  (Visual Studio)
#   bin/mmps_host              (Linux/Makefiles)
