//
//  tb_connection.h
//  Tightbeam

#ifndef __TIGHTBEAM_CONNECTION_H
#define __TIGHTBEAM_CONNECTION_H

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

#include <stdbool.h>
#include <Tightbeam/tightbeam.h>
#include <Tightbeam/tb_transport.h>

TB_ASSUME_NONNULL_BEGIN
__TB_BEGIN_DECLS

#define TB_CONNECTION_SIZE 64

/// Opaque encapsulation of capabilities/ports needed to describe a Tightbeam
/// connection to a specific endpoint paired with a specific address space.
///
/// Along with `struct tb_endpoint_s` and `struct tb_transport_s`, this lets
/// these objects be held in non-malloc owned memory. The representation
/// contains a pointer to signal that the real defintion has pointers, should
/// that information be useful, e.g., if this structure is embedded.
///
/// The warning that `_Alignas` may be incompatible is suppressed because
/// these headers are exported to Apple-internal adopters; we assume that
/// all adopters' build environments support this C11 keyword.
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wunknown-warning-option"
#pragma clang diagnostic ignored "-Wpre-c11-compat"
struct tb_connection_s {
    union {
        _Alignas(8) char _opaque[TB_CONNECTION_SIZE];
        void *_stand_in; /* convey that real defintion has pointer field */
    };
};
#pragma clang diagnostic pop

#if __swift__
typedef void * tb_client_connection_t __TB_WARN_UNUSED_RESULT;
typedef void * tb_service_connection_t __TB_WARN_UNUSED_RESULT;
#else // __swift __
typedef struct tb_connection_s * __single tb_client_connection_t __TB_WARN_UNUSED_RESULT;
typedef struct tb_connection_s * __single tb_service_connection_t __TB_WARN_UNUSED_RESULT;
#endif // __swift

typedef _Nullable tb_message_t (^tb_connection_message_handler_b)(
		tb_service_connection_t connection, tb_message_t query);

typedef _Nullable tb_message_t (*tb_connection_message_handler_f)(
        tb_service_connection_t connection, tb_message_t query, void *context);

/// Construct a new client connection object using an endpoint that can be used to send Tightbeam
/// messages.
///
/// @param endpoint
/// The endpoint to construct the message with.
///
/// @discussion
/// The endpoint must wrap the resources needed to bootstrap the specified transport type. This will assert on
/// failure.
TB_EXPORT
tb_client_connection_t
tb_client_connection_create_with_endpoint(tb_endpoint_t endpoint);

/// Construct a new client connection object using an endpoint that can be used to send Tightbeam
/// messages.
///
/// @param connection
/// An existing, uninitialized `tb_client_connection_t`
///
/// @param transport
/// An existing, uninitialized `tb_transport_t`
///
/// @param endpoint
/// The endpoint to construct the connection with.
///
/// @discussion
/// The endpoint must wrap the resources needed to bootstrap the specified transport type. This will assert on
/// failure.
///
/// This call is only currently supported for `TB_TRANSPORT_TYPE_CL4` endpoint types.
TB_EXPORT
tb_error_t
tb_client_connection_create_with_endpoint_static(
        tb_client_connection_t connection, tb_transport_t transport,
        tb_endpoint_t endpoint);

/// Construct a new service connection object using an endpoint that can be used to send Tightbeam
/// messages.
///
/// @param endpoint
/// The endpoint to construct the message with.
///
/// @discussion
/// The endpoint must wrap the resources needed to bootstrap the specified transport type. This will assert on
/// failure.
TB_EXPORT
tb_service_connection_t
tb_service_connection_create_with_endpoint(tb_endpoint_t endpoint,
		tb_connection_message_handler_b handler);

/// Construct a new service connection object using an endpoint that can be used to send Tightbeam
/// messages.
///
/// @param endpoint
/// The endpoint to construct the message with.
///
/// @param handler
/// The handler for incoming messages on this Tightbeam connection.
///
/// @param context
/// The context associated with the message handler. The context is owned by the caller,
/// and the caller is responsible for destruction of this context upon termination of the connection.
///
/// @discussion
/// The endpoint must wrap the resources needed to bootstrap the specified transport type. This will assert on
/// failure.
TB_EXPORT
tb_service_connection_t
tb_service_connection_create_with_endpoint_f(tb_endpoint_t endpoint,
        tb_connection_message_handler_f handler, void * _Nullable context);

