cmake_minimum_required(VERSION 3.13)

# Building tests by default depends on whether this is a subproject
if(DEFINED PROJECT_NAME)
    option(picoquic_BUILD_TESTS "Build Tests for picoquic" OFF)
    option(BUILD_PICO_SIM "Build pico_sim" OFF)
else()
    option(picoquic_BUILD_TESTS "Build Tests for picoquic" ON)
    option(BUILD_PICO_SIM "Build pico_sim" ON)
endif()

project(picoquic
        VERSION 1.1.49.2
        DESCRIPTION "picoquic library"
        LANGUAGES C CXX)

option(WITH_THREADS "Build with Threads support" ON)
if(WITH_THREADS)
    find_package(Threads REQUIRED)
else()
    message(FATAL_ERROR "WITH_THREADS=OFF is not yet fully implemented")
endif()

option(DISABLE_DEBUG_PRINTF "Disable Picoquic debug output" OFF)
option(ENABLE_ASAN "Enable AddressSanitizer (ASAN) for debugging" OFF)
option(ENABLE_UBSAN "Enable UndefinedBehaviorSanitizer (UBSan) for debugging" OFF)
option(BUILD_DEMO "Build picoquicdemo" ON)
option(BUILD_HTTP "Build picohttp" ON)
option(BUILD_PQBENCH "Build pqbench" ON)
option(BUILD_LOGLIB "Build picoquic-log" ON)
option(BUILD_LOGREADER "Build picolog_t the log reader" ON)

message(STATUS "Initial CMAKE_C_FLAGS=${CMAKE_C_FLAGS}")

if(DISABLE_DEBUG_PRINTF)
    list(APPEND PICOQUIC_COMPILE_DEFINITIONS DISABLE_DEBUG_PRINTF)
endif()

include(CheckCCompilerFlag)
include(CheckCXXCompilerFlag)
include(CheckCSourceCompiles)
include(CMakePushCheckState)

if(ENABLE_ASAN)
    cmake_push_check_state()
    set(CMAKE_REQUIRED_LIBRARIES "-fsanitize=address")
    check_c_compiler_flag(-fsanitize=address C__fsanitize_address_VALID)
    check_cxx_compiler_flag(-fsanitize=address CXX__fsanitize_address_VALID)
    cmake_pop_check_state()
    if(NOT C__fsanitize_address_VALID OR NOT CXX__fsanitize_address_VALID)
        message(FATAL_ERROR "ENABLE_ASAN was requested, but not supported!")
    endif()
    list(APPEND PICOQUIC_ADDITIONAL_C_FLAGS -fsanitize=address)
    list(APPEND PICOQUIC_ADDITIONAL_CXX_FLAGS -fsanitize=address)
    list(APPEND PICOQUIC_LINKER_FLAGS -fsanitize=address)
endif()

if(ENABLE_UBSAN)
    cmake_push_check_state()
    set(CMAKE_REQUIRED_LIBRARIES "-fsanitize=undefined")
    check_c_compiler_flag(-fsanitize=undefined C__fsanitize_undefined_VALID)
    check_cxx_compiler_flag(-fsanitize=undefined CXX__fsanitize_undefined_VALID)
    cmake_pop_check_state()
    if(NOT C__fsanitize_undefined_VALID OR NOT CXX__fsanitize_undefined_VALID)
        message(FATAL_ERROR "ENABLE_UBSAN was requested, but not supported!")
    endif()
    list(PREPEND PICOQUIC_ADDITIONAL_C_FLAGS -fsanitize=undefined)
    list(PREPEND PICOQUIC_ADDITIONAL_CXX_FLAGS -fsanitize=undefined)
    list(PREPEND PICOQUIC_LINKER_FLAGS -fsanitize=undefined)

    # Ease detecting of "Runtime errors". If such an error is found, print a verbose
    # error report and exit the program
    cmake_push_check_state()
    set(CMAKE_REQUIRED_LIBRARIES "-fno-sanitize-recover")
    check_c_compiler_flag(-fno-sanitize-recover C__fnosanitize_recover_VALID)
    check_cxx_compiler_flag(-fno-sanitize-recover CXX__fnosanitize_recover_VALID)
    cmake_pop_check_state()
    if(NOT C__fnosanitize_recover_VALID OR NOT CXX__fnosanitize_recover_VALID)
        message(FATAL_ERROR "ENABLE_UBSAN was requested, but fno-sanitize-recover is not supported!")
    endif()
    list(APPEND PICOQUIC_ADDITIONAL_C_FLAGS -fno-sanitize-recover)
    list(APPEND PICOQUIC_ADDITIONAL_CXX_FLAGS -fno-sanitize-recover)
    list(APPEND PICOQUIC_LINKER_FLAGS -fno-sanitize-recover)
