cmake_minimum_required(VERSION 3.14)

project(ADC LANGUAGES CXX)

set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_SOURCE_DIR}")

set(CMAKE_CXX_STANDARD 23)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
# set(CMAKE_BUILD_TYPE Release)

#
# check compiler version to ensure supporting of
# 'deducing this' C++23 feature
#
# if("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU")
#     if (CMAKE_CXX_COMPILER_VERSION VERSION_LESS 14.0)
#         message(FATAL_ERROR "GCC version must be at least 14.0!")
#     endif()
# elseif ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang")
#     if (CMAKE_CXX_COMPILER_VERSION VERSION_LESS 18.0)
#         message(FATAL_ERROR "Clang version must be at least 18.0!")
#     endif()
# elseif(CMAKE_CXX_COMPILER_ID STREQUAL "MSVC")
#     if(CMAKE_CXX_COMPILER_VERSION VERSION_LESS "19.32")
#          message(FATAL_ERROR "MSVC version must be at least 19.32")
#     endif()
# else()
#     message(WARNING "You are using an unsupported compiler! Compilation has only been tested with Clang and GCC.")
# endif()

set(ADC_COMMON_HEADERS
    common/adc_traits.h
    common/adc_utils.h
    common/adc_serialization.h
    # common/adc_value_holder.h
    # common/adc_value.h
    # common/adc_valholder.h
)

set(ADC_DEVICE_HEADERS
    device/adc_device_attribute.h
    device/adc_device_command.h
    device/adc_device.h
    device/adc_device_concepts.h
)

set(ADC_NETWORK_HEADERS
    # net/adc_netmsg.h
    # net/adc_netmessage.h
    net/adc_netproto.h
    # net/adc_netservice.h
    net/adc_endpoint.h
    net/adc_netserver.h
    net/adc_netclient.h
    net/adc_net_concepts.h
    net/adc_device_netmsg.h
    net/adc_device_netserver.h
    net/adc_device_netclient.h
)

option(ASIO_LIBRARY "Use of ASIO library for networking implementation" ON)

if(ASIO_LIBRARY)
    find_package(ASIO REQUIRED)

    set(ADC_NETWORK_HEADERS
        ${ADC_NETWORK_HEADERS}
        net/asio/adc_netservice_asio.h
        net/asio/adc_device_netserver_asio.h
    )

    add_compile_definitions(PUBLIC USE_ASIO_LIBRARY)

    option(
        OPENSSL_LIBRARY
        "Use openssl library for related ASIO-based implementation"
        ON
    )
    if(OPENSSL_LIBRARY)
        find_package(OpenSSL REQUIRED)
        add_compile_definitions(PUBLIC USE_OPENSSL_WITH_ASIO)
    endif()
endif()

option(SPDLOG_LIBRARY "Use of SPDLOG library for logging" ON)

if(SPDLOG_LIBRARY)
    find_package(spdlog REQUIRED)
    find_package(fmt REQUIRED)

    set(ADC_COMMON_HEADERS ${ADC_COMMON_HEADERS} common/adc_spdlog.h)

    set(ADC_NETWORK_HEADERS ${ADC_NETWORK_HEADERS} net/adc_netserver_spdlog.h)

    add_compile_definitions(PUBLIC USE_SPDLOG_LIBRARY)
endif()

option(
    USE_UWEBSOCKET
    "Use of uWebsocket library for websocket-related staff"
    ON
)

if(USE_UWEBSOCKET)
    include(FetchContent)
    include(ExternalProject)

    FetchContent_Declare(
        uwebsockets
        SOURCE_DIR
        ${CMAKE_BINARY_DIR}/uWebsockets
        BINARY_DIR
        ${CMAKE_BINARY_DIR}
        GIT_REPOSITORY "https://github.com/uNetworking/uWebSockets.git"
        GIT_TAG "v20.67.0"
        GIT_SHALLOW TRUE
        GIT_SUBMODULES ""
        GIT_PROGRESS TRUE
    )
    FetchContent_MakeAvailable(uwebsockets)
    FetchContent_GetProperties(uwebsockets SOURCE_DIR uwebsockets_SOURCE_DIR)

    set(UWEBSOCKETS_INCLUDE_DIR ${uwebsockets_SOURCE_DIR}/src)

    FetchContent_Declare(
        usockets
        SOURCE_DIR
        ${uwebsockets_SOURCE_DIR}/uSockets
        BINARY_DIR
        ${CMAKE_BINARY_DIR}
        GIT_REPOSITORY "https://github.com/uNetworking/uSockets.git"
        GIT_TAG "v0.8.8"
        GIT_SHALLOW TRUE
        GIT_SUBMODULES ""
        GIT_PROGRESS TRUE
    )
    FetchContent_MakeAvailable(usockets)
    FetchContent_GetProperties(usockets SOURCE_DIR usockets_SOURCE_DIR)

    set(USOCKETS_INCLUDE_DIR ${usockets_SOURCE_DIR}/src)

    include(ExternalProject)

    ExternalProject_Add(
        LIBUS
        SOURCE_DIR ${usockets_SOURCE_DIR}
        PREFIX ${CMAKE_BINARY_DIR}/_deps
        BUILD_IN_SOURCE TRUE
        CONFIGURE_COMMAND ""
        BUILD_COMMAND ${CMAKE_COMMAND} -E env "WITH_OPENSSL=1" make
        INSTALL_COMMAND ""
        BUILD_BYPRODUCTS ${usockets_SOURCE_DIR}/uSockets.a
    )

    add_library(LIBUS_LIB STATIC IMPORTED)
    set_target_properties(
        LIBUS_LIB
        PROPERTIES IMPORTED_LOCATION ${usockets_SOURCE_DIR}/uSockets.a
    )
    get_target_property(LIBUS_LIB_PATH LIBUS_LIB IMPORTED_LOCATION)

    include_directories(${USOCKETS_INCLUDE_DIR})
    include_directories(${UWEBSOCKETS_INCLUDE_DIR})
