//
//  tb_transport.h
//  Tightbeam

#ifndef __TIGHTBEAM_TRANSPORT_H
#define __TIGHTBEAM_TRANSPORT_H

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

#include <Tightbeam/tightbeam.h>

TB_ASSUME_NONNULL_BEGIN
__TB_BEGIN_DECLS

#define TB_TRANSPORT_CONTEXT_SIZE 144
#define TB_TRANSPORT_SIZE 136

#ifndef __RTKIT__
/// 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"
struct tb_transport_s {
    union {
        _Alignas(8) char _opaque[TB_TRANSPORT_SIZE + TB_TRANSPORT_CONTEXT_SIZE];
        void *_stand_in; /* convey that this has pointers */
    };
};
#pragma clang diagnostic pop
#else
struct tb_transport_s;
#endif
typedef struct tb_transport_s
        * __single tb_transport_t __TB_WARN_UNUSED_RESULT;

struct tb_transport_message_buffer_s;
typedef struct tb_transport_message_buffer_s
        * __single tb_transport_message_buffer_t __TB_WARN_UNUSED_RESULT;

#pragma mark - Connection -> Transport

/// Construct a transport-specific message buffer.
///
/// @param bytes
/// The number of bytes to reserve for the message buffer.
///
/// @param capabilities
/// Number of capability slots to reserve.
///
/// @param msg_buffer
/// An out-parameter that will be assigned with a pointer to the structure
/// representing the reserved message buffer.
///
/// @return An error if the transport buffer construction failed, or success.
///
/// @discussion
/// A transport is free to reserve extra memory in the buffer for any
/// transport-specific data it may need.
typedef tb_error_t (*tb_transport_construct_message_buffer_f)(size_t bytes,
		size_t capabilities, tb_transport_message_buffer_t msg_buffer);

/// Construct a transport-specific message buffer.
///
/// @param transport
/// A pointer to the transport instance that should create the buffer.
///
/// @param bytes
/// The number of bytes to reserve for the message buffer.
///
/// @param capabilities
/// Number of capability slots to reserve.
///
/// @param msg_buffer
/// An out-parameter that will be assigned with a pointer to the structure
/// representing the reserved message buffer.
///
/// @return An error if the transport buffer construction failed, or success.
///
/// @discussion
/// A transport is free to reserve extra memory in the buffer for any
/// transport-specific data it may need.
typedef tb_error_t (*tb_transport_construct_message_buffer_2_f) (
        tb_transport_t transport, size_t bytes, size_t capabilities,
        tb_transport_message_buffer_t msg_buffer);

/// Destruct a transport message buffer and release any allocated resources
/// associated with it.
///
/// @param msg_buffer
/// Pointer to the message buffer to destruct.
typedef void (*tb_transport_destruct_message_buffer_f)(
		tb_transport_message_buffer_t msg_buffer);

/// Destruct a transport message buffer and release any allocated resources
/// associated with it.
///
/// @param transport
/// A pointer to the transport instance that should free the buffer.
///
/// @param msg_buffer
/// Pointer to the message buffer to destruct.
typedef void (*tb_transport_destruct_message_buffer_2_f)(
        tb_transport_t transport, tb_transport_message_buffer_t msg_buffer);

/// Reset a message buffer for receipt of a new payload. This will free the
/// existing payload, replace it with a new one and reset all pointers.
///
/// @param msg_buffer
/// The message buffer to reset.
///
/// @param capabilities
/// Number of capability slots to reserve.
///
/// @param bytes
/// The number of bytes to reserve for the message.
///
/// @return An error if the transport buffer reset failed, or success.
typedef tb_error_t (*tb_transport_reset_message_buffer_f)(
		tb_transport_message_buffer_t msg_buffer, size_t capabilities,
		size_t bytes);

/// Reset a message buffer for receipt of a new payload. This will free the
/// existing payload, replace it with a new one and reset all pointers.
///
/// @param transport
/// A pointer to the transport instance that should reset the buffer.
///
/// @param msg_buffer
/// The message buffer to reset.
///
/// @param capabilities
/// Number of capability slots to reserve.
///
/// @param bytes
/// The number of bytes to reserve for the message.
///
/// @return An error if the transport buffer reset failed, or success.
typedef tb_error_t (*tb_transport_reset_message_buffer_2_f)(
        tb_transport_t transport, tb_transport_message_buffer_t msg_buffer,
        size_t capabilities, size_t bytes);

/// Send a message and don't wait for a response.
///
/// @param transport
/// The transport to send the connection over.
///
/// @param query
/// The message to send. This must be a completed message, contents will be
/// uninitialized after this method returns. This message must have a state of
/// `TB_MESSAGE_STATE_SENT`.
///
/// @param response
/// An out-parameter pointer to a response message. This will point to any
/// reply received in the event that the `TB_CONNECTION_WAIT_FOR_REPLY` flag
/// was passed.
///
/// @param flags
/// Flags to further control the message send operation.
///
/// @return An error if the send message operation failed, or success.
typedef tb_error_t (*tb_transport_send_message_f)(
        tb_transport_t transport, tb_message_t query,
		tb_message_t _Nonnull * _Nullable response,
		tb_connection_flags_t flags);

/// Activate the provided transport. This is called by the connection after all
/// configuration is complete.
///
/// @param transport
/// The transport to activate.
typedef tb_error_t (*tb_transport_activate_f)(tb_transport_t transport);