/// Construct a new connection object that can be used to send Tightbeam messages.
///
/// @param transport
/// The Tightbeam transport to use with this connection. Connection takes ownership of the provided
/// transport.
///
/// @return
/// A Tightbeam Connection instance that can be used to send messages.
TB_EXPORT
tb_client_connection_t
tb_client_connection_create(tb_transport_t transport);

/// Destruct a client connection
///
/// @param connection
/// The client Tightbeam connection created with tb_client_connection_create
TB_EXPORT
void
tb_client_connection_destruct(tb_client_connection_t connection);

/// Construct a new connection object that can be used to receive Tightbeam messages.
///
/// @param transport
/// The Tightbeam transport to use with this connection. Connection takes ownership of the provided
/// transport.
///
/// @param handler
/// A handler block for incoming messages on this Tightbeam connection.
///
/// @return
/// A Tightbeam Connection instance that can be used to receive messages.
TB_EXPORT
tb_service_connection_t
tb_service_connection_create(tb_transport_t transport,
		tb_connection_message_handler_b handler);

/// Construct a new connection object that can be used to receive Tightbeam messages.
///
/// @param transport
/// The Tightbeam transport to use with this connection. Connection takes ownership of the provided
/// transport.
///
/// @param handler
/// The handler for incoming messages on this Tightbeam connection.
///
/// @param context
/// The context associated with the message handler. The context is owned by the caller,
/// and the caller is responsible for destruction of this context upon termination of the connection.
///
/// @return
/// A Tightbeam Connection instance that can be used to receive messages.
TB_EXPORT
tb_service_connection_t
tb_service_connection_create_f(tb_transport_t transport,
        tb_connection_message_handler_f handler, void * _Nullable context);

/// Destruct a service connection
///
/// @param connection
/// The service Tightbeam connection created with tb_service_connection_create
TB_EXPORT
void
tb_service_connection_destruct(tb_service_connection_t connection);

/// Activate a client connection. This should be called when all connection configuration is complete.
///
/// @param connection
/// The client connection to activate.
///
/// @return
/// On success returns `TB_ERROR_SUCCESS`, or a code indicating an error occurred.
TB_EXPORT
tb_error_t
tb_client_connection_activate(tb_client_connection_t connection);

/// Activate a service connection. This should be called when all connection configuration is complete.
///
/// @param connection
/// The service connection to activate.
///
/// @return
/// On success returns `TB_ERROR_SUCCESS`, or a code indicating an error occurred.
TB_EXPORT
tb_error_t
tb_service_connection_activate(tb_service_connection_t connection);

/// Send a completed query message to the specified connection. The resources associated with the
/// message (including the space reserved for it in the preallocated Tightbeam message buffer) will be
/// consumed or released by this method in all cases (including send errors).
///
/// @param connection
/// The Tightbeam connection to send the query to.
///
/// @param query
/// Pointer to query message metadata to operate on, must be a completed message, contents will be
/// uninitialized after this method returns.
///
/// @param reply
/// Pointer to client-provided storage for reply message metadata, contents will be initialized by this method.
/// This will be filled in by the connection if the `TB_CONNECTION_WAIT_FOR_REPLY` flag is provided.
///
/// @param flags
/// Flags for the send operation.
///
/// @return
/// An error if the send operation failed or success.
TB_EXPORT
tb_error_t
tb_connection_send_query(tb_client_connection_t connection,
		tb_message_t query, tb_message_t _Nullable * _Nullable reply,
		tb_connection_flags_t flags);