endif(USE_UWEBSOCKET)

option(BUILD_TESTS "Build tests" ON)

if(BUILD_TESTS)
    find_package(doctest)

    # set(VALUEHOLDER_TEST_APP adc_valueholder_test)
    # add_executable(${VALUEHOLDER_TEST_APP} tests/adc_valueholder_test.cpp)

    set(DEVATTR_TEST_APP adc_devattr_test)
    add_executable(${DEVATTR_TEST_APP} tests/adc_devattr_test.cpp)

    set(DEV_TEST_APP adc_dev_test)
    add_executable(${DEV_TEST_APP} tests/adc_dev_test.cpp)

    set(NETMSG_TEST_APP adc_netmsg_test)
    add_executable(${NETMSG_TEST_APP} tests/adc_netmsg_test.cpp)

    set(NETSERVICE_TEST_APP adc_netservice_test)
    add_executable(${NETSERVICE_TEST_APP} tests/adc_netservice_test.cpp)
    if(OPENSSL_LIBRARY)
        target_link_libraries(
            ${NETSERVICE_TEST_APP}
            OpenSSL::SSL
            OpenSSL::Crypto
        )
    endif()

    if(ASIO_LIBRARY)
        find_package(cxxopts CONFIG)
        set(ASIO_NETSERVER_TEST_APP adc_asio_netserver_test)
        add_executable(
            ${ASIO_NETSERVER_TEST_APP}
            tests/adc_asio_netserver_test.cpp
        )
        target_link_libraries(
            ${ASIO_NETSERVER_TEST_APP}
            PUBLIC Threads::Threads cxxopts::cxxopts
        )
        if(OPENSSL_LIBRARY)
            target_link_libraries(
                ${ASIO_NETSERVER_TEST_APP}
                PUBLIC OpenSSL::SSL OpenSSL::Crypto
            )
        endif()
    endif()

    if(NOT doctest_FOUND)
        include(FetchContent)
        FetchContent_Declare(
            doctest
            GIT_REPOSITORY https://github.com/doctest/doctest.git
            GIT_TAG 2.4.11
        )
        FetchContent_MakeAvailable(doctest)
    endif()

    # target_link_libraries(${VALUEHOLDER_TEST_APP} PRIVATE doctest::doctest)
    target_link_libraries(${DEVATTR_TEST_APP} PRIVATE doctest::doctest)
    include(CTest)
    # add_test(VALUE_HOLDER ${VALUEHOLDER_TEST_APP})
    add_test(VALUE_HOLDER ${DEVATTR_TEST_APP})
    add_test(NETMSG_TEST ${NETMSG_TEST_APP})

    add_test(ASIO_NETSRV_TEST ${ASIO_NETSERVER_TEST_APP})
    if(SPDLOG_LIBRARY)
        target_link_libraries(${ASIO_NETSERVER_TEST_APP} PRIVATE fmt::fmt)
    endif()

    enable_testing()
endif(BUILD_TESTS)

include(GNUInstallDirs)

add_library(
    ${PROJECT_NAME}
    INTERFACE
    ${ADC_COMMON_HEADERS}
    ${ADC_DEVICE_HEADERS}
    ${ADC_NETWORK_HEADERS}
)
target_compile_features(${PROJECT_NAME} INTERFACE cxx_std_20)
# target_link_libraries(${PROJECT_NAME} INTERFACE ASIO::ASIO)
target_include_directories(
    ${PROJECT_NAME}
    INTERFACE
        $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
        $<INSTALL_INTERFACE:include>
)
