set(k_optosky_gui_core_sources
    optosky_gui_csv.cpp
    optosky_gui_driver_raii.cpp
    optosky_gui_platform.cpp
    optosky_gui_state.cpp
    optosky_gui_worker.cpp
)

option(OPTOSKY_GUI_INSTALL_DESKTOP_INTEGRATION
       "Install Linux desktop/AppStream/icon integration for packaged GUI builds"
       ON)

if(NOT TARGET Optosky::optosky)
    message(FATAL_ERROR "gui/ must be built with a public Optosky::optosky target")
endif()

include("${PROJECT_SOURCE_DIR}/driver/cmake/OptoskySanitizers.cmake")

set(k_optosky_nfd_target "")
if(OPTOSKY_GUI_USE_SYSTEM_NFD)
    find_package(nfd CONFIG QUIET)
    if(TARGET nfd::nfd)
        set(k_optosky_nfd_target nfd::nfd)
    endif()
endif()

if(NOT k_optosky_nfd_target AND OPTOSKY_GUI_VENDOR_NFD)
    set(k_optosky_nfd_dir "${CMAKE_SOURCE_DIR}/third_party/nativefiledialog-extended")
    if(NOT EXISTS "${k_optosky_nfd_dir}/CMakeLists.txt")
        message(FATAL_ERROR
            "System Native File Dialog Extended was not found and "
            "third_party/nativefiledialog-extended is not available.")
    endif()
    set(NFD_BUILD_TESTS OFF CACHE BOOL "Build Native File Dialog tests" FORCE)
    set(NFD_BUILD_SDL2_TESTS OFF CACHE BOOL "Build Native File Dialog SDL2 tests" FORCE)
    set(NFD_BUILD_GLFW3_TESTS OFF CACHE BOOL "Build Native File Dialog GLFW3 tests" FORCE)
    set(NFD_INSTALL OFF CACHE BOOL "Generate Native File Dialog installation target" FORCE)
    if(UNIX AND NOT APPLE)
        set(NFD_PORTAL ON CACHE BOOL "Use xdg-desktop-portal for Native File Dialog" FORCE)
        set(NFD_X11 OFF CACHE BOOL "Support X11 in Native File Dialog" FORCE)
        set(NFD_WAYLAND OFF CACHE BOOL "Support Wayland in Native File Dialog" FORCE)
    endif()
    set(k_optosky_saved_build_shared_libs ${BUILD_SHARED_LIBS})
    set(BUILD_SHARED_LIBS OFF)
    add_subdirectory("${k_optosky_nfd_dir}"
                     "${CMAKE_BINARY_DIR}/third_party/nativefiledialog-extended")
    set(BUILD_SHARED_LIBS ${k_optosky_saved_build_shared_libs})
    set(k_optosky_nfd_target nfd::nfd)
endif()

if(NOT k_optosky_nfd_target)
    message(FATAL_ERROR
        "Native File Dialog Extended was not found. Install a package that "
        "provides nfd::nfd or configure with OPTOSKY_GUI_VENDOR_NFD=ON after "
        "adding third_party/nativefiledialog-extended.")
endif()

add_library(optosky_gui_core STATIC ${k_optosky_gui_core_sources})
target_compile_features(optosky_gui_core PUBLIC cxx_std_17)
target_include_directories(optosky_gui_core PUBLIC ${CMAKE_CURRENT_SOURCE_DIR})
target_link_libraries(optosky_gui_core PUBLIC Optosky::optosky ${k_optosky_nfd_target})

if(CMAKE_CXX_COMPILER_ID MATCHES "GNU|Clang")
    target_compile_options(optosky_gui_core PRIVATE
        -Wall -Wextra -Wpedantic -Wconversion -Wshadow
    )
endif()
optosky_apply_sanitizers(optosky_gui_core)