/// Query a transport for whether it supports sending and receiving multipart
/// messages.
///
/// @param transport
/// The transport instance to query.
///
/// @return A bool indicating whether the transport instance supports multipart
/// messages or not.
typedef bool (*tb_transport_supports_multipart_messages_f)(tb_transport_t
        transport);

/// Get the provided transport's transmit buffer size.
///
/// @param transport
/// The transport to get the transmit buffer size for.
///
/// @return The send buffer size.
typedef size_t (*tb_transport_get_tx_buffer_size_f)(tb_transport_t transport);

/// Destruct the provided transport. This is called by the connection before it goes
/// away to allow the transport to clean up any memory it allocated.
///
/// @param transport
/// The transport to destruct.
typedef void (*tb_transport_destruct_f)(tb_transport_t transport);

#pragma mark - Transport -> Connection

/// The block the transport will attempt to call when a message is received.
///
/// @param query
/// The message received.
///
/// @return
/// A response to the query or NULL.
typedef _Nullable tb_message_t (^tb_transport_message_handler_b)(
		tb_message_t query);

/// The function pointer the transport will attempt to call when a message is
/// received.
///
/// @param query
/// The message received.
///
/// @param context
/// The associated context pointer provided when the function pointer was set
///
/// @return
/// A response to the query or NULL.
typedef _Nullable tb_message_t (*tb_transport_message_handler_f)(
        tb_message_t query, void *context);

/// The block the transport will call after a reply has been sent. This is
/// called so that the connection can drive the reply message further along in
/// the state machine.
///
/// @param reply
/// The reply message that was sent.
typedef void (^tb_connection_reply_sent_b)(tb_message_t reply);

/// The block the transport will call after a reply has been sent. This is
/// called so that the connection can drive the reply message further along in
/// the state machine.
///
/// @param reply
/// The reply message that was sent.
///
/// @param context
/// The associated context pointer provided when the function pointer was set.
typedef void (*tb_transport_reply_sent_handler_f)(tb_message_t reply,
		void *context);

#pragma mark - Accessors

TB_EXPORT
void
tb_transport_set_message_handler(tb_transport_t transport,
        tb_transport_message_handler_b handler);

TB_EXPORT
void
tb_transport_set_message_handler_f(tb_transport_t tpt,
        tb_transport_message_handler_f handler, void *context);

TB_EXPORT
void
tb_transport_set_reply_sent_handler(tb_transport_t transport,
        tb_connection_reply_sent_b handler);

TB_EXPORT
tb_error_t
tb_transport_call_message_handler(tb_transport_t tpt, tb_message_t query,
        tb_message_t _Nullable * _Nonnull reply);

TB_EXPORT
void
tb_transport_message_buffer_wrap_buffer(
        tb_transport_message_buffer_t message_buffer,
        uint8_t * __sized_by(payload_buffer_size) payload_buffer,
        size_t payload_buffer_size);

TB_EXPORT
tb_error_t
tb_transport_send_message(
        tb_transport_t transport, tb_message_t query,
        tb_message_t _Nonnull * _Nullable response,
        tb_connection_flags_t flags);

TB_EXPORT
bool
tb_transport_client_activate(tb_transport_t transport);

TB_EXPORT
bool
tb_transport_service_activate(tb_transport_t transport);

/// Ask the provided transport to construct a message buffer.
///
/// @param transport
/// A pointer to the transport instance that should create the buffer.
///
/// @param bytes
/// The number of bytes to reserve for the message buffer.
///
/// @param capabilities
/// Number of capability slots to reserve.
///
/// @param msg_buffer
/// An out-parameter that will be assigned with a pointer to the structure
/// representing the reserved message buffer.
///
/// @return An error if the transport buffer construction failed, or success.
///
/// @discussion
/// A transport is free to reserve extra memory in the buffer for any
/// transport-specific data it may need.
TB_EXPORT
tb_error_t
tb_transport_construct_message_buffer(tb_transport_t transport, size_t bytes,
                                      size_t capabilities,
                                      tb_transport_message_buffer_t msg_buffer);

/// Destruct a transport message buffer and release any allocated resources
/// associated with it.
///
/// @param transport
/// A pointer to the transport instance that should free the buffer.
///
/// @param msg_buffer
/// Pointer to the message buffer to destruct.
TB_EXPORT
void
tb_transport_destruct_message_buffer(tb_transport_t transport, 
                                     tb_transport_message_buffer_t msg_buffer);

/// Reset a message buffer for receipt of a new payload. This will free the
/// existing payload, replace it with a new one and reset all pointers.
///
/// @param transport
/// A pointer to the transport instance that should reset the buffer.
///
/// @param msg_buffer
/// The message buffer to reset.
///
/// @param capabilities
/// Number of capability slots to reserve.
///
/// @param bytes
/// The number of bytes to reserve for the message.
///
/// @return An error if the transport buffer reset failed, or success.
TB_EXPORT
tb_error_t
tb_transport_reset_message(tb_transport_t transport,
                           tb_transport_message_buffer_t msg_buffer,
                           size_t capabilities,
                           size_t bytes);

TB_EXPORT
bool
tb_transport_supports_multipart_messages(tb_transport_t tpt);

TB_EXPORT
size_t
tb_transport_get_tx_buffer_size(tb_transport_t transport);

TB_EXPORT
void
tb_transport_startup(void);

__TB_END_DECLS
TB_ASSUME_NONNULL_END

#endif // __TIGHTBEAM_TRANSPORT_H
