# All library sources consolidated into a single target.
# Linker --gc-sections handles dead code elimination for consumers that only
# use a subset of the stack (~570K total, 30 object files).
set(OPENSOMEIP_SOURCES
    common/result.cpp
    someip/types.cpp
    someip/message.cpp
    core/session_manager.cpp
    serialization/serializer.cpp
    transport/endpoint.cpp
    transport/udp_transport.cpp
    transport/tcp_transport.cpp
    transport/event_driven_udp_transport.cpp
    transport/event_driven_tcp_transport.cpp
    e2e/e2e_protection.cpp
    e2e/e2e_header.cpp
    e2e/e2e_crc.cpp
    e2e/e2e_profile_registry.cpp
    e2e/e2e_profiles/standard_profile.cpp
    rpc/rpc_client.cpp
    rpc/rpc_server.cpp
    sd/sd_message.cpp
    sd/sd_client.cpp
    sd/sd_server.cpp
    events/event_publisher.cpp
    events/event_subscriber.cpp
    tp/tp_segmenter.cpp
    tp/tp_reassembler.cpp
    tp/tp_manager.cpp
)

# --- Platform backend selection via include path ---
# Threading / memory backend
if(SOMEIP_USE_FREERTOS)
    set(SOMEIP_THREADING_IMPL_DIR ${PROJECT_SOURCE_DIR}/include/platform/freertos)
elseif(SOMEIP_USE_THREADX)
    set(SOMEIP_THREADING_IMPL_DIR ${PROJECT_SOURCE_DIR}/include/platform/threadx)
elseif(WIN32)
    set(SOMEIP_THREADING_IMPL_DIR ${PROJECT_SOURCE_DIR}/include/platform/win32)
else()
    set(SOMEIP_THREADING_IMPL_DIR ${PROJECT_SOURCE_DIR}/include/platform/posix)
endif()

# Networking backend
if(SOMEIP_USE_LWIP)
    set(SOMEIP_NET_IMPL_DIR ${PROJECT_SOURCE_DIR}/include/platform/lwip)
elseif(WIN32)
    set(SOMEIP_NET_IMPL_DIR ${PROJECT_SOURCE_DIR}/include/platform/win32)
else()
    set(SOMEIP_NET_IMPL_DIR ${PROJECT_SOURCE_DIR}/include/platform/posix)
endif()

# Allocation backend (listed before threading so static memory_impl.h shadows RTOS headers)
if(SOMEIP_USE_STATIC_ALLOC)
    set(SOMEIP_ALLOC_IMPL_DIR ${PROJECT_SOURCE_DIR}/include/platform/static)
else()
    set(SOMEIP_ALLOC_IMPL_DIR ${PROJECT_SOURCE_DIR}/include/platform/dynamic)
endif()

# Library type follows BUILD_SHARED_LIBS (default: STATIC)
add_library(opensomeip ${OPENSOMEIP_SOURCES})

# Map selected PAL backend dirs onto the installed include tree so
# find_package(opensomeip) can resolve buffer_pool_impl.h / net_impl.h.
file(RELATIVE_PATH _OPENSOMEIP_ALLOC_REL "${PROJECT_SOURCE_DIR}/include" "${SOMEIP_ALLOC_IMPL_DIR}")
file(RELATIVE_PATH _OPENSOMEIP_THREAD_REL "${PROJECT_SOURCE_DIR}/include" "${SOMEIP_THREADING_IMPL_DIR}")
file(RELATIVE_PATH _OPENSOMEIP_NET_REL "${PROJECT_SOURCE_DIR}/include" "${SOMEIP_NET_IMPL_DIR}")

target_include_directories(opensomeip PUBLIC
    $<BUILD_INTERFACE:${PROJECT_SOURCE_DIR}/include>
    $<BUILD_INTERFACE:${SOMEIP_ALLOC_IMPL_DIR}>
    $<BUILD_INTERFACE:${SOMEIP_THREADING_IMPL_DIR}>
    $<BUILD_INTERFACE:${SOMEIP_NET_IMPL_DIR}>
    $<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}/someip>
    $<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}/someip/${_OPENSOMEIP_ALLOC_REL}>
    $<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}/someip/${_OPENSOMEIP_THREAD_REL}>
    $<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}/someip/${_OPENSOMEIP_NET_REL}>
)

# Threading: only link pthreads on non-RTOS builds
if(NOT SOMEIP_USE_FREERTOS AND NOT SOMEIP_USE_THREADX)
    find_package(Threads REQUIRED)
    target_link_libraries(opensomeip PUBLIC Threads::Threads)
endif()

# RTOS kernel linkage
if(SOMEIP_USE_FREERTOS AND TARGET freertos_kernel)
    target_link_libraries(opensomeip PUBLIC freertos_kernel)