if(BUILD_TESTING AND OPTOSKY_BUILD_TESTS)
    function(optosky_gui_add_test target_name source_file test_name)
        add_executable(${target_name} ${source_file})
        target_link_libraries(${target_name} PRIVATE optosky_gui_core)
        optosky_apply_sanitizers(${target_name})
        if(WIN32)
            add_test(
                NAME ${test_name}
                COMMAND ${CMAKE_COMMAND} -E env
                    "PATH=$<TARGET_FILE_DIR:optosky>$<SEMICOLON>$ENV{PATH}"
                    $<TARGET_FILE:${target_name}>
            )
        else()
            add_test(NAME ${test_name} COMMAND ${target_name})
        endif()
    endfunction()

    optosky_gui_add_test(optosky_gui_test_csv tests/optosky_gui_test_csv.cpp
                         optosky_gui.csv)
    optosky_gui_add_test(optosky_gui_test_state tests/optosky_gui_test_state.cpp
                         optosky_gui.state)
    optosky_gui_add_test(optosky_gui_test_worker tests/optosky_gui_test_worker.cpp
                         optosky_gui.worker)
    if(OPTOSKY_ENABLE_LIVE_TESTS)
        optosky_gui_add_test(optosky_gui_live_worker
                             tests/optosky_gui_live_worker.cpp
                             optosky_gui.live_worker)
        set_tests_properties(optosky_gui.live_worker PROPERTIES
            LABELS "live;hardware"
        )
    endif()
    add_test(
        NAME optosky_gui.source_boundaries
        COMMAND ${CMAKE_COMMAND}
            -DOPTOSKY_GUI_SOURCE_DIR=${CMAKE_CURRENT_SOURCE_DIR}
            -P ${CMAKE_CURRENT_SOURCE_DIR}/tests/check_gui_source_boundaries.cmake
    )

    if(OPTOSKY_ENABLE_VALGRIND_TESTS)
        find_program(VALGRIND_EXECUTABLE valgrind REQUIRED)
        set(k_optosky_gui_valgrind_options
            --leak-check=full
            --show-leak-kinds=definite,indirect
            --errors-for-leak-kinds=definite,indirect
            --error-exitcode=1
        )
        foreach(k_gui_test csv state worker)
            add_test(
                NAME optosky_gui.valgrind.${k_gui_test}
                COMMAND ${VALGRIND_EXECUTABLE}
                    ${k_optosky_gui_valgrind_options}
                    $<TARGET_FILE:optosky_gui_test_${k_gui_test}>
            )
            set_tests_properties(optosky_gui.valgrind.${k_gui_test} PROPERTIES
                LABELS "valgrind;gui"
            )
        endforeach()
        if(OPTOSKY_ENABLE_LIVE_TESTS)
            add_test(
                NAME optosky_gui.valgrind.live_worker
                COMMAND ${VALGRIND_EXECUTABLE}
                    ${k_optosky_gui_valgrind_options}
                    $<TARGET_FILE:optosky_gui_live_worker>
            )
            set_tests_properties(optosky_gui.valgrind.live_worker PROPERTIES
                LABELS "live;hardware;valgrind;gui"
            )
        endif()
    endif()
endif()

if(NOT OPTOSKY_GUI_BUILD_APP)
    return()
endif()

set(k_optosky_imgui_dir "${CMAKE_SOURCE_DIR}/third_party/imgui")
set(k_optosky_implot_dir "${CMAKE_SOURCE_DIR}/third_party/implot")

if(NOT EXISTS "${k_optosky_imgui_dir}/imgui.cpp")
    message(FATAL_ERROR
        "Dear ImGui is required for optosky_gui. Add the pinned submodule at "
        "third_party/imgui and document it in third_party/README.md.")
endif()
if(NOT EXISTS "${k_optosky_implot_dir}/implot.cpp")
    message(FATAL_ERROR
        "ImPlot is required for optosky_gui. Add the pinned submodule at "
        "third_party/implot and document it in third_party/README.md.")
endif()

if(NOT OPTOSKY_GUI_USE_SYSTEM_GLFW AND NOT OPTOSKY_GUI_VENDOR_GLFW)
    message(FATAL_ERROR
        "No GLFW provider selected. Enable OPTOSKY_GUI_USE_SYSTEM_GLFW or "
        "OPTOSKY_GUI_VENDOR_GLFW.")
endif()

set(k_optosky_glfw_target "")
if(OPTOSKY_GUI_USE_SYSTEM_GLFW)
    find_package(glfw3 QUIET)
    if(glfw3_FOUND)
        set(k_optosky_glfw_target glfw)
    endif()
endif()

if(NOT k_optosky_glfw_target AND OPTOSKY_GUI_VENDOR_GLFW)
    if(NOT EXISTS "${CMAKE_SOURCE_DIR}/third_party/glfw/CMakeLists.txt")
        message(FATAL_ERROR
            "System GLFW was not found and third_party/glfw is not available. "
            "Install glfw3 or add the pinned vendored GLFW submodule.")
    endif()
    set(GLFW_BUILD_DOCS OFF CACHE BOOL "Build GLFW documentation" FORCE)
    set(GLFW_BUILD_EXAMPLES OFF CACHE BOOL "Build GLFW examples" FORCE)
    set(GLFW_BUILD_TESTS OFF CACHE BOOL "Build GLFW tests" FORCE)
    set(GLFW_INSTALL OFF CACHE BOOL "Generate GLFW installation target" FORCE)
    if(UNIX AND NOT APPLE)
        set(GLFW_BUILD_WAYLAND OFF CACHE BOOL "Build GLFW Wayland support" FORCE)
        set(GLFW_BUILD_X11 ON CACHE BOOL "Build GLFW X11 support" FORCE)
    endif()
    add_subdirectory("${CMAKE_SOURCE_DIR}/third_party/glfw"
                     "${CMAKE_BINARY_DIR}/third_party/glfw")
    set(k_optosky_glfw_target glfw)