endif()

set(PICOQUIC_LIBRARY_FILES
    picoquic/bbr.c
    picoquic/bbr1.c
    picoquic/bytestream.c
    picoquic/cc_common.c
    picoquic/config.c
    picoquic/cubic.c
    picoquic/c4.c
    picoquic/dualq_aqm.c
    picoquic/ech.c
    picoquic/error_names.c
    picoquic/fastcc.c
    picoquic/frames.c
    picoquic/frame_names.c
    picoquic/intformat.c
    picoquic/logger.c
    picoquic/logwriter.c
    picoquic/loss_recovery.c
    picoquic/newreno.c
    picoquic/pacing.c
    picoquic/packet.c
    picoquic/packet_names.c
    picoquic/paths.c
    picoquic/performance_log.c
    picoquic/picohash.c
    picoquic/picoquic_lb.c
    picoquic/picoquic_ptls_fusion.c
    picoquic/picoquic_ptls_minicrypto.c
    picoquic/picoquic_ptls_openssl.c
    picoquic/picoquic_mbedtls.c
    picoquic/picosocks.c
    picoquic/picosplay.c
    picoquic/port_blocking.c
    picoquic/prague.c
    picoquic/qmux.c
    picoquic/quicctx.c
    picoquic/register_all_cc_algorithms.c
    picoquic/sacks.c
    picoquic/sender.c
    picoquic/sim_link.c
    picoquic/siphash.c
    picoquic/sockloop.c
    picoquic/spinbit.c
    picoquic/streams.c
    picoquic/ticket_store.c
    picoquic/timing.c
    picoquic/token_store.c
    picoquic/tls_api.c
    picoquic/tp_names.c
    picoquic/transport.c
    picoquic/unified_log.c
    picoquic/util.c)

set(PICOQUIC_CORE_HEADERS
     picoquic/picoquic.h
     picoquic/picosocks.h
     picoquic/picoquic_utils.h
     picoquic/picoquic_packet_loop.h
     picoquic/picoquic_config.h
     picoquic/picoquic_lb.h
     picoquic/picoquic_newreno.h
     picoquic/picoquic_cubic.h
     picoquic/picoquic_bbr.h
     picoquic/picoquic_bbr1.h
     picoquic/picoquictest_dualq.h
     picoquic/picoquic_fastcc.h
     picoquic/picoquic_prague.h
     picoquic/c4.h
     picoquic/siphash.h)

set(PICOQUIC_CORE_HEADERS_PRIVATE
    picoquic/bytestream.h
    picoquic/cc_common.h
    picoquic/cidset.h
    picoquic/performance_log.h
    picoquic/picohash.h
    picoquic/picoquic_binlog.h
    picoquic/picoquic_crypto_provider_api.h
    picoquic/picoquic_internal.h
    picoquic/picoquic_logger.h
    picoquic/picoquic_set_binlog.h
    picoquic/picoquic_set_textlog.h
    picoquic/picoquic_unified_log.h
    picoquic/picosplay.h
    picoquic/tls_api.h
    picoquic/wincompat.h)

set(LOGLIB_LIBRARY_FILES
    loglib/autoqlog.c
    loglib/cidset.c
    loglib/csv.c
    loglib/logconvert.c
    loglib/logreader.c
    loglib/memory_log.c
    loglib/qlog.c
    loglib/qlog_frames.c
    loglib/qlog_fns.c
    loglib/svg.c)

set(PICOQUIC_LOGLIB_HEADERS
    loglib/autoqlog.h
    loglib/auto_memlog.h)

