//
//  tb_message.h
//  Tightbeam

#ifndef __TIGHTBEAM_MESSAGE_H
#define __TIGHTBEAM_MESSAGE_H

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

#include <Tightbeam/tightbeam.h>

TB_ASSUME_NONNULL_BEGIN
__TB_BEGIN_DECLS

TB_ENUM(tb_message_state, uint32_t,
    TB_MESSAGE_STATE_UNINITIALIZED TB_SWIFT_NAME(uninitialized) = 0,
    TB_MESSAGE_STATE_PREPARING TB_SWIFT_NAME(preparing),
    TB_MESSAGE_STATE_READY TB_SWIFT_NAME(ready),
    TB_MESSAGE_STATE_SENT TB_SWIFT_NAME(sent),
    TB_MESSAGE_STATE_RECEIVED TB_SWIFT_NAME(received),
    TB_MESSAGE_STATE_DESTRUCTED TB_SWIFT_NAME(destructed),
) TB_SWIFT_NAME(MessageState);

TB_ENUM(tb_message_disposition, uint8_t,
    TB_MESSAGE_DISPOSITION_UNSET TB_SWIFT_NAME(unset) = 0,
    TB_MESSAGE_DISPOSITION_QUERY TB_SWIFT_NAME(query),
    TB_MESSAGE_DISPOSITION_REPLY TB_SWIFT_NAME(reply),
) TB_SWIFT_NAME(MessageDisposition);

/// Opaque metadata for a Tightbeam message associated with a specific thread
/// and the per-thread preallocated Tightbeam message buffer space.
/// - Tracks:
///     - message state: sent/received, query/reply, constructed/completed/
///                      destructed
///     - thread identity: struct is not transportable between threads
///     - pointer & size of reserved message buffer(s)
///     - position into message buffers
///     - transport error code
/// - metadata struct storage is assumed to be client managed (and typically
///   stack allocated)
struct tb_message_s;
#if __swift__
typedef void * tb_message_t __TB_WARN_UNUSED_RESULT;
#else
typedef struct tb_message_s * __single tb_message_t __TB_WARN_UNUSED_RESULT;
#endif

typedef uint64_t tb_message_identifier_t;

/// Mark an unsent query or reply message as being ready to send, or a received message as having been
/// fully consumed.
///
/// After query or reply message composition is complete, call this method to mark the message as being
/// ready to send. Will validate that all the reserved space has been used and the message contains
/// everything required for sending and return an error otherwise.
///
/// For a received message, call this method to mark the message as having all of its payload and capabilities
/// fully consumed, and release all the resources associated with it. Will validate that all the received
/// information has been retrieved and return an error otherwise.
///
/// @param msg
/// (in/out) pointer to message metadata to operate on. For received messages contents will be uninitialized
/// after this method returns.
TB_EXPORT
void
tb_message_complete(tb_message_t msg);

/// Get the state of a message
///
/// @param msg
/// The message to retrieve the state of.
TB_EXPORT
tb_message_state_t
tb_message_get_state(tb_message_t msg);

/// Get the disposition of a message.
///
/// @param msg
/// The message to retrieve the disposition of.
TB_EXPORT
tb_message_disposition_t
tb_message_get_disposition(tb_message_t msg);

/// Get the total size in bytes of the payload of a message
///
/// @param msg
/// The message to examine.
///
/// @note
/// This function indicates the total count of bytes in the payload of the given message.
TB_EXPORT
size_t
tb_message_get_size(tb_message_t msg);

TB_EXPORT
tb_error_t
tb_message_measure_subrange(tb_message_t msg, uint64_t *start, size_t *len,
        tb_error_t (^block)(void));

TB_EXPORT
tb_error_t
tb_message_subrange(tb_message_t msg, uint64_t start, size_t len,
        TB_NOESCAPE tb_error_t (^block)(tb_message_t));

TB_EXPORT
uint8_t * _Nullable __TB_ALLOC_SIZE(1)
tb_owned_buffer_allocate(size_t size);

TB_EXPORT
void
tb_owned_buffer_deallocate(uint8_t *ptr, size_t size);

TB_EXPORT
size_t
tb_message_size_capability(void);

TB_EXPORT
void
tb_message_precheck_encoding(tb_message_t msg, size_t sz);

TB_EXPORT
void
tb_message_precheck_decoding(tb_message_t msg, size_t sz);

/// Copy the remainder of the buffer available in `src` to `dst`.
///
/// @param src
/// The source message buffer
///
/// @param dst
/// The destination message buffer
///
/// @param sz
/// The size, in bytes, to copy from `src` to `dst`.
///
/// @note
/// This method will assert (crash) there are not `sz` bytes available in `src`, or if there is insufficient
/// room in `dst` to encode `sz` bytes.
TB_EXPORT
void
tb_message_copy_buffer(tb_message_t src, tb_message_t dst, size_t sz);

/// Assign the capability to the next available slot in the message.
///
/// @param msg
/// The message to store the capability in.
///
/// @param capability
/// The capability to assign.
///
/// @return
/// An error if there are no more free capability slots. Otherwise success.
TB_EXPORT
tb_error_t
tb_message_encode_capability(tb_message_t msg, uint64_t capability);

/// Decode the next capability from the message.
///
/// @param msg
/// The message to decode the capability from.
///
/// @param capability
/// A pointer to the capability that will be assigned to the capability value.
///
/// @return
/// If no capability exists this will return a decode error, otherwise success.
TB_EXPORT
tb_error_t
tb_message_decode_capability(tb_message_t msg, uint64_t *capability);

__TB_END_DECLS
TB_ASSUME_NONNULL_END

#endif // __TIGHTBEAM_MESSAGE_H