endif()

if(NOT k_optosky_glfw_target)
    message(FATAL_ERROR
        "System GLFW was not found. Install glfw3 or configure with "
        "OPTOSKY_GUI_VENDOR_GLFW=ON after adding third_party/glfw.")
endif()

find_package(OpenGL REQUIRED)

add_library(optosky_gui_imgui STATIC
    ${k_optosky_imgui_dir}/imgui.cpp
    ${k_optosky_imgui_dir}/imgui_draw.cpp
    ${k_optosky_imgui_dir}/imgui_tables.cpp
    ${k_optosky_imgui_dir}/imgui_widgets.cpp
    ${k_optosky_imgui_dir}/backends/imgui_impl_glfw.cpp
    ${k_optosky_imgui_dir}/backends/imgui_impl_opengl3.cpp
    ${k_optosky_implot_dir}/implot.cpp
    ${k_optosky_implot_dir}/implot_items.cpp
)
target_compile_features(optosky_gui_imgui PUBLIC cxx_std_17)
target_include_directories(optosky_gui_imgui PUBLIC
    ${k_optosky_imgui_dir}
    ${k_optosky_implot_dir}
)
target_link_libraries(optosky_gui_imgui PUBLIC
    ${k_optosky_glfw_target}
    OpenGL::GL
)
optosky_apply_sanitizers(optosky_gui_imgui)

add_executable(optosky_gui
    main.cpp
    optosky_gui_app.cpp
    optosky_gui_debug_screenshot.cpp
    optosky_gui_style.cpp
)
target_compile_features(optosky_gui PRIVATE cxx_std_17)
target_link_libraries(optosky_gui PRIVATE
    optosky_gui_core
    optosky_gui_imgui
)
optosky_apply_sanitizers(optosky_gui)
if(WIN32)
    add_custom_command(
        TARGET optosky_gui
        POST_BUILD
        COMMAND ${CMAKE_COMMAND} -E copy_if_different
                "$<TARGET_FILE:optosky>"
                "$<TARGET_FILE_DIR:optosky_gui>"
        COMMAND ${CMAKE_COMMAND} -E copy_if_different
                "$<TARGET_FILE_DIR:optosky>/libusb-1.0.dll"
                "$<TARGET_FILE_DIR:optosky_gui>"
        COMMAND ${CMAKE_COMMAND} -E copy_if_different
                "$<TARGET_FILE:nfd::nfd>"
                "$<TARGET_FILE_DIR:optosky_gui>"
        COMMAND ${CMAKE_COMMAND} -E copy_directory
                "${CMAKE_CURRENT_SOURCE_DIR}/assets"
                "$<TARGET_FILE_DIR:optosky_gui>/assets"
    )
else()
    add_custom_command(
        TARGET optosky_gui
        POST_BUILD
        COMMAND ${CMAKE_COMMAND} -E copy_directory
                "${CMAKE_CURRENT_SOURCE_DIR}/assets"
                "$<TARGET_FILE_DIR:optosky_gui>/assets"
    )
endif()

install(TARGETS optosky_gui
    RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
    COMPONENT gui
)
if(WIN32)
    install(FILES "$<TARGET_FILE:nfd::nfd>"
        DESTINATION ${CMAKE_INSTALL_BINDIR}
        COMPONENT gui
    )
    install(DIRECTORY assets/
        DESTINATION ${CMAKE_INSTALL_BINDIR}/assets
        COMPONENT gui
    )
else()
    install(DIRECTORY assets/
        DESTINATION ${CMAKE_INSTALL_DATADIR}/optosky_gui/assets
        COMPONENT gui
    )
endif()
if(UNIX AND NOT APPLE AND OPTOSKY_GUI_INSTALL_DESKTOP_INTEGRATION)
    install(FILES
        ${CMAKE_SOURCE_DIR}/packaging/linux/com.hpart.optosky.desktop
        DESTINATION ${CMAKE_INSTALL_DATADIR}/applications
        COMPONENT gui
    )
    if(NOT DEFINED OPTOSKY_METAINFO_FILE)
        set(OPTOSKY_METAINFO_FILE
            ${CMAKE_SOURCE_DIR}/packaging/linux/com.hpart.optosky.metainfo.xml
        )
    endif()
    install(FILES
        ${OPTOSKY_METAINFO_FILE}
        DESTINATION ${CMAKE_INSTALL_DATADIR}/metainfo
        COMPONENT gui
    )
    install(FILES
        ${CMAKE_SOURCE_DIR}/packaging/linux/com.hpart.optosky.png
        DESTINATION ${CMAKE_INSTALL_DATADIR}/icons/hicolor/512x512/apps
        COMPONENT gui
    )
endif()