set(PICOQUIC_TEST_LIBRARY_FILES
    picoquictest/ack_of_ack_test.c
    picoquictest/ack_frequency_test.c
    picoquictest/app_limited.c
    picoquictest/bytestream_test.c
    picoquictest/cc_compete_test.c
    picoquictest/cert_verify_test.c
    picoquictest/cleartext_aead_test.c
    picoquictest/code_version_test.c
    picoquictest/config_test.c
    picoquictest/congestion_test.c
    picoquictest/cnx_creation_test.c
    picoquictest/cnxstress.c
    picoquictest/cplusplus.cpp
    picoquictest/cpu_limited.c
    picoquictest/datagram_tests.c
    picoquictest/delay_tolerant_test.c
    picoquictest/dualq_aqm_test.c
    picoquictest/ech_test.c
    picoquictest/edge_cases.c
    picoquictest/flow_control_test.c
    picoquictest/getter_test.c
    picoquictest/hashtest.c
    picoquictest/high_latency_test.c
    picoquictest/intformattest.c
    picoquictest/l4s_test.c
    picoquictest/mbedtls_test.c
    picoquictest/mediatest.c
    picoquictest/memlog_test.c
    picoquictest/minicrypto_test.c
    picoquictest/multipath_test.c
    picoquictest/netperf_test.c
    picoquictest/openssl_test.c
    picoquictest/p2p_test.c
    picoquictest/pacing_test.c
    picoquictest/parseheadertest.c
    picoquictest/picolog_test.c
    picoquictest/picoquic_lb_test.c
    picoquictest/pn2pn64test.c
    picoquictest/qlog_frame_test.c
    picoquictest/qlog_test.c
    picoquictest/qmux_test.c
    picoquictest/quic_tester.c
    picoquictest/red_aqm.c
    picoquictest/rctl_aqm.c
    picoquictest/sacktest.c
    picoquictest/satellite_test.c
    picoquictest/skip_frame_test.c
    picoquictest/socket_test.c
    picoquictest/sockloop_test.c
    picoquictest/spinbit_test.c
    picoquictest/splay_test.c
    picoquictest/stream0_frame_test.c
    picoquictest/stresstest.c
    picoquictest/ticket_store_test.c
    picoquictest/tls_api_test.c
    picoquictest/transport_param_test.c
    picoquictest/util_test.c
    picoquictest/warptest.c
    picoquictest/wifitest.c )

set(PICOHTTP_LIBRARY_FILES
    picohttp/democlient.c
    picohttp/demoserver.c
    picohttp/h3zero.c
    picohttp/h3zero_client.c
    picohttp/h3zero_common.c
    picohttp/h3zero_server.c
    picohttp/h3zero_uri.c
    picohttp/h3zero_url_template.c
    picohttp/picomask.c
    picohttp/quicperf.c
    picohttp/picoquic_ns.c
    picohttp/webtransport.c
    picohttp/wt_baton.c)

set(PICOHTTP_HEADERS
     picoquic/picosplay.h
     picohttp/h3zero.h
     picohttp/h3zero_common.h
     picohttp/h3zero_uri.h
     picohttp/h3zero_url_template.h
     picohttp/democlient.h
     picohttp/demoserver.h
     picohttp/pico_webtransport.h
     picohttp/picoquic_ns.h
     picohttp/picomask.h
     picohttp/wt_baton.h)

set(PICOHTTP_TEST_LIBRARY_FILES
    picoquictest/h3zerotest.c
    picoquictest/h3zero_stream_test.c
    picoquictest/h3zero_uri_test.c
    picoquictest/picomask_test.c
    picoquictest/quicperf_test.c
    picoquictest/webtransport_test.c)

OPTION(WITH_OPENSSL "build with OpenSSL" ON)
OPTION(WITH_AEGIS "enable AEGIS support through picotls/libaegis" OFF)

OPTION(PICOQUIC_FETCH_PTLS "Fetch PicoTLS during configuration" OFF)
OPTION(PICOQUIC_FETCH_AEGIS "Fetch libaegis during configuration when WITH_AEGIS is enabled" OFF)
OPTION(PICOQUIC_AEGIS_FAVOR_PERFORMANCE "Build fetched libaegis with FAVOR_PERFORMANCE enabled" ON)
OPTION(PICOQUIC_AEGIS_NATIVE_ARCH "Build fetched libaegis with -march=native for this host CPU" OFF)
if(WITH_AEGIS)
    set(PICOQUIC_FETCH_PTLS_DEFAULT_TAG "7c32032f91449d695b24b82955f20d04d47e6cff")
else()
    set(PICOQUIC_FETCH_PTLS_DEFAULT_TAG "bfa67875982afc4c24f21e146cef4747fa189c2f")
endif()
set(PICOQUIC_FETCH_PTLS_TAG "${PICOQUIC_FETCH_PTLS_DEFAULT_TAG}" CACHE STRING "Git tag or commit used when PICOQUIC_FETCH_PTLS is enabled")
set(PICOQUIC_FETCH_AEGIS_TAG "0.10.1" CACHE STRING "Git tag or commit used when PICOQUIC_FETCH_AEGIS is enabled")
if(PICOQUIC_FETCH_PTLS OR (WITH_AEGIS AND PICOQUIC_FETCH_AEGIS))
    include(FetchContent)