endif()
if(SOMEIP_USE_THREADX AND TARGET threadx)
    target_link_libraries(opensomeip PUBLIC threadx)
endif()

if(WIN32)
    target_link_libraries(opensomeip PUBLIC ws2_32)
endif()

# FreeRTOS platform sources (skipped when static allocation owns memory backend)
if(SOMEIP_USE_FREERTOS AND NOT SOMEIP_USE_STATIC_ALLOC)
    target_sources(opensomeip PRIVATE
        ${CMAKE_CURRENT_SOURCE_DIR}/platform/freertos/memory.cpp)
endif()

# ThreadX platform sources (skipped when static allocation owns memory backend)
if(SOMEIP_USE_THREADX AND NOT SOMEIP_USE_STATIC_ALLOC)
    target_sources(opensomeip PRIVATE
        ${CMAKE_CURRENT_SOURCE_DIR}/platform/threadx/memory.cpp)
endif()

# Static allocation platform sources
if(SOMEIP_USE_STATIC_ALLOC)
    target_sources(opensomeip PRIVATE
        ${CMAKE_CURRENT_SOURCE_DIR}/platform/static/memory.cpp
        ${CMAKE_CURRENT_SOURCE_DIR}/platform/static/buffer_pool.cpp
        ${CMAKE_CURRENT_SOURCE_DIR}/platform/static/etl_error_handler.cpp)
    target_link_libraries(opensomeip PUBLIC etl::etl)

    # ETL safety configuration: disable exceptions, enable error logging.
    # The custom error handler (when registered) surfaces container overflow
    # as a return code rather than abort() — required for FMEA compliance.
    target_compile_definitions(opensomeip PUBLIC
        SOMEIP_STATIC_ALLOC
        ETL_LOG_ERRORS=1
        ETL_CHECK_PUSH_POP
    )

    # Propagate optional pool-sizing cache variables as compile definitions so
    # static_config.h picks them up.  This avoids stuffing -D flags into
    # CMAKE_CXX_FLAGS (which overrides CMAKE_CXX_FLAGS_INIT from the
    # toolchain and can strip essential -mcpu / -mthumb flags).
    foreach(_pool_var
        SOMEIP_MESSAGE_POOL_SIZE
        SOMEIP_BYTE_POOL_SMALL_COUNT
        SOMEIP_BYTE_POOL_SMALL_SIZE
        SOMEIP_BYTE_POOL_MEDIUM_COUNT
        SOMEIP_BYTE_POOL_MEDIUM_SIZE
        SOMEIP_BYTE_POOL_LARGE_COUNT
        SOMEIP_BYTE_POOL_LARGE_SIZE
        SOMEIP_MAX_TP_SEGMENTS
        SOMEIP_MAX_TCP_CONNECTIONS
        SOMEIP_MAX_TP_REASSEMBLY_SIZE
        SOMEIP_MAX_SD_OPTIONS
        SOMEIP_DEFAULT_MAP_CAPACITY
        SOMEIP_DEFAULT_VECTOR_CAPACITY
        SOMEIP_DEFAULT_QUEUE_CAPACITY
        SOMEIP_PIMPL_EVENTPUB_SIZE
        SOMEIP_PIMPL_EVENTSUB_SIZE
        SOMEIP_PIMPL_RPCCLIENT_SIZE
        SOMEIP_PIMPL_RPCSERVER_SIZE
        SOMEIP_PIMPL_SDCLIENT_SIZE
        SOMEIP_PIMPL_SDSERVER_SIZE
        SOMEIP_FREERTOS_HEAP_SIZE)
        if(DEFINED ${_pool_var})
            target_compile_definitions(opensomeip PUBLIC ${_pool_var}=${${_pool_var}})
        endif()
    endforeach()

    # GCC 13 false-positive (https://gcc.gnu.org/bugzilla/show_bug.cgi?id=109561):
    # std::variant move-constructor inlining triggers -Wmaybe-uninitialized on
    # SD entry/option members despite {0} default member initializers.
    if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
        set_source_files_properties(
            ${CMAKE_CURRENT_SOURCE_DIR}/sd/sd_message.cpp
            ${CMAKE_CURRENT_SOURCE_DIR}/sd/sd_client.cpp
            ${CMAKE_CURRENT_SOURCE_DIR}/sd/sd_server.cpp
            PROPERTIES COMPILE_OPTIONS "-Wno-maybe-uninitialized")
    endif()

    # Malloc trap object library (linked into verification tests, not into main lib).
    # Only built on hosted platforms — requires <dlfcn.h> (unavailable on bare-metal).
    if(NOT CMAKE_CROSSCOMPILING)
        add_library(someip_malloc_trap OBJECT
            ${CMAKE_CURRENT_SOURCE_DIR}/platform/static/malloc_trap.cpp)
        target_compile_definitions(someip_malloc_trap PRIVATE SOMEIP_MALLOC_TRAP)
    endif()
