//
//  tb_endpoint.h
//  Tightbeam

#ifndef __TIGHTBEAM_ENDPOINT_H
#define __TIGHTBEAM_ENDPOINT_H

#include <sys/cdefs.h>
__ptrcheck_abi_assume_single()

#include <Tightbeam/tightbeam.h>

TB_ASSUME_NONNULL_BEGIN
__TB_BEGIN_DECLS

TB_ENUM(tb_transport_type, uint32_t,
    TB_TRANSPORT_TYPE_NONE TB_SWIFT_NAME(none) = 0,
    TB_TRANSPORT_TYPE_NULL TB_SWIFT_NAME(null),
    TB_TRANSPORT_TYPE_MACH TB_SWIFT_NAME(mach),
    TB_TRANSPORT_TYPE_CL4 TB_SWIFT_NAME(cl4),
    TB_TRANSPORT_TYPE_EVE_SINGLETON TB_SWIFT_NAME(eveSingleton),
    TB_TRANSPORT_TYPE_EVE TB_SWIFT_NAME(eve),
    TB_TRANSPORT_TYPE_RTKIT TB_SWIFT_NAME(rtkit),
    TB_TRANSPORT_TYPE_XNU TB_SWIFT_NAME(xnu),
    TB_TRANSPORT_TYPE_DARWIN TB_SWIFT_NAME(darwin),
    TB_TRANSPORT_TYPE_UNIX_SOCKET TB_SWIFT_NAME(unixSocket),
    TB_TRANSPORT_TYPE_DELEGATED TB_SWIFT_NAME(delegated),
    TB_TRANSPORT_TYPE_AFK TB_SWIFT_NAME(afk),
    TB_TRANSPORT_TYPE_DELEGATED_C TB_SWIFT_NAME(delegatedC),
    TB_TRANSPORT_TYPE_CL4_LARGE TB_SWIFT_NAME(cL4Large),
    TB_TRANSPORT_TYPE_SEPARATION_MONITOR TB_SWIFT_NAME(separationMonitor),
) TB_SWIFT_NAME(TransportType);

#define TB_ENDPOINT_OPTIONS_NONE 0

/// See comment for `struct tb_connection_s`
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wunknown-warning-option"
#pragma clang diagnostic ignored "-Wpre-c11-compat"
#define TB_ENDPOINT_SIZE 96
struct tb_endpoint_s {
    union {
        _Alignas(8) char _opaque[TB_ENDPOINT_SIZE];
        void *_stand_in; /* convey that this has a pointer field */
    };
};
#pragma clang diagnostic pop

/// Endpoints encapsulate the necessary data and options to bootstrap the initialization of a Transport.
/// They contain 3 main properties:
/// 1. Type - The type of the transport they can bootstrap. The available options are dictated by the
///   `tb_transport_type_t`enum.
/// 2. Data - A pointer to arbitrary data that is opaque to the Endpoint and the Tightbeam layers above the
///   Transport. Clients pass a destructor block on Endpoint creation. This block is called once when the
///   Endpoint is destructed by the Connection after it's been used to initialize a Transport.
/// 3. Options - An unsigned 32 bit value who's value is opaque to anything other than the Transport. This
///   can be used to help the Transport interpret the kind of data behind the `data` pointer.
///
/// Endpoints become owned by the Connections they are passed to. The Connection will use the Endpoint to
/// initialize the requested Transport and will then destruct them.
#if __swift__
typedef void * tb_endpoint_t __TB_WARN_UNUSED_RESULT;
#else
typedef struct tb_endpoint_s * __single tb_endpoint_t __TB_WARN_UNUSED_RESULT;
#endif // __swift__

typedef void * __unsafe_indexable tb_endpoint_data_t __TB_WARN_UNUSED_RESULT;

typedef uint32_t tb_endpoint_options_t __TB_WARN_UNUSED_RESULT;

typedef void (^tb_endpoint_data_destructor_b)(tb_endpoint_data_t ep_data);

typedef void (*tb_endpoint_data_destructor_f)(void *_Nullable context,
        tb_endpoint_data_t ep_data);

/// Interface-specific value passed to the transport for internal use
///
/// See documentation below for:
/// - `tb_endpoint_get_interface_identifier`
/// - `tb_endpoint_set_interface_identifier`
typedef uint64_t tb_interface_identifier_t;

/// Create a new endpoint.
///
/// @param type
/// The type of transport this endpoint should represent.
///
/// @param options
/// Transport-specific options that will be passed to the transport on initialization.
///
/// @return A new endpoint.
TB_EXPORT
tb_endpoint_t
tb_endpoint_create(tb_transport_type_t type, tb_endpoint_options_t options);