endif()
if(WITH_AEGIS AND PICOQUIC_FETCH_AEGIS)
    set(FAVOR_PERFORMANCE ${PICOQUIC_AEGIS_FAVOR_PERFORMANCE} CACHE BOOL "Favor libaegis performance over side-channel mitigations" FORCE)
    FetchContent_Declare(aegis
        GIT_REPOSITORY      https://github.com/aegis-aead/libaegis.git
        GIT_TAG             ${PICOQUIC_FETCH_AEGIS_TAG})
    FetchContent_MakeAvailable(aegis)
    if(PICOQUIC_AEGIS_NATIVE_ARCH AND CMAKE_C_COMPILER_ID MATCHES "Clang|GNU")
        target_compile_options(aegis PRIVATE -march=native)
    endif()
    set(AEGIS_INCLUDE_DIR "${aegis_SOURCE_DIR}/src/include" CACHE PATH "Fetched libaegis include directory" FORCE)
    set(AEGIS_LIBRARY aegis CACHE STRING "Fetched libaegis target" FORCE)
    set(AEGIS_LIBRARIES aegis CACHE STRING "Fetched libaegis target" FORCE)
    set(aegis_LIBRARIES aegis CACHE STRING "Fetched libaegis target" FORCE)
endif()
if(PICOQUIC_FETCH_PTLS)
    if(WITH_AEGIS)
        FetchContent_Declare(picotls
            GIT_REPOSITORY      https://github.com/h2o/picotls.git
            GIT_TAG             ${PICOQUIC_FETCH_PTLS_TAG}
            PATCH_COMMAND       ${CMAKE_COMMAND} -E copy ${CMAKE_CURRENT_SOURCE_DIR}/cmake/Findaegis.cmake <SOURCE_DIR>/cmake/Findaegis.cmake)
    else()
        FetchContent_Declare(picotls
            GIT_REPOSITORY      https://github.com/h2o/picotls.git
            GIT_TAG             ${PICOQUIC_FETCH_PTLS_TAG})
    endif()
    FetchContent_MakeAvailable(picotls)
endif()