endif()

# C API wrapper library (extern "C" FFI boundary)
if(BUILD_CAPI)
    set(OPENSOMEIP_CAPI_SOURCES
        capi/opensomeip_message.cpp
        capi/opensomeip_serializer.cpp
        capi/opensomeip_transport.cpp
        capi/opensomeip_rpc.cpp
        capi/opensomeip_sd.cpp
        capi/opensomeip_events.cpp
        capi/opensomeip_tp.cpp
        capi/opensomeip_e2e.cpp
    )

    add_library(opensomeip_capi ${OPENSOMEIP_CAPI_SOURCES})
    target_link_libraries(opensomeip_capi PUBLIC opensomeip)
    target_include_directories(opensomeip_capi PUBLIC
        $<BUILD_INTERFACE:${PROJECT_SOURCE_DIR}/include>
        $<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}/someip>
    )
    target_include_directories(opensomeip_capi PRIVATE
        ${CMAKE_CURRENT_SOURCE_DIR}/capi
    )
    set_target_properties(opensomeip_capi PROPERTIES
        VERSION ${PROJECT_VERSION}
        SOVERSION ${PROJECT_VERSION_MAJOR}
        POSITION_INDEPENDENT_CODE ON
        C_STANDARD 11
    )
    if(BUILD_SHARED_LIBS)
        set_target_properties(opensomeip_capi PROPERTIES
            CXX_VISIBILITY_PRESET hidden
            VISIBILITY_INLINES_HIDDEN ON
        )
        target_compile_definitions(opensomeip_capi
            PUBLIC  OPENSOMEIP_CAPI_SHARED
            PRIVATE OPENSOMEIP_CAPI_BUILDING
        )
    endif()
    # The CAPI wrapper uses try/catch as an exception firewall (every entry
    # point catches C++ exceptions and converts them to error codes).  ARM
    # cross-compile toolchains set -fno-exceptions globally; re-enable for
    # this translation unit so the firewall compiles.
    if(CMAKE_CXX_COMPILER_ID MATCHES "GNU|Clang")
        target_compile_options(opensomeip_capi PRIVATE -fexceptions)
    endif()
endif()

# Backward-compatible aliases so existing target_link_libraries() calls
# continue to work (e.g. target_link_libraries(my_app someip-rpc)).
add_library(someip-core ALIAS opensomeip)
add_library(someip-transport ALIAS opensomeip)
add_library(someip-rpc ALIAS opensomeip)
add_library(someip-sd ALIAS opensomeip)
add_library(someip-events ALIAS opensomeip)
add_library(someip-tp ALIAS opensomeip)
add_library(someip-serialization ALIAS opensomeip)
add_library(someip-e2e ALIAS opensomeip)
add_library(someip-common ALIAS opensomeip)

# Windows portability: suppress problematic macros from <windows.h>.
#   NOGDI            – prevents <wingdi.h> (defines ERROR as 0)
#   WIN32_LEAN_AND_MEAN – trims unused Win32 sub-headers
#   NOMINMAX         – prevents min/max macros that break std::min/std::max
if(WIN32)
    target_compile_definitions(opensomeip PRIVATE NOGDI WIN32_LEAN_AND_MEAN NOMINMAX)
endif()

# Set library properties for proper soname versioning
set_target_properties(opensomeip PROPERTIES
    VERSION ${PROJECT_VERSION}
    SOVERSION ${PROJECT_VERSION_MAJOR}
    POSITION_INDEPENDENT_CODE ON
)

# Symbol visibility for shared builds: hide by default, export explicitly
if(BUILD_SHARED_LIBS)
    set_target_properties(opensomeip PROPERTIES
        CXX_VISIBILITY_PRESET hidden
        VISIBILITY_INLINES_HIDDEN ON
    )
endif()

# Host unit tests delay outbound connect so get_connection_state() can
# observe CONNECTING. Production (BUILD_TESTS=OFF, including the shared
# package) and RTOS backends do not compile the setter or the sleep.
if(BUILD_TESTS AND NOT SOMEIP_USE_FREERTOS AND NOT SOMEIP_USE_THREADX)
    set_property(SOURCE ${CMAKE_CURRENT_SOURCE_DIR}/transport/tcp_transport.cpp
        APPEND PROPERTY COMPILE_DEFINITIONS SOMEIP_TCP_TEST_CONNECT_HOLD)
endif()