/// Create a new endpoint with a data pointer.
///
/// @param type
/// The type of transport this endpoint should represent.
///
/// @param data
/// Transport-specific data needed to bootstrap the transport.
///
/// @param options
/// Transport-specific options that will be passed to the transport on initialization.
///
/// @param data_destructor
/// A block that will be called once during Endpoint destruction. Clients should use this to free any resources
/// retained by `data`.
///
/// @return A new endpoint.
TB_EXPORT
tb_endpoint_t
tb_endpoint_create_with_data(tb_transport_type_t type,
        tb_endpoint_data_t _Nullable data,
        tb_endpoint_options_t options,
        tb_endpoint_data_destructor_b _Nullable data_destructor);

/// Create a new endpoint with a data pointer.
///
/// @param type
/// The type of transport this endpoint should represent.
///
/// @param data
/// Transport-specific data needed to bootstrap the transport.
///
/// @param options
/// Transport-specific options that will be passed to the transport on initialization.
///
/// @param data_destructor
/// A function that will be called once during Endpoint destruction. Clients should use this to free any resources
/// retained by `data`.
///
/// @param data_destructor_context
/// The context associated with the destructor. This context is owned by
/// the caller and thus the caller is responsible for deallocation of associated resources.
///
/// @return A new endpoint.
TB_EXPORT
tb_endpoint_t
tb_endpoint_create_with_data_f(tb_transport_type_t type, tb_endpoint_data_t data,
        tb_endpoint_options_t options,
        tb_endpoint_data_destructor_f _Nullable data_destructor,
        void * _Nullable data_destructor_context);

/// Create a new endpoint with a data value.
///
/// @param type
/// The type of transport this endpoint should represent.
///
/// @param value
/// Transport-specific value needed to bootstrap the transport.
///
/// @param options
/// Transport-specific options that will be passed to the transport on initialization.
///
/// @return A new endpoint.
TB_EXPORT
tb_endpoint_t
tb_endpoint_create_with_value(tb_transport_type_t type, uint64_t value,
        tb_endpoint_options_t options);

/// Create a new endpoint with a data value and places the intialised result into an existing,
/// unintiailzed `tb_endpoint_t` structure.
///
/// @param ep
/// A pointer to an existing, unintiailzed endpoint.
///
/// @param type
/// The type of transport this endpoint should represent.
///
/// @param value
/// Transport-specific value needed to bootstrap the transport.
///
/// @param options
/// Transport-specific options that will be passed to the transport on initialization.
///
/// @return A new endpoint.
TB_EXPORT
tb_error_t
tb_endpoint_create_with_value_static(tb_endpoint_t ep, tb_transport_type_t type,
        uint64_t value, tb_endpoint_options_t options);

/// Destruct a tb_endpoint_t
///
/// @param endpoint
/// A tb_endpoint_t created with tb_endpoint_create
TB_EXPORT
void
tb_endpoint_destruct(tb_endpoint_t endpoint);

/// Get a pointer to the data inside the endpoint.
///
/// @param endpoint
/// The endpoint to get the data pointer for.
///
/// @return
/// A pointer to the data inside the endpoint.
TB_EXPORT
tb_endpoint_data_t
tb_endpoint_get_data(tb_endpoint_t endpoint);

/// Get the data value stored inside the endpoint.
///
/// @param endpoint
/// The endpoint to get the data value for.
///
/// @return
/// The data value stored in the endpoint.
TB_EXPORT
uint64_t
tb_endpoint_get_value(tb_endpoint_t endpoint);

/// Get the transport type for the provided endpoint.
///
/// @param endpoint
/// The endpoint to get the transport type for.
///
/// @return
/// The transport type for the provided endpoint.
TB_EXPORT
tb_transport_type_t
tb_endpoint_get_type(tb_endpoint_t endpoint);

/// Get the options contained in the provided endpoint.
///
/// @param endpoint
/// The endpoint to get the options for.
///
/// @return
/// The options contained in the provided endpoint.
TB_EXPORT
tb_endpoint_options_t
tb_endpoint_get_options(tb_endpoint_t endpoint);

/// Get the interface identifier set for this endpoint.
///
/// @return
/// Return an identifier associated with an interface.
///
/// @discussion
/// This is for transports' internal use, for, e.g., policy enforcement.
/// The value here is derived from the module and service.
TB_EXPORT
tb_interface_identifier_t
tb_endpoint_get_interface_identifier(tb_endpoint_t endpoint);

/// Set the interface identifier
///
/// @discussion
/// This is set by generated code. The value is later read by the transport
/// for internal use. The value is derived from the module and service.
TB_EXPORT
void
tb_endpoint_set_interface_identifier(tb_endpoint_t endpoint,
       tb_interface_identifier_t interface_identifier);

__TB_END_DECLS
TB_ASSUME_NONNULL_END

#endif // __TIGHTBEAM_ENDPOINT_H