set(CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake")
find_package(PTLS REQUIRED)
message(STATUS "picotls/include: ${PTLS_INCLUDE_DIRS}" )
message(STATUS "picotls libraries: ${PTLS_LIBRARIES}" )

if(WITH_AEGIS)
    find_package(aegis REQUIRED)
    if(NOT EXISTS "${AEGIS_INCLUDE_DIR}/aegis.h")
        message(FATAL_ERROR "libaegis found, but aegis.h not found in ${AEGIS_INCLUDE_DIR}")
    endif()
    cmake_push_check_state()
    set(CMAKE_REQUIRED_INCLUDES ${AEGIS_INCLUDE_DIR})
    set(PICOQUIC_SAVED_TRY_COMPILE_TARGET_TYPE ${CMAKE_TRY_COMPILE_TARGET_TYPE})
    set(CMAKE_TRY_COMPILE_TARGET_TYPE STATIC_LIBRARY)
    check_c_source_compiles("
#include <aegis.h>
int main(void)
{
    aegis128l_state st128;
    aegis256_state st256;
    unsigned char in[1] = {0};
    unsigned char out[1] = {0};
    unsigned char tag[16] = {0};
    unsigned char key128[aegis128l_KEYBYTES] = {0};
    unsigned char nonce128[aegis128l_NPUBBYTES] = {0};
    unsigned char key256[aegis256_KEYBYTES] = {0};
    unsigned char nonce256[aegis256_NPUBBYTES] = {0};
    aegis128l_state_init(&st128, 0, 0, nonce128, key128);
    aegis256_state_init(&st256, 0, 0, nonce256, key256);
    return aegis128l_state_encrypt_update(&st128, out, in, sizeof(in)) ||
        aegis128l_state_encrypt_final(&st128, tag, sizeof(tag)) ||
        aegis256_state_encrypt_update(&st256, out, in, sizeof(in)) ||
        aegis256_state_encrypt_final(&st256, tag, sizeof(tag));
}
" PICOQUIC_AEGIS_PICOTLS_API_COMPATIBLE)
    set(CMAKE_TRY_COMPILE_TARGET_TYPE ${PICOQUIC_SAVED_TRY_COMPILE_TARGET_TYPE})
    cmake_pop_check_state()
    if(NOT PICOQUIC_AEGIS_PICOTLS_API_COMPATIBLE)
        message(FATAL_ERROR "WITH_AEGIS requires the libaegis 0.10.1-compatible incremental API used by current picotls. Use the latest upstream libaegis release or set AEGIS_PREFIX to a compatible install.")
    endif()
    message(STATUS "AEGIS include: ${AEGIS_INCLUDE_DIR}")
    message(STATUS "AEGIS library: ${AEGIS_LIBRARIES}")
    list(APPEND PICOQUIC_COMPILE_DEFINITIONS PTLS_HAVE_AEGIS PICOQUIC_WITH_AEGIS)
    list(APPEND PICOQUIC_CRYPTO_EXTRA_INCLUDE_DIRS ${AEGIS_INCLUDE_DIR})
    list(APPEND PICOQUIC_CRYPTO_EXTRA_LIBRARIES ${AEGIS_LIBRARIES})
endif()

OPTION(PTLS_WITH_FUSION "build 'fusion' AES-GCM engine" ${PTLS_WITH_FUSION_DEFAULT})
if(PTLS_WITH_FUSION)
    message(STATUS "PTLS Fusion is enabled")
else()
    message(STATUS "PTLS compiled without support for Fusion")
    list(APPEND PICOQUIC_COMPILE_DEFINITIONS PTLS_WITHOUT_FUSION)
endif()

if(WITH_OPENSSL)
    find_package(OpenSSL REQUIRED)
    message(STATUS "root: ${OPENSSL_ROOT_DIR}")
    message(STATUS "OpenSSL_VERSION: ${OPENSSL_VERSION}")
    message(STATUS "OpenSSL_INCLUDE_DIR: ${OPENSSL_INCLUDE_DIR}")
    message(STATUS "OpenSSL_LIBRARIES: ${OPENSSL_LIBRARIES}")
    message(STATUS "CMAKE_C_FLAGS: ${CMAKE_C_FLAGS}")
    message(STATUS "CMAKE_CXX_FLAGS: ${CMAKE_CXX_FLAGS}")
else()
    message(STATUS "Building without picotls-openssl")
    list(APPEND PICOQUIC_COMPILE_DEFINITIONS PTLS_WITHOUT_OPENSSL)
endif()


OPTION(WITH_SELECT "build with select" OFF)
if(WITH_SELECT)
    message(STATUS "Building with select() instead of poll()")
    list(APPEND PICOQUIC_COMPILE_DEFINITIONS PICOQUIC_USES_SELECT)
endif()

OPTION(WITH_MBEDTLS "enable MBEDTLS" OFF)

IF (WITH_MBEDTLS)
    FIND_PACKAGE(MbedTLS)
    IF (MbedTLS_FOUND)
        message(STATUS "Enabling MbedTLS support")
        message(STATUS "mbedtls/include: ${MBEDTLS_INCLUDE_DIRS}")
        message(STATUS "mbedtls libraries: ${MBEDTLS_LIBRARIES}")
        list(APPEND PICOQUIC_COMPILE_DEFINITIONS PICOQUIC_WITH_MBEDTLS)
        list(APPEND PICOQUIC_LIBRARY_FILES 
                    picoquic_mbedtls/ptls_mbedtls.c
                    picoquic_mbedtls/ptls_mbedtls_sign.c)
    ELSE ()
        message(STATUS "mbedtls/include: ${MBEDTLS_INCLUDE_DIRS}")
        message(STATUS "mbedtls libraries: ${MBEDTLS_LIBRARIES}")
        MESSAGE (FATAL_ERROR "MbedTLS not found")
    ENDIF()
ENDIF ()

OPTION(WITH_IO_URING "enable IO_URING" OFF)
IF (WITH_IO_URING)
    message(STATUS "Enabling IO_URING support")
    find_library(LIBURING NAMES uring)  
    if (NOT LIBURING)  
        message(FATAL_ERROR "liburing not found")
    else()
        message(STATUS "Found liburing: ${LIBURING}")
        list(APPEND PICOQUIC_COMPILE_DEFINITIONS PICOQUIC_WITH_IO_URING)
    endif()
ENDIF ()

OPTION(WITH_THREAD_CHECK "enable thread check" OFF)
if (WITH_THREAD_CHECK)
    message(STATUS "Enabling thread check")
    list(APPEND PICOQUIC_COMPILE_DEFINITIONS PICOQUIC_WITH_THREAD_CHECK)
endif ()

# set_picoquic_compile_settings(TARGET) makes is easy to consistently
# assign compiler build options to each of the following targets
macro(set_picoquic_compile_settings)
    set_target_properties(${ARGV0}
        PROPERTIES
            C_STANDARD 11
            C_STANDARD_REQUIRED YES
            C_EXTENSIONS YES)
    set_target_properties(${ARGV0}
        PROPERTIES
            CXX_STANDARD 11
            CXX_STANDARD_REQUIRED YES
            CXX_EXTENSIONS YES)
    target_compile_options(${ARGV0}
        PRIVATE
            $<$<C_COMPILER_ID:Clang>: -O3 -Wall -fno-exceptions
                -fno-signed-zeros -fno-trapping-math
                ${PICOQUIC_ADDITIONAL_C_FLAGS}>
            $<$<C_COMPILER_ID:AppleClang>: -O3 -Wall -fno-exceptions
                -fno-signed-zeros -fno-trapping-math
                ${PICOQUIC_ADDITIONAL_C_FLAGS}>
            $<$<C_COMPILER_ID:GNU>: -O3 -Wall -fno-exceptions
                -fno-signed-zeros -frename-registers -fno-trapping-math
                ${PICOQUIC_ADDITIONAL_C_FLAGS}>
            $<$<C_COMPILER_ID:MSVC>: >
            $<$<CXX_COMPILER_ID:Clang>: -O3 -Wall -fno-exceptions
                -fno-signed-zeros -fno-trapping-math
                ${PICOQUIC_ADDITIONAL_CXX_FLAGS}>
            $<$<CXX_COMPILER_ID:AppleClang>: -O3 -Wall -fno-exceptions
                -fno-signed-zeros -fno-trapping-math
                ${PICOQUIC_ADDITIONAL_CXX_FLAGS}>
            $<$<CXX_COMPILER_ID:GNU>: -O3 -Wall -fno-exceptions
                -fno-signed-zeros -frename-registers -fno-trapping-math
                ${PICOQUIC_ADDITIONAL_CXX_FLAGS}>
            $<$<CXX_COMPILER_ID:MSVC>: >)
    target_compile_definitions(${ARGV0} PRIVATE ${PICOQUIC_COMPILE_DEFINITIONS})
    target_link_options(${ARGV0} PRIVATE ${PICOQUIC_LINKER_FLAGS})
endmacro()

add_library(picoquic-core ${PICOQUIC_CORE_HEADERS} ${PICOQUIC_LIBRARY_FILES})
target_sources(picoquic-core PRIVATE ${PICOQUIC_CORE_HEADERS_PRIVATE})
add_library(picoquic::picoquic-core ALIAS picoquic-core)

message(STATUS "Defining picoquic-core")
message(STATUS "mbedtls/include: ${MBEDTLS_INCLUDE_DIRS}")

target_include_directories(picoquic-core
    PRIVATE
        ${PTLS_INCLUDE_DIRS}
        ${OPENSSL_INCLUDE_DIR}
        ${PICOQUIC_CRYPTO_EXTRA_INCLUDE_DIRS}
    PUBLIC
        $<BUILD_INTERFACE:${MBEDTLS_INCLUDE_DIRS}>
        $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/picoquic>
        $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/c4/src>
        $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/picoquic_mbedtls>
        $<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>)
target_link_libraries(picoquic-core
    PRIVATE
        ${LIBURING}
        ${OPENSSL_LIBRARIES}
        ${MBEDTLS_LIBRARIES}
    PUBLIC
        ${PTLS_LIBRARIES}
        ${PICOQUIC_CRYPTO_EXTRA_LIBRARIES}
        Threads::Threads)
set_picoquic_compile_settings(picoquic-core)

if (BUILD_DEMO OR BUILD_LOGREADER OR (BUILD_TESTING AND picoquic_BUILD_TESTS))
    if (NOT BUILD_LOGLIB)
        set(BUILD_LOGLIB ON)
    endif()
endif()

if (BUILD_LOGLIB)
    add_library(picoquic-log ${LOGLIB_LIBRARY_FILES})
    add_library(picoquic::picoquic-log ALIAS picoquic-log)
    target_link_libraries(picoquic-log PUBLIC picoquic-core)
    target_include_directories(picoquic-log
        PRIVATE
            ${PTLS_INCLUDE_DIRS}
        PUBLIC
            $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/picoquic>
            $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/loglib>
            $<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>)
    set_picoquic_compile_settings(picoquic-log)
endif()

if (BUILD_DEMO)
    if (NOT BUILD_HTTP)
        set(BUILD_HTTP ON)
    endif()
endif()

if (BUILD_HTTP)
    add_library(picohttp-core ${PICOHTTP_LIBRARY_FILES})
    add_library(picoquic::picohttp-core ALIAS picohttp-core)
    target_link_libraries(picohttp-core
        PRIVATE
            ${PTLS_LIBRARIES}
            ${OPENSSL_LIBRARIES}
            ${MBEDTLS_LIBRARIES}
        PUBLIC
            picoquic-core
            picoquic-log)
    target_include_directories(picohttp-core
        PRIVATE
            ${PTLS_INCLUDE_DIRS}
            ${OPENSSL_INCLUDE_DIR}
            ${MBEDTLS_INCLUDE_DIRS}
        PUBLIC
            $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/picoquic>
            $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/picohttp>
            $<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>)
    set_picoquic_compile_settings(picohttp-core)
endif()

if (BUILD_DEMO)
    add_executable(picoquicdemo
        picoquicfirst/picoquicdemo.c
        picoquicfirst/getopt.c)
    target_link_libraries(picoquicdemo
        PUBLIC
            ${PTLS_LIBRARIES}
            ${OPENSSL_LIBRARIES}
            ${MBEDTLS_LIBRARIES}
            picoquic-log
            picoquic-core
            picohttp-core)
    target_include_directories(picoquicdemo PRIVATE picohttp)
    set_picoquic_compile_settings(picoquicdemo)
endif()

if(BUILD_PQBENCH)
    add_executable(pqbench
        pqbench_app/pqbench.c
        picoquicfirst/getopt.c)
    target_link_libraries(pqbench
        PUBLIC
            ${PTLS_LIBRARIES}
            ${OPENSSL_LIBRARIES}
            ${MBEDTLS_LIBRARIES}
            picoquic-log
            picoquic-core
            picohttp-core)
    target_include_directories(pqbench PRIVATE picohttp loglib picoquic)
    set_picoquic_compile_settings(pqbench)
endif()

if(BUILD_PICO_SIM AND BUILD_HTTP)
    add_executable(pico_sim
        p_sim/pico_sim.c
        picoquicfirst/getopt.c)
    target_link_libraries(pico_sim
        PUBLIC
            ${PTLS_LIBRARIES}
            ${OPENSSL_LIBRARIES}
            ${MBEDTLS_LIBRARIES}
            picohttp-core
            picoquic-core
            picoquic-log)
    target_include_directories(pico_sim PRIVATE picoquicfirst picohttp loglib picoquic)
    set_picoquic_compile_settings(pico_sim)
endif()

if (BUILD_LOGREADER)
    add_executable(picolog_t picolog/picolog.c)
    target_link_libraries(picolog_t PRIVATE picoquic-log picoquic-core)
    target_include_directories(picolog_t PRIVATE loglib)
    set_picoquic_compile_settings(picolog_t)
endif()

include(CTest)

if(BUILD_TESTING AND picoquic_BUILD_TESTS)

    add_library(picoquic-test STATIC ${PICOQUIC_TEST_LIBRARY_FILES})
    target_link_libraries(picoquic-test PUBLIC picoquic-core picoquic-log picohttp-core)
    target_include_directories(picoquic-test
        PRIVATE
            ${MBEDTLS_INCLUDE_DIRS}
        PUBLIC
            ${PTLS_INCLUDE_DIRS}
            picoquic
            picohttp
            picoquictest)
    set_picoquic_compile_settings(picoquic-test)

    add_executable(picoquic_ct picoquic_t/picoquic_t.c)
    
    target_link_libraries(picoquic_ct
        PUBLIC
            ${PTLS_LIBRARIES}
            ${OPENSSL_LIBRARIES}
            ${MBEDTLS_LIBRARIES}
            picoquic-test
            picoquic-log
            picohttp-core
            picoquic-core)

    set_picoquic_compile_settings(picoquic_ct)

    add_executable(aegisperf picoquictest/aegis_perf.c)

    target_link_libraries(aegisperf
        PUBLIC
            ${PTLS_LIBRARIES}
            ${OPENSSL_LIBRARIES}
            ${MBEDTLS_LIBRARIES}
            picoquic-test
            picoquic-log
            picohttp-core
            picoquic-core)

    target_include_directories(aegisperf
        PRIVATE
            ${PTLS_INCLUDE_DIRS}
            picoquic
            picohttp
            picoquictest)

    set_picoquic_compile_settings(aegisperf)

    add_executable(picohttp_ct
        picohttp_t/picohttp_t.c
        ${PICOHTTP_TEST_LIBRARY_FILES})
        
    target_link_libraries(picohttp_ct
        PUBLIC
            ${PTLS_LIBRARIES}
            ${OPENSSL_LIBRARIES}
            ${MBEDTLS_LIBRARIES}
            picoquic-test
            picoquic-log
            picohttp-core
            picoquic-core)

    target_include_directories(picohttp_ct PRIVATE picohttp)
    set_picoquic_compile_settings(picohttp_ct)

    add_executable(pico_baton baton_app/baton_app.c)
    target_link_libraries(pico_baton PRIVATE picoquic-log picoquic-core picohttp-core)
    target_include_directories(pico_baton PRIVATE loglib picoquic picohttp)
    set_picoquic_compile_settings(pico_baton)

    add_executable(picoquic_sample
        sample/sample.c
        sample/sample_background.c
        sample/sample_client.c
        sample/sample_server.c)
    target_link_libraries(picoquic_sample PRIVATE picoquic-log picoquic-core)
    target_include_directories(picoquic_sample PRIVATE loglib picoquic)
    set_picoquic_compile_settings(picoquic_sample)

    add_test(NAME picoquic_ct
             COMMAND picoquic_ct -S ${PROJECT_SOURCE_DIR} -n -r)
    add_test(NAME picohttp_ct
             COMMAND picohttp_ct -S ${PROJECT_SOURCE_DIR} -n -r)

    add_executable(thread_test
        thread_tester/thread_test.c)
    target_link_libraries(thread_test PRIVATE picoquic-log picoquic-core)
    target_include_directories(thread_test PRIVATE loglib picoquic)
    set_picoquic_compile_settings(thread_test)

endif()

# get all project files for formatting
file(GLOB_RECURSE CLANG_FORMAT_SOURCE_FILES *.c *.h)

# Adds clangformat as target that formats all source files
add_custom_target(
    clangformat
    COMMAND clang-format
    -style=Webkit
    -i
    ${CLANG_FORMAT_SOURCE_FILES})

if (NOT CMAKE_INSTALL_INCLUDEDIR)
    set(CMAKE_INSTALL_INCLUDEDIR ${CMAKE_INSTALL_PREFIX}/include)
endif()

if (NOT CMAKE_INSTALL_LIBDIR)
    set(CMAKE_INSTALL_LIBDIR ${CMAKE_INSTALL_PREFIX}/lib)
endif()

if (NOT CMAKE_INSTALL_BINDIR)
    set(CMAKE_INSTALL_BINDIR ${CMAKE_INSTALL_PREFIX}/bin)
endif()

if (TARGET picoquicdemo)
    install(TARGETS picoquicdemo
        RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR})
endif()

if (TARGET picohttp-core)
    install(TARGETS picohttp-core
        EXPORT picoquic-targets
        ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
        INCLUDES DESTINATION ${CMAKE_INSTALL_INCLUDEDIR})
    install(FILES
        ${PICOHTTP_HEADERS}
        DESTINATION ${CMAKE_INSTALL_INCLUDEDIR})
endif()

if (TARGET picolog_t)
    install(TARGETS picolog_t
        RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR})