/// Construct a new message and reserve the specified amount of space for message payload and for
/// capabilities in the preallocated Tightbeam message buffer.
///
/// Marks the message as 'being prepared', you must call tb_message_complete() before sending
/// (or consuming).
///
/// @param connection
/// The client connection to construct the message for.
///
/// @param msg
/// (in/out) pointer to client-provided storage for message metadata, contents will be initialized by this method.
///
/// @param bytes
/// Number of message payload bytes to reserve.
///
/// @param capabilities
/// Number of capability slots to reserve.
///
/// @return
/// An error if the message construction failed, or success.
TB_EXPORT
tb_error_t
tb_client_connection_message_construct(tb_client_connection_t connection,
		tb_message_t msg, tb_transport_message_buffer_t transport_buffer,
		size_t bytes, size_t capabilities);

/// Destruct a message. This clears message state and releases any Tightbeam-owned resources attached.
///
/// @param connection
/// The client connection for which the message was created.
///
/// @param msg
/// The message to destruct.
///
/// @return
/// An error if the message destruction failed, or success.
TB_EXPORT
tb_error_t
tb_client_connection_message_destruct(tb_client_connection_t connection,
		tb_message_t msg);

/// Reset a client message so it can be sent again.
///
/// @param connection
/// The client connection that will be used to send the message.
///
/// @param msg
/// The message to reset.
///
/// @param bytes
/// The number of bytes to reserve for the message.
///
/// @return
/// An error if the message reset failed, or success.
TB_EXPORT
tb_error_t
tb_client_connection_message_reset(tb_client_connection_t connection,
		tb_message_t msg, size_t bytes, size_t capabilities);

/// Construct a new message and reserve the specified amount of space for message payload and for
/// capabilities in the preallocated Tightbeam message buffer.
///
/// Marks the message as 'being prepared', you must call tb_message_complete() before sending (or
/// consuming).
///
/// @param connection
/// The service connection to construct the message for.
///
/// @param msg
/// (in/out) pointer to client-provided storage for message metadata, contents will be initialized by this method.
///
/// @param bytes
/// Number of message payload bytes to reserve.
///
/// @param capabilities
/// Number of capability slots to reserve.
///
/// @return
/// An error if the message construction failed, or success.
TB_EXPORT
tb_error_t
tb_service_connection_message_construct(tb_service_connection_t connection,
		tb_message_t msg, tb_transport_message_buffer_t transport_buffer,
		size_t bytes, size_t capabilities);

/// Destruct a message. This cleares message state and releases any Tightbeam-owned resources attached.
///
/// @param connection
/// The service connection for which the message was created.
///
/// @param msg
/// The message to destruct.
///
/// @return
/// An error if the message destruction failed, or success.
TB_EXPORT
tb_error_t
tb_service_connection_message_destruct(tb_service_connection_t connection,
		tb_message_t msg);

/// Configure an existing message to be used as a reply. This will set the message state as
/// `TB_MESSAGE_STATE_PREPARING` and the disposition as `TB_MESSAGE_DISPOSITION_REPLY`.
/// The underlying Transport buffer will be re-configured for encoding of a new message payload.
///
/// @param connection
/// The service connection that will be used to send the reply message.
///
/// @param msg
/// The message to configure as a reply.
///
/// @param bytes
/// The number of bytes to reserve for the payload.
///
/// @param capabilities
/// The number of capabilities that will be encoded.
///
/// @return
/// An error if the configuration failed, or success.
TB_EXPORT
tb_error_t
tb_service_connection_message_configure_reply(
		tb_service_connection_t connection, tb_message_t msg, size_t bytes,
		size_t capabilities);

/// Returns the context associated with the given Tightbeam connection.
///
/// @param con
/// The connection for which the context information is required.
///
/// @return
/// The context associated with the given Tightbeam connection.
TB_EXPORT
void *_Nullable
tb_connection_get_context(struct tb_connection_s *_Nonnull con);

/// Sets the context associated with the given Tightbeam connection.
/// The new context pointer overwrites any previously associated context pointer.
/// The context is owned by the caller and thus Tightbeam will not deallocate
/// any memory associated with this context when the connection is terminated.
///
/// @param con
/// The connection to set context.
///
/// @param ctx
/// The context pointer to associate with this connection.
TB_EXPORT
void
tb_connection_set_context(struct tb_connection_s *_Nonnull con, void *_Nullable ctx);

__TB_END_DECLS
TB_ASSUME_NONNULL_END

#endif // __TIGHTBEAM_CONNECTION_H
