cmake_minimum_required(VERSION 3.21)

list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_LIST_DIR}/cmake")
include(DynamicVersion)

extract_version_string(
    HEADER_FILE ${CMAKE_CURRENT_LIST_DIR}/include/httpp.h
    VERSION_PREFIX HTTPP_
    OUTPUT_VAR PROJECT_VERSION
)

project(httpp LANGUAGES CXX VERSION ${PROJECT_VERSION})
message(STATUS "Project: ${PROJECT_NAME}@v${PROJECT_VERSION}")
include(Startup)

set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_CXX_EXTENSIONS OFF)

include(Optimize)

# Project options
option(HTTPP_BUILD_TESTS "Build tests" ${PROJECT_IS_TOP_LEVEL})
option(HTTPP_BUILD_PYTHON "Build Python bindings" ${PROJECT_IS_TOP_LEVEL})

set(HTTPP_SOURCE_DIR ${PROJECT_SOURCE_DIR})

# --- httpp C++ core ---
# httplib.h is vendored at src/third_party/cpp-httplib (a real committed
# file, no system package). mbedtls is a git submodule at
# src/third_party/mbedtls (also built from source, no system package).
# Both are included only from src/core/*.cpp, never from a public
# include/httpp/*.hpp header, and are compiled with hidden visibility so
# they never leak into the httpp .so/.a's public symbols.
file(GLOB HTTPP_CORE_SOURCES ${PROJECT_SOURCE_DIR}/src/core/*.cpp)

# liburlparser (git submodule, built from source — no system package) does
# the real URL parsing (scheme/host/port/path, IPv4/IPv6, PSL-aware). It is
# linked PRIVATE into httpp_core, same as cpp-httplib: consumers of
# <httpp/url.hpp> see only httpp::url, never urlparser::url directly.
set(BUILD_PYTHON OFF CACHE BOOL "" FORCE)
# Needed so liburlparser's static lib can be linked into httpp's shared
# lib / the Cython extension module (which are themselves PIC).
set(CMAKE_POSITION_INDEPENDENT_CODE ON)
add_subdirectory(${PROJECT_SOURCE_DIR}/src/third_party/liburlparser)

# mbedtls (TLS support for HTTPS, via cpp-httplib's CPPHTTPLIB_MBEDTLS_SUPPORT).
set(ENABLE_PROGRAMS OFF CACHE BOOL "" FORCE)
set(ENABLE_TESTING OFF CACHE BOOL "" FORCE)
set(MBEDTLS_FATAL_WARNINGS OFF CACHE BOOL "" FORCE)
set(USE_STATIC_MBEDTLS_LIBRARY ON CACHE BOOL "" FORCE)
set(USE_SHARED_MBEDTLS_LIBRARY OFF CACHE BOOL "" FORCE)
# mbedtls's codegen'd sources (library/error.c,
# tf-psa-crypto/core/psa_crypto_driver_wrappers_no_static.c, etc.) must
# exist on disk before add_subdirectory() below runs. mbedtls generates
# them itself via its own GEN_FILES option on Linux/macOS, but defaults
# GEN_FILES OFF on any Windows host (assuming a release tarball with these
# files already bundled). We build mbedtls from a git submodule, which
# never has them on any platform, so we generate them ourselves here,
# unconditionally, using mbedtls's own official scripts — the approach
# mbedtls's maintainers recommend: https://github.com/Mbed-TLS/mbedtls/issues/10678.
# Requires Python 3 and Perl on PATH (both are already required to build
# mbedtls from a git checkout, per its own documentation).
set(_httpp_mbedtls_dir ${PROJECT_SOURCE_DIR}/src/third_party/mbedtls)
find_package(Python3 COMPONENTS Interpreter REQUIRED)
execute_process(
    COMMAND ${Python3_EXECUTABLE} framework/scripts/make_generated_files.py
    WORKING_DIRECTORY ${_httpp_mbedtls_dir}/tf-psa-crypto
    RESULT_VARIABLE _httpp_mbedtls_gen_result
)
if(NOT _httpp_mbedtls_gen_result EQUAL 0)
    message(FATAL_ERROR "Failed to generate tf-psa-crypto sources (exit ${_httpp_mbedtls_gen_result})")
endif()
execute_process(
    COMMAND ${Python3_EXECUTABLE} scripts/make_generated_files.py
    WORKING_DIRECTORY ${_httpp_mbedtls_dir}
    RESULT_VARIABLE _httpp_mbedtls_gen_result
)
if(NOT _httpp_mbedtls_gen_result EQUAL 0)
    message(FATAL_ERROR "Failed to generate mbedtls sources (exit ${_httpp_mbedtls_gen_result})")
endif()
# mbedtls's own install() rules are unconditional unless this is forced ON
# (its "am I a subproject" auto-detection doesn't reliably suppress them);
# httpp links it in statically and never re-exports its headers/libs, so
# nothing from mbedtls itself belongs in the installed wheel.
set(DISABLE_PACKAGE_CONFIG_AND_INSTALL ON CACHE BOOL "" FORCE)
add_subdirectory(${PROJECT_SOURCE_DIR}/src/third_party/mbedtls)

add_library(httpp_core SHARED ${HTTPP_CORE_SOURCES})
add_library(httpp::core ALIAS httpp_core)

# Windows/MSVC dllexport-vs-dllimport: HTTPP_API (include/httpp/export.hpp)
# resolves to __declspec(dllexport) only when HTTPP_BUILDING_DLL is defined.
# It must be defined while compiling httpp_core's OWN translation units
# (client.cpp/server.cpp/url.cpp) so the public classes are actually
# exported from httpp_core.dll — and must NOT be defined for anyone
# consuming the already-built DLL (they need dllimport instead), hence
# PRIVATE, not PUBLIC/INTERFACE.
target_compile_definitions(httpp_core PRIVATE HTTPP_BUILDING_DLL)

set_target_properties(httpp_core PROPERTIES
    POSITION_INDEPENDENT_CODE ON
    CXX_VISIBILITY_PRESET hidden
    VISIBILITY_INLINES_HIDDEN ON
    MACOSX_RPATH ON
    INSTALL_NAME_DIR "@rpath"
)

target_include_directories(httpp_core
    PUBLIC
        $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
        $<INSTALL_INTERFACE:httpp/include>
)
# SYSTEM: these are vendored/third-party headers we don't control (cpp-httplib,
# indicators, etc.). Marking them SYSTEM tells the compiler their own internal
# warnings (e.g. indicators.hpp's narrowing-conversion C4244/C4267 on MSVC)
# aren't ours to fix and shouldn't surface under our own -Wall/-Wextra/-W4
# below — without patching vendored code to silence its own warnings.
target_include_directories(httpp_core SYSTEM PRIVATE
    ${CMAKE_CURRENT_SOURCE_DIR}/src/third_party/cpp-httplib
    ${CMAKE_CURRENT_SOURCE_DIR}/src/third_party
)

find_package(Threads REQUIRED)
target_link_libraries(httpp_core PUBLIC Threads::Threads)
target_link_libraries(httpp_core PRIVATE url::base)

# HTTPS support: mbedtls is linked PRIVATE and CPPHTTPLIB_MBEDTLS_SUPPORT is
# only ever defined in src/core/internal/httplib_common.hpp (never in a
# public header), so consumers of httpp never need mbedtls on their own
# include/link path — see that file for the shared httplib.h include.
target_link_libraries(httpp_core PRIVATE mbedtls mbedx509)
target_compile_definitions(httpp_core PRIVATE CPPHTTPLIB_MBEDTLS_SUPPORT)

# macOS: cpp-httplib (src/third_party/cpp-httplib/httplib.h) reads the
# system trust store via the Keychain APIs (CPPHTTPLIB_USE_CERTS_FROM_MACOSX_KEYCHAIN),
# which pulls in CoreFoundation/Security symbols (e.g. SecTrustSettingsCopyCertificates).
# These aren't linked by default and must be added explicitly, or the final
# link of libhttpp_core.dylib fails with "symbol(s) not found for architecture".
if(APPLE)
    find_library(COREFOUNDATION_LIBRARY CoreFoundation REQUIRED)
    find_library(SECURITY_LIBRARY Security REQUIRED)
    target_link_libraries(httpp_core PRIVATE ${COREFOUNDATION_LIBRARY} ${SECURITY_LIBRARY})
endif()

target_compile_options(httpp_core PRIVATE
    $<$<CXX_COMPILER_ID:MSVC>:/W4 /EHsc>
    $<$<NOT:$<CXX_COMPILER_ID:MSVC>>:-Wall -Wextra -Wpedantic>
)

apply_optimization_flags(httpp_core)

if(HTTPP_BUILD_PYTHON OR DEFINED SKBUILD)
    set(PYTHON_INSTALL_DIR ${CMAKE_BINARY_DIR}/python/install)
    add_subdirectory(${PROJECT_SOURCE_DIR}/src/bindings/python)
    if(DEFINED SKBUILD)
        return()
    endif()
endif()

# Installation
install(TARGETS httpp_core
    ARCHIVE DESTINATION lib
    LIBRARY DESTINATION lib
)
install(DIRECTORY include DESTINATION .)

# Tests
if(HTTPP_BUILD_TESTS)
    enable_testing()
    set(CMAKE_CTEST_ARGUMENTS "--output-on-failure" "-V")
    add_subdirectory(${PROJECT_SOURCE_DIR}/tests)
endif()