endif()

if (TARGET picoquic-log)
    install(TARGETS picoquic-log
        EXPORT picoquic-targets
        ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
        INCLUDES DESTINATION ${CMAKE_INSTALL_INCLUDEDIR})

    install(FILES
        ${PICOQUIC_LOGLIB_HEADERS}
        DESTINATION ${CMAKE_INSTALL_INCLUDEDIR})
endif()


install(TARGETS picoquic-core
    EXPORT picoquic-targets
    ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
    INCLUDES DESTINATION ${CMAKE_INSTALL_INCLUDEDIR})

if(PICOQUIC_FETCH_PTLS)
    set(LIB_PATH "${CMAKE_INSTALL_LIBDIR}/libpicoquic-core.a")
    foreach(_ptls_lib ${PTLS_LIBRARIES})
        string(APPEND LIB_PATH ";${CMAKE_INSTALL_LIBDIR}/lib${_ptls_lib}.a")
    endforeach()
    set(LIB_PATH "${LIB_PATH}" CACHE PATH "Path of library files")

    install(TARGETS ${PTLS_LIBRARIES}
        EXPORT picoquic-targets
        ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR})
else()
    set(LIB_PATH "${CMAKE_INSTALL_LIBDIR}/libpicoquic-core.a" CACHE PATH "Path of library file")
endif()

install(EXPORT picoquic-targets
    FILE picoquic-targets.cmake
    NAMESPACE picoquic::
    DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/picoquic)

install(FILES
        ${PICOQUIC_CORE_HEADERS}
        DESTINATION ${CMAKE_INSTALL_INCLUDEDIR})

include(CMakePackageConfigHelpers)

execute_process(
    COMMAND git rev-parse --short HEAD
    WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
    OUTPUT_VARIABLE GIT_HASH
    OUTPUT_STRIP_TRAILING_WHITESPACE
)

set(PROJECT_VERSION "0.0.0-${GIT_HASH}")
message(STATUS "Project version: ${PROJECT_VERSION}")

if (NOT CMAKE_INSTALL_LIBDIR)
    set(CMAKE_INSTALL_LIBDIR ${CMAKE_INSTALL_PREFIX}/lib)
endif()

set(INCLUDE_INSTALL_DIR ${CMAKE_INSTALL_INCLUDEDIR} CACHE PATH "Location of header files")

set(INSTALL_CONFIG_DIR ${CMAKE_INSTALL_LIBDIR}/cmake/${PROJECT_NAME})

configure_package_config_file(${PROJECT_NAME}-config.cmake.in ${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}-config.cmake PATH_VARS PROJECT_VERSION LIB_PATH INCLUDE_INSTALL_DIR INSTALL_DESTINATION ${INSTALL_CONFIG_DIR})
write_basic_package_version_file(${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}-config-version.cmake VERSION ${PROJECT_VERSION} COMPATIBILITY ExactVersion)

install(FILES ${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}-config.cmake ${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}-config-version.cmake DESTINATION ${INSTALL_CONFIG_DIR})
