#pragma once

/**
** \defgroup ssb SSB
** Everything about SSB, SHS, and MUXRPC happens here.
** @{
*/

#include "quickjs.h"

#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>

enum
{
	k_ssb_rpc_flag_binary = 0x0,
	k_ssb_rpc_flag_utf8 = 0x1,
	k_ssb_rpc_flag_json = 0x2,
	k_ssb_rpc_mask_type = 0x3,

	k_ssb_rpc_flag_end_error = 0x4,
	k_ssb_rpc_flag_stream = 0x8,
	k_ssb_rpc_mask_message = 0xC,

	k_ssb_rpc_mask_send = 0xf,

	k_ssb_rpc_flag_new_request = 0x10,

	k_ssb_blob_bytes_max = 5 * 1024 * 1024,

	k_ssb_peer_exchange_expires_seconds = 60 * 60,

	k_max_private_message_recipients = 8,
};

/**
** Flags affecting signature verification.
*/
typedef enum _tf_ssb_verify_flags_t
{
	k_tf_ssb_verify_flag_debug = 1,
} tf_ssb_verify_flags_t;

/**
** The type of change to a set of connections.
*/
typedef enum _tf_ssb_change_t
{
	k_tf_ssb_change_create,
	k_tf_ssb_change_connect,
	k_tf_ssb_change_remove,
	k_tf_ssb_change_update,
} tf_ssb_change_t;

/**
** The origin of a broadcast entry.
*/
typedef enum _tf_ssb_broadcast_origin_t
{
	k_tf_ssb_broadcast_origin_discovery,
	k_tf_ssb_broadcast_origin_room,
	k_tf_ssb_broadcast_origin_peer_exchange,
} tf_ssb_broadcast_origin_t;

/**
** Flags describing the structure of a message.
*/
typedef enum _tf_ssb_message_flags_t
{
	/**
	** The sequence field precedes the author field if specified.  The
	** other way around, otherwise.
	*/
	k_tf_ssb_message_flag_sequence_before_author = 1,
} tf_ssb_message_flags_t;

/**
** Flags affecting an SSB connection.
*/
typedef enum _tf_ssb_connect_flags_t
{
	k_tf_ssb_connect_flag_one_shot = 0x1,
	k_tf_ssb_connect_flag_do_not_store = 0x2,
	k_tf_ssb_connect_flag_use_invite = 0x4,
} tf_ssb_connect_flags_t;

/** An SSB instance. */
typedef struct _tf_ssb_t tf_ssb_t;
/** An SSB connection. */
typedef struct _tf_ssb_connection_t tf_ssb_connection_t;
/** A connection's EBT state. */
typedef struct _tf_ssb_ebt_t tf_ssb_ebt_t;
/** A trace instance. */
typedef struct _tf_trace_t tf_trace_t;
/** An SQLite database handle. */
typedef struct sqlite3 sqlite3;
/** An event loop. */
typedef struct uv_loop_s uv_loop_t;
/** A socket address. */
struct sockaddr_in;

enum
{
	k_id_base64_len = 57,
	k_id_bin_len = 32,
	k_blob_id_len = 53,
};

/**
** Statistics about an SSB instance.
*/
typedef struct _tf_ssb_stats_t
{
	/** Number of active connections. */
	int connections;
	/** Number of active hosts discovered by network broadcast. */
	int broadcasts;
	/** Number of messages stored. */
	int messages_stored;
	/** Number of blobs stored. */
	int blobs_stored;
	/** Number of RPC messages received. */
	int rpc_in;
	/** Number of RPC messages sent. */
	int rpc_out;
	/** Number of active RPC requests. */
	int request_count;
	/** Number of callbacks registered. */
	struct
	{
		int rpc;
		int connections_changed;
		int message_added;
		int blob_want_added;
		int broadcasts_changed;
	} callbacks;
} tf_ssb_stats_t;

/**
** State about requesting blobs.
*/
typedef struct _tf_ssb_blob_wants_t
{
	/** The request number of the blob.wants RPC call. */
	int32_t request_number;
	/** The number of blob wants we are waiting for a response to. */
	int wants_sent;
	/** The last blob ID considered. */
	char last_id[k_blob_id_len];
} tf_ssb_blob_wants_t;

/**
** A queue for storing messages.
*/
typedef struct _tf_ssb_store_queue_t
{
	/** The first node in the queue. */
	void* head;
	/** The last node in the queue. */
	void* tail;
	/** Whether the queue is currently running. */
	bool running;
} tf_ssb_store_queue_t;

/**
** Create an SSB instance.
** @param loop The event loop to use or NULL to create a new one.
** @param context The JS context to use or NULL to create a new one.
** @param db_path The path to the SQLite database to use.
** @param network_key The SSB network key to use or NULL to use the standard key.
*/
tf_ssb_t* tf_ssb_create(uv_loop_t* loop, JSContext* context, const char* db_path, const char* network_key);

/**
** Destroy an SSB instance.
** @param ssb The SSB instance to destroy.
*/
void tf_ssb_destroy(tf_ssb_t* ssb);

/**
** Checking if the SSB instance is in the process of shutting down.
** @param ssb The SSB instance.
** @return true If the SSB instance is shutting down.
*/
bool tf_ssb_is_shutting_down(tf_ssb_t* ssb);

/**
** Start optional periodic work.
** @param ssb The SSB instance.
*/
void tf_ssb_start_periodic(tf_ssb_t* ssb);

/**
** Control logging verbosity.
** @param ssb The SSB instance.
** @param verbose True to log messages for every RPC message sent and received.
*/
void tf_ssb_set_verbose(tf_ssb_t* ssb, bool verbose);

/**
** Reduce logging verbosity.
** @param ssb The SSB instance.
** @param quiet Disable unnecessary messages.
*/
void tf_ssb_set_quiet(tf_ssb_t* ssb, bool quiet);

/**
** Acquire an SQLite database for unrestricted reading.  Release qith tf_ssb_release_db_reader().
** @param ssb The SSB instance.
** @return A database with full read access to the database.
*/
sqlite3* tf_ssb_acquire_db_reader(tf_ssb_t* ssb);

/**
** Acquire an SQLite database for restricted reading.  Release qith
** tf_ssb_release_db_reader().
** @param ssb The SSB instance.
** @return A database with read access to a safe subset of the database.
*/
sqlite3* tf_ssb_acquire_db_reader_restricted(tf_ssb_t* ssb);

/**
** Release a database acquired with tf_ssb_acquire_db_reader() or
** tf_ssb_acquire_db_reader_restricted().
** @param ssb The SSB instance.
** @param db The database.
*/
void tf_ssb_release_db_reader(tf_ssb_t* ssb, sqlite3* db);

/**
** Acquire an SQLite database with full write access to the database.  Release
** with tf_ssb_release_db_writer().
** @param ssb The SSB instance.
** @return The writable database.
*/
sqlite3* tf_ssb_acquire_db_writer(tf_ssb_t* ssb);

/**
** Release a database acquired with tf_ssb_acquire_db_writer().
** @param ssb The SSB instance.
** @param db The database.
*/
void tf_ssb_release_db_writer(tf_ssb_t* ssb, sqlite3* db);

/**
** Get the SSB instance's event loop.
** @param ssb The SSB instance.
** @return The loop.
*/
uv_loop_t* tf_ssb_get_loop(tf_ssb_t* ssb);

/**
** Generate a public/private key pair for the SSB instance.
** @param ssb The SSB instance.
*/
void tf_ssb_generate_keys(tf_ssb_t* ssb);

/**
** Generate a public/private key pair and store in buffers.
** @param[out] out_public Buffer to receive the public key.
** @param public_size Size of the public key buffer.
** @param[out] out_private Buffer to receive the private key.
** @param private_size Size of the private key buffer.
*/
void tf_ssb_generate_keys_buffer(char* out_public, size_t public_size, char* out_private, size_t private_size);

/**
** Get the private key of the SSB instance.
** @param ssb The SSB instance.
** @param[out] out_private Buffer to receive the private key.
** @param private_size The size of the private key buffer.
*/
void tf_ssb_get_private_key(tf_ssb_t* ssb, uint8_t* out_private, size_t private_size);

/**
** Set the trace instance to use for the SSB instance.
** @param ssb The SSB instance.
** @param trace The trace instance to use.
*/
void tf_ssb_set_trace(tf_ssb_t* ssb, tf_trace_t* trace);

/**
** Get the SSB instance's trace instance.
** @param ssb The SSB instance.
** @return The trace instance.
*/
tf_trace_t* tf_ssb_get_trace(tf_ssb_t* ssb);

/**
** Get the SSB istance's JS context.
** @param ssb The SSB instance.
** @return The JS context.
*/
JSContext* tf_ssb_get_context(tf_ssb_t* ssb);

/**
** Begin listening for SSB discovery messages.
** @param ssb The SSB instance.
** @param linger True if listening for broadcasts should keep the event loop alive.
*/
void tf_ssb_broadcast_listener_start(tf_ssb_t* ssb, bool linger);

/**
** Begin sending SSB discovevry messages.
** @param ssb The SSB instance.
*/
void tf_ssb_broadcast_sender_start(tf_ssb_t* ssb);

/**
** Sign an SSB message.
** @param ssb The SSB instance.
** @param author The author's public key.
** @param private_key The author's private key.
** @param message The message to sign.
** @param previous_id The ID of the previous message in the feed.  Optional.
** @param previous_sequence The sequence number of the previous message in the feed.  Optional.
** @return The signed message.
*/
JSValue tf_ssb_sign_message(tf_ssb_t* ssb, const char* author, const uint8_t* private_key, JSValue message, const char* previous_id, int32_t previous_sequence);

/**
** Get the server's identity.
** @param ssb The SSB instance.
** @param[out] out_id A buffer populated with the identity.
** @param out_id_size The size of the identity buffer.
** @return True if the identity was successfully retrieved.
*/
bool tf_ssb_whoami(tf_ssb_t* ssb, char* out_id, size_t out_id_size);

/**
** Call a callback for each active host discovered by network discovery broadcast.
** @param ssb The SSB instance.
** @param callback The callback.
** @param user_data User data for the callback.
*/
void tf_ssb_visit_broadcasts(tf_ssb_t* ssb,
	void (*callback)(const char* host, const struct sockaddr_in* addr, tf_ssb_broadcast_origin_t origin, tf_ssb_connection_t* tunnel, const uint8_t* pub, void* user_data),
	void* user_data);

/**
** Add a broadcast entry.
** @param ssb The SSB instance.
** @param connection The connection string to add.
** @param origin The origin of the broadcast entry.
** @param expires_seconds How long the broadcast entry should last.
*/
void tf_ssb_add_broadcast(tf_ssb_t* ssb, const char* connection, tf_ssb_broadcast_origin_t origin, int64_t expires_seconds);

/**
** Get the identities of all active connections.
** @param ssb The SSB instance.
** @return A NULL-terminated array of SSB identities.  Free with tf_free().
*/
const char** tf_ssb_get_connection_ids(tf_ssb_t* ssb);

/**
** Retrieve a list of active connections.
** @param ssb The SSB instance.
** @param[out] out_connections An array to be populated with the connections.
** @param out_connections_count The size of the connections array.
** @return The number of connections populated in out_connections.
*/
int tf_ssb_get_connections(tf_ssb_t* ssb, tf_ssb_connection_t** out_connections, int out_connections_count);

/**
** Callback for completing establishing a connection.
** @param connection The established connection if successful or null.
** @param reason The reason for failure if the connection failed.
** @param user_data User data.
*/
typedef void(tf_ssb_connect_callback_t)(tf_ssb_connection_t* connection, const char* reason, void* user_data);

/**
** Establish an SHS connection with a host.
** @param ssb The SSB instance.
** @param host The host name or address.
** @param port The host's SHS port.
** @param key The host's SSB identity.
** @param connect_flags Flags affecting the connection.
** @param callback Completion callback.
** @param user_data User data to be passed to the callback.
*/
void tf_ssb_connect(tf_ssb_t* ssb, const char* host, int port, const uint8_t* key, int connect_flags, tf_ssb_connect_callback_t* callback, void* user_data);

/**
** Establish an SHS connection with a host by string address.
** @param ssb The SSB instance.
** @param address The address.
** @param connect_flags Flags affecting the connection.
** @param callback Completion callback.
** @param user_data User data to be passed to the callback.
*/
void tf_ssb_connect_str(tf_ssb_t* ssb, const char* address, int connect_flags, tf_ssb_connect_callback_t* callback, void* user_data);

/**
** Begin listening for SHS connections on the given port.
** @param ssb The SSB instance.
** @param port The port number.
** @return The assigned port on success or 0 on failure.
*/
int tf_ssb_server_open(tf_ssb_t* ssb, int port);

/**
** Determine the port that a server is listening on.
** @param ssb The SSB instance.
** @return The port number, or 0 if not bound.
*/
int tf_ssb_server_get_port(tf_ssb_t* ssb);

/**
** Stop listening for SHS connections.
** @param ssb The SSB instance.
*/
void tf_ssb_server_close(tf_ssb_t* ssb);

/**
** Close all active SHS connections.
** @param ssb The SSB instance.
** @param reason Reason for the close.
*/
void tf_ssb_close_all(tf_ssb_t* ssb, const char* reason);

/**
** Send a graceful close message to all active SHS connections.
** @param ssb The SSB instance.
*/
void tf_ssb_send_close(tf_ssb_t* ssb);

/**
** Convert an SSB identity from string to binary.
** @param[out] bin A buffer to receive the binary identity.
** @param str The string identity.
** @return True if the conversion was successful.
*/
bool tf_ssb_id_str_to_bin(uint8_t* bin, const char* str);

/**
** Convert an SSB identity from binary to string.
** @param[out] str A buffer to receive the identity string.
** @param str_size The size of the string buffer.
** @param bin The binary identity.
** @return True if the conversion was successful.
*/
bool tf_ssb_id_bin_to_str(char* str, size_t str_size, const uint8_t* bin);

/**
** Verify a message's signature and remove the signature if successful.
** @param context A JS context.
** @param val The message.
** @param verify_flags Verification options of type tf_ssb_verify_flags_t.
** @param[out] out_id A buffer to receive the message's identity.
** @param out_id_size The size of out_id.
** @param[out] out_signature A buffer to receive the message's signature.
** @param out_signature_size The size of out_signature.
** @param[out] out_flags tf_ssb_message_flags_t describing the message.
** @return True if the signature is valid and was successfully extracted.
*/
bool tf_ssb_verify_and_strip_signature(
	JSContext* context, JSValue val, int verify_flags, char* out_id, size_t out_id_size, char* out_signature, size_t out_signature_size, int* out_flags);

/**
** Determine the message identifier.
** @param context A JS context.
** @param message The message.
** @param[out] out_id A buffer to receive the identifier.
** @param out_id_size The size of out_id.
*/
void tf_ssb_calculate_message_id(JSContext* context, JSValue message, char* out_id, size_t out_id_size);

/**
** A function called on completion of tf_ssb_verify_strip_and_store_message().
** @param id The stored message identifier.
** @param verified True if the message was verified successfully.
** @param is_new True if the message was newly added to the database.
** @param user_data The user data.
*/
typedef void(tf_ssb_verify_strip_store_callback_t)(const char* id, bool verified, bool is_new, void* user_data);

/**
** Verify a message's signature, remove the signature, and store the message in the database.
** @param ssb The SSB instance.
** @param value The message.
** @param callback A callback called when the operation completed.
** @param user_data User data to pass to the callback.
*/
void tf_ssb_verify_strip_and_store_message(tf_ssb_t* ssb, JSValue value, tf_ssb_verify_strip_store_callback_t* callback, void* user_data);

/**
** Check if a connection is an outgoing connection.
** @param connection The connection.
** @return True if the connection is outgoing.
*/
bool tf_ssb_connection_is_client(tf_ssb_connection_t* connection);

/**
** Get the hostname of the remote end of a connection.
** @param connection The connection.
** @return The hostname or address.
*/
const char* tf_ssb_connection_get_host(tf_ssb_connection_t* connection);

/**
** Get a connection's remote port number.
** @param connection The connection.
** @return The port number.
*/
int tf_ssb_connection_get_port(tf_ssb_connection_t* connection);

/**
** If a connection is a tunnel, get its parent connection.
** @param connection The connection.
** @return The parent connection if the connection is tunneled or NULL.
*/
tf_ssb_connection_t* tf_ssb_connection_get_tunnel(tf_ssb_connection_t* connection);

/**
** Get a connection's SSB instance.
** @param connection The connection.
** @return The SSB instance.
*/
tf_ssb_t* tf_ssb_connection_get_ssb(tf_ssb_connection_t* connection);

/**
** Get a connection's JS context.
** @param connection The connection.
** @return The JS context.
*/
JSContext* tf_ssb_connection_get_context(tf_ssb_connection_t* connection);

/**
** Close a connection.
** @param connection The connection.
** @param reason Human-readable reason for closing the connection.
*/
void tf_ssb_connection_close(tf_ssb_connection_t* connection, const char* reason);

/**
** Check whether a connection is connected.
** @param connection The connection.
** @return True if the connection is alive.
*/
bool tf_ssb_connection_is_connected(tf_ssb_connection_t* connection);

/**
** Check whether a connection is in the process of closing.
** @param connection The connection.
** @return True if the connection is closing.
*/
bool tf_ssb_connection_is_closing(tf_ssb_connection_t* connection);

/**
** Get the next outgoing request number for a connection.
** @param connection The connection.
** @return The next request number.
*/
int32_t tf_ssb_connection_next_request_number(tf_ssb_connection_t* connection);

/**
** Get an active connection by its identity.
** @param ssb The SSB instance.
** @param id The SSB identity.
** @return The connection if found or NULL.
*/
tf_ssb_connection_t* tf_ssb_connection_get(tf_ssb_t* ssb, const char* id);

/**
** Get the SSB identity of a connection.
** @param connection The connection.
** @param[out] out_id A buffer to be populated with the identity.
** @param out_id_size The size of out_id.
** @return True if the identity was retrieved.
*/
bool tf_ssb_connection_get_id(tf_ssb_connection_t* connection, char* out_id, size_t out_id_size);

/**
** Get the JS object representing a connection.
** @param connection The connection.
** @return The object.
*/
JSValue tf_ssb_connection_get_object(tf_ssb_connection_t* connection);

/**
** A callback called when a callback is cleaned up.
** @param ssb The SSB instance.
** @param user_data User data.
*/
typedef void(tf_ssb_callback_cleanup_t)(tf_ssb_t* ssb, void* user_data);

/**
** A callback called when the connection list changes.
** @param ssb The SSB instance.
** @param change The type of change.
** @param connection The connection that changed.
** @param user_data User data.
*/
typedef void(tf_ssb_connections_changed_callback_t)(tf_ssb_t* ssb, tf_ssb_change_t change, tf_ssb_connection_t* connection, void* user_data);

/**
** Register a callback when the connection list changes.
** @param ssb The SSB instance.
** @param callback The callback to register.
** @param cleanup The cleanup callback to register.
** @param user_data User data to pass to the callbacks.
*/
void tf_ssb_add_connections_changed_callback(tf_ssb_t* ssb, tf_ssb_connections_changed_callback_t* callback, tf_ssb_callback_cleanup_t* cleanup, void* user_data);

/**
** Remove a callback when the connection list changes.
** @param ssb The SSB instance.
** @param callback The callback.
** @param user_data The user data registered with the callback.
*/
void tf_ssb_remove_connections_changed_callback(tf_ssb_t* ssb, tf_ssb_connections_changed_callback_t* callback, void* user_data);

/**
** A callback called when a new broadcast is received or one expires.
** @param ssb The SSB instance.
** @param user_data The user data.
*/
typedef void(tf_ssb_broadcasts_changed_callback_t)(tf_ssb_t* ssb, void* user_data);

/**
** Register a callback when broadcasts change.
** @param ssb The SSB instance.
** @param callback The callback function.
** @param cleanup A function to call when the callback is removed.
** @param user_data User data to pass to the callback.
*/
void tf_ssb_add_broadcasts_changed_callback(tf_ssb_t* ssb, tf_ssb_broadcasts_changed_callback_t* callback, tf_ssb_callback_cleanup_t* cleanup, void* user_data);

/**
** Remove a callback registered for when broadcasts changed.
** @param ssb The SSB instance.
** @param callback The callback function.
** @param user_data The user data registered with the callback.
*/
void tf_ssb_remove_broadcasts_changed_callback(tf_ssb_t* ssb, tf_ssb_broadcasts_changed_callback_t* callback, void* user_data);

/**
** A callback called when a message is added to the database.
** @param ssb The SSB instance.
** @param user_data The user data.
*/
typedef void(tf_ssb_message_added_callback_t)(tf_ssb_t* ssb, void* user_data);

/**
** Register a callback called when a message is added to the database.
** @param ssb The SSB instance.
** @param callback The callback function.
** @param cleanup A function to call when the callback is removed.
** @param user_data User data to pass to the callback.
*/
void tf_ssb_add_message_added_callback(tf_ssb_t* ssb, tf_ssb_message_added_callback_t* callback, tf_ssb_callback_cleanup_t* cleanup, void* user_data);

/**
** Remove a callback registered for when a message is added to the database.
** @param ssb The SSB instance.
** @param callback The callback function.
** @param user_data User data registered with the callback.
*/
void tf_ssb_remove_message_added_callback(tf_ssb_t* ssb, tf_ssb_message_added_callback_t* callback, void* user_data);

/**
** Call all callbacks registered for when a message is added to the database.
** @param ssb The SSB instance.
*/
void tf_ssb_notify_message_added(tf_ssb_t* ssb);

/**
** A callback called when a blob is added to the database.
** @param ssb The SSB instance.
** @param id The blob identifier.
** @param user_data The user data.
*/
typedef void(tf_ssb_blob_stored_callback_t)(tf_ssb_t* ssb, const char* id, void* user_data);

/**
** Register a callback called when a blob is added to the database.
** @param ssb The SSB instance.
** @param callback The callback function.
** @param cleanup A function to call when the callback is removed.
** @param user_data User data to pass to the callback.
*/
void tf_ssb_add_blob_stored_callback(tf_ssb_t* ssb, tf_ssb_blob_stored_callback_t* callback, tf_ssb_callback_cleanup_t* cleanup, void* user_data);

/**
** Remove a callback registered for when a blob is added to the database.
** @param ssb The SSB instance.
** @param callback The callback function.
** @param user_data User data registered with the callback.
*/
void tf_ssb_remove_blob_stored_callback(tf_ssb_t* ssb, tf_ssb_blob_stored_callback_t* callback, void* user_data);

/**
** Record that a new blob was stored.
** @param ssb The SSB instance.
** @param id The identity of the newly stored blob.
*/
void tf_ssb_notify_blob_stored(tf_ssb_t* ssb, const char* id);

/**
** A callback called when a blob is newly requested.
** @param ssb The SSB instance.
** @param id The blob identity.
** @param user_data The user data.
*/
typedef void(tf_ssb_blob_want_added_callback_t)(tf_ssb_t* ssb, const char* id, void* user_data);

/**
** Register a function to be called when a blob is newly requested.
** @param ssb The SSB instance.
** @param callback The callback.
** @param cleanup A function to call when the callback is removed.
** @param user_data User data to pass to the callback.
*/
void tf_ssb_add_blob_want_added_callback(tf_ssb_t* ssb, tf_ssb_blob_want_added_callback_t* callback, tf_ssb_callback_cleanup_t* cleanup, void* user_data);

/**
** Remove a callback registered for when a blob is newly requested.
** @param ssb The SSB instance.
** @param callback The callback to remove.
** @param user_data The user data registered with the callback.
*/
void tf_ssb_remove_blob_want_added_callback(tf_ssb_t* ssb, tf_ssb_blob_want_added_callback_t* callback, void* user_data);

/**
** Call all callbacks registered for when a blob is newly requested.
** @param ssb The SSB instance.
** @param id The requested blob identity.
*/
void tf_ssb_notify_blob_want_added(tf_ssb_t* ssb, const char* id);

/**
** A function called when a MUXRPC request is made.
** @param connection The SSB connection.
** @param flags The RPC flags.
** @param request_number The request number.
** @param args Request arguments.
** @param message The raw message data.
** @param size The size of the raw message data.
** @param user_data User data registered with the callback.
*/
typedef void(tf_ssb_rpc_callback_t)(tf_ssb_connection_t* connection, uint8_t flags, int32_t request_number, JSValue args, const uint8_t* message, size_t size, void* user_data);

/**
** Register a MUXRPC callback by name.
** @param ssb The SSB instance.
** @param name The RPC name as a .-separated string.
** @param callback The callback.
** @param cleanup A function to be called when the callback is removed.
** @param user_data User data to pass to the callback.
*/
void tf_ssb_add_rpc_callback(tf_ssb_t* ssb, const char* name, tf_ssb_rpc_callback_t* callback, tf_ssb_callback_cleanup_t* cleanup, void* user_data);

/**
** Remove a MUXRPC callback.
** @param ssb The SSB instance.
** @param name The NULL-terminated name.
** @param callback The callback to remove.
** @param user_data The user data registered with the callback.
*/
void tf_ssb_remove_rpc_callback(tf_ssb_t* ssb, const char** name, tf_ssb_rpc_callback_t* callback, void* user_data);

/**
** Send a MUXRPC message.
** @param connection The connection on which to send the message.
** @param flags The message flags.
** @param request_number The request number.
** @param new_request_name The name of the request if it is new.
** @param message The message payload.
** @param size The size of the message.
** @param callback A callback to call if a response is received.
** @param cleanup A callback to call if the callback is removed.
** @param user_data User data to pass to the callback.
** @return true If the message was queued to send, false if the connection or request were invalid.
*/
bool tf_ssb_connection_rpc_send(tf_ssb_connection_t* connection, uint8_t flags, int32_t request_number, const char* new_request_name, const uint8_t* message, size_t size,
	tf_ssb_rpc_callback_t* callback, tf_ssb_callback_cleanup_t* cleanup, void* user_data);

/**
** Send a JSON MUXRPC message.
** @param connection The connection on which to send the message.
** @param flags The message flags.
** @param request_number The request number.
** @param new_request_name The name of the request if it is new.
** @param message The JS message payload.
** @param callback A callback to call if a response is received.
** @param cleanup A callback to call if the callback is removed.
** @param user_data User data to pass to the callback.
** @return true If the message was queued to send, false if the connection or request were invalid.
*/
bool tf_ssb_connection_rpc_send_json(tf_ssb_connection_t* connection, uint8_t flags, int32_t request_number, const char* new_request_name, JSValue message,
	tf_ssb_rpc_callback_t* callback, tf_ssb_callback_cleanup_t* cleanup, void* user_data);

/**
** Send a MUXRPC error message.
** @param connection The connection on which to send the message.
** @param flags The message flags.
** @param request_number The request number.
** @param error The error string.
** @return true If the message was queued to send, false if the connection or request were invalid.
*/
bool tf_ssb_connection_rpc_send_error(tf_ssb_connection_t* connection, uint8_t flags, int32_t request_number, const char* error);

/**
** Send a MUXRPC "method not allowed" error message.
** @param connection The connection on which to send the message.
** @param flags The message flags.
** @param request_number The request number.
** @param name The name of the not-allowed method.
** @return true If the message was queued to send, false if the connection or request were invalid.
*/
bool tf_ssb_connection_rpc_send_error_method_not_allowed(tf_ssb_connection_t* connection, uint8_t flags, int32_t request_number, const char* name);

/**
** Register a callback to be called when a message is received for the given
** request number.
** @param connection The connection on which to register the callback.
** @param request_number The request number.
** @param name The name of the RPC request.
** @param callback The callback.
** @param cleanup The function to call when the callback is removed.
** @param user_data User data to pass to the callback.
** @param dependent_connection A connection, which, if removed, invalidates this request.
*/
void tf_ssb_connection_add_request(tf_ssb_connection_t* connection, int32_t request_number, const char* name, tf_ssb_rpc_callback_t* callback, tf_ssb_callback_cleanup_t* cleanup,
	void* user_data, tf_ssb_connection_t* dependent_connection);

/**
** Remove a callback registered to be called when a message is received for the
** given request number.
** @param connection The connection.
** @param request_number The request number.
*/
void tf_ssb_connection_remove_request(tf_ssb_connection_t* connection, int32_t request_number);

/**
** Get a debug representation of active requests.
** @param connection The connection.
** @return The active requests as a JS object.
*/
JSValue tf_ssb_connection_requests_to_object(tf_ssb_connection_t* connection);

/**
** A function scheduled to be run later.
** @param connection The owning connection.
** @param skip Whether the work ought to be skipped, because it is being replaced.
** @param user_data User data registered with the callback.
*/
typedef void(tf_ssb_scheduled_callback_t)(tf_ssb_connection_t* connection, bool skip, void* user_data);

/**
** Schedule work to be run when the connection is next idle.
** @param connection The owning connection.
** @param key A key identifying the work.  If work by the same key already exists, the new request will be discarded.
** @param callback The callback to call.
** @param user_data User data to pass to the callback.
*/
void tf_ssb_connection_schedule_idle(tf_ssb_connection_t* connection, const char* key, tf_ssb_scheduled_callback_t* callback, void* user_data);

/**
** Schedule work to run on a worker thread.
** @param connection The owning connection.
** @param work_callback The callback to run on a thread.
** @param after_work_callback The callback to run on the main thread when the work is complete.
** @param user_data User data to pass to the callback.
*/
void tf_ssb_connection_run_work(tf_ssb_connection_t* connection, void (*work_callback)(tf_ssb_connection_t* connection, void* user_data),
	void (*after_work_callback)(tf_ssb_connection_t* connection, int result, void* user_data), void* user_data);

/**
** Schedule work to run on a worker thread.
** @param ssb The owning SSB instance.
** @param work_callback The callback to run on a thread.
** @param after_work_callback The callback to run on the main thread when the work is complete.
** @param user_data User data to pass to the callback.
*/
void tf_ssb_run_work(
	tf_ssb_t* ssb, void (*work_callback)(tf_ssb_t* ssb, void* user_data), void (*after_work_callback)(tf_ssb_t* ssb, int result, void* user_data), void* user_data);

/**
** Register for new messages on a connection.
** @param connection The SHS connection.
** @param author The author for whom to request new messages.
** @param request_number The MUXRPC request on which to send new messages.
** @param keys Whether to send with keys.
*/
void tf_ssb_connection_add_new_message_request(tf_ssb_connection_t* connection, const char* author, int32_t request_number, bool keys);

/**
** Remove a request for new messages on a connection.
** @param connection the SHS connection.
** @param author The author for whom to no longer request new messages.
*/
void tf_ssb_connection_remove_new_message_request(tf_ssb_connection_t* connection, const char* author);

/**
** Get whether we are an attendant on a room connection.
** @param connection The SHS connection.
** @return True if this is an attendant connection.
*/
bool tf_ssb_connection_is_attendant(tf_ssb_connection_t* connection);

/**
** Get the request number used to notify of room attendant changes.
** @param connection the SHS connection.
** @return A request number.
*/
int32_t tf_ssb_connection_get_attendant_request_number(tf_ssb_connection_t* connection);

/**
** Register for attendant change notifications on a connection.
** @param connection The SHS connection.
** @param attendant Whether this connection will be an attendant.
** @param request_number The request number on which to send attendant changes.
*/
void tf_ssb_connection_set_attendant(tf_ssb_connection_t* connection, bool attendant, int request_number);

/**
** Register for endpoint change notifications on a connection.
** @param connection The SHS connection.
** @param endpoint Whether this connection will be an endpoint.
** @param request_number The request number on which to send endpoint changes.
*/
void tf_ssb_connection_set_endpoint(tf_ssb_connection_t* connection, bool endpoint, int request_number);

/**
** Get whether we are a potential tunnel endpoint.
** @param connection The SHS connection.
** @return True if this is an endpoint connection.
*/
bool tf_ssb_connection_is_endpoint(tf_ssb_connection_t* connection);

/**
** Get the request number used to notify of tunnel endpoint changes.
** @param connection the SHS connection.
** @return A request number.
*/
int32_t tf_ssb_connection_get_endpoint_request_number(tf_ssb_connection_t* connection);

/**
** Clear all attendants from a room.
** @param connection The SHS connection.
*/
void tf_ssb_connection_clear_room_attendants(tf_ssb_connection_t* connection);

/**
** Add a room attendant.
** @param connection The SHS connection.
** @param id The attendant identifier.
*/
void tf_ssb_connection_add_room_attendant(tf_ssb_connection_t* connection, const char* id);

/**
** Remove a room attendant.
** @param connection The SHS connection.
** @param id The attendanr identifier.
*/
void tf_ssb_connection_remove_room_attendant(tf_ssb_connection_t* connection, const char* id);

/**
** Create a tunnel.
** @param ssb The SSB instance.
** @param portal_id The identity of the tunnel intermediary.
** @param request_number The tunnel request.
** @param target_id The identity being tunneled to.
** @param connect_flags Flags affecting the connection.
** @return The new tunnel connection.
*/
tf_ssb_connection_t* tf_ssb_connection_tunnel_create(tf_ssb_t* ssb, const char* portal_id, int32_t request_number, const char* target_id, int connect_flags);

/**
** Get the request number on which to send EBT responses.
** @param connection The SHS connection.
** @return The request number.
*/
int32_t tf_ssb_connection_get_ebt_request_number(tf_ssb_connection_t* connection);

/**
** Set the request number on which to send EBT responses.
** @param connection The SHS connection.
** @param request_number The request number.
*/
void tf_ssb_connection_set_ebt_request_number(tf_ssb_connection_t* connection, int32_t request_number);

/**
** Get general statistics about an SSB instance.
** @param ssb The SSB instance.
** @param[out] out_stats Populated with performance statistics.
*/
void tf_ssb_get_stats(tf_ssb_t* ssb, tf_ssb_stats_t* out_stats);

/**
** Get information about requested blobs.
** @param connection An SHS connection.
** @return Blob wants information.
*/
tf_ssb_blob_wants_t* tf_ssb_connection_get_blob_wants_state(tf_ssb_connection_t* connection);

/**
** Record whether the calling thread is busy.
** @param ssb The SSB instance.
** @param busy True if the calling thread is now busy.
*/
void tf_ssb_record_thread_busy(tf_ssb_t* ssb, bool busy);

/**
** Get an estimate of utilization of all running threads.
** @param ssb The SSB instance.
** @return The utilization percent.
*/
float tf_ssb_get_average_thread_percent(tf_ssb_t* ssb);

/**
** Get the queue of messages in the progress of being stored.
** @param ssb The SSB instance.
** @return The queue.
*/
tf_ssb_store_queue_t* tf_ssb_get_store_queue(tf_ssb_t* ssb);

/**
** Record whether the calling thread is the main thread or not.  Some
** operations are disallowed on the main thread for performance.
** @param ssb The SSB instance.
** @param main_thread Whether the calling thread is the main thread.
*/
void tf_ssb_set_main_thread(tf_ssb_t* ssb, bool main_thread);

/**
** Get whether the running server is operating a room.
** @param ssb The SSB instance.
** @return True if the server is a room.
*/
bool tf_ssb_is_room(tf_ssb_t* ssb);

/**
** Set whether the running server is operating a room.
** @param ssb The SSB instance.
** @param is_room Whether to run a room.
*/
void tf_ssb_set_is_room(tf_ssb_t* ssb, bool is_room);

/**
** Get whether the running server supports replication of messages and blobs.
** @param ssb The SSB instance.
** @return True if the server is a replicator.
*/
bool tf_ssb_is_replicator(tf_ssb_t* ssb);

/**
** Set whether the running server supports replication of messages and blobs.
** @param ssb The SSB instance.
** @param is_replicator Whether to support replication.
*/
void tf_ssb_set_is_replicator(tf_ssb_t* ssb, bool is_replicator);

/**
** Get whether the running server participates in peer exchange.
** @param ssb The SSB instance.
** @return True if the server participates in peer exchange.
*/
bool tf_ssb_is_peer_exchange(tf_ssb_t* ssb);

/**
** Set whether the running server participates in peer exchange.
** @param ssb The SSB instance.
** @param is_peer_exchange Whether to participate in peer exchange.
*/
void tf_ssb_set_is_peer_exchange(tf_ssb_t* ssb, bool is_peer_exchange);

/**
** Get the name of the room hosted by the running server.
** @param ssb The SSB instance.
** @return The room name or NULL.
*/
const char* tf_ssb_get_room_name(tf_ssb_t* ssb);

/**
** Set the name of the room hosted by the running server.
** @param ssb The SSB instance.
** @param room_name The name of the room.
*/
void tf_ssb_set_room_name(tf_ssb_t* ssb, const char* room_name);

/**
** Schedule work to be run after a time delay.
** @param ssb The SSB instance.
** @param delay_ms The duration to wait in milliseconds.
** @param callback The callback to call to run the work.
** @param user_data User data to pass to the callback.
*/
void tf_ssb_schedule_work(tf_ssb_t* ssb, int delay_ms, void (*callback)(tf_ssb_t* ssb, void* user_data), void* user_data);

/**
** Verify a signature.
** @param public_key The public key for which the message was signed.
** @param payload The signed payload.
** @param payload_length The length of the signed payload in bytes.
** @param signature The signature.
** @param signature_is_urlb64 True if the signature is in URL base64 format, otherwise standard base64.
** @return true If the message was successfully verified.
*/
bool tf_ssb_hmacsha256_verify(const char* public_key, const void* payload, size_t payload_length, const char* signature, bool signature_is_urlb64);

/**
** Adjust read backpressure.  If it gets too high, TCP receive will be paused
** until it lowers.
** @param connection The connection on which to affect backpressure.
** @param delta The change in backpressure.  Higher will eventually pause
** receive.  Lower will resume it.
*/
void tf_ssb_connection_adjust_read_backpressure(tf_ssb_connection_t* connection, int delta);

/**
** Adjust write count.  Work scheduled by tf_ssb_connection_schedule_idle will
** only start when this reaches zero.
** @param connection The connection on which to affect backpressure.
** @param delta The change in write count.  Higher will pause processing
** scheduled idle work queue.  Lower will resume it.
*/
void tf_ssb_connection_adjust_write_count(tf_ssb_connection_t* connection, int delta);

/**
** Get the reason why a connection is going away.
** @param connection The connection.
** @return The reason or NULL.
*/
const char* tf_ssb_connection_get_destroy_reason(tf_ssb_connection_t* connection);

/**
** Initiate a tunnel connection.
** @param ssb The SSB instance.
** @param portal_id The public key of the instance through which to tunnel.
** @param target_id The public key of the instance with which to establish a connection.
** @param connect_flags Flags affecting the connection.
** @return true if the tunnel instance was found.
*/
bool tf_ssb_tunnel_create(tf_ssb_t* ssb, const char* portal_id, const char* target_id, int connect_flags);

/**
** Initiate a one time sync operation.
** @param ssb The SSB instance.
*/
void tf_ssb_sync_start(tf_ssb_t* ssb);

/**
** Get a connection's flags.
** @param connection The connection.
*/
int tf_ssb_connection_get_flags(tf_ssb_connection_t* connection);

/**
** Get a connection's EBT state.
** @param connection The connection.
** @return the EBT state for the connection.
*/
tf_ssb_ebt_t* tf_ssb_connection_get_ebt(tf_ssb_connection_t* connection);

/**
** Encrypt a private message to a set of recipients.
** @param private_key The private key of the author.
** @param recipients A list of recipient identities.
** @param recipients_count The number of recipients in recipients.
** @param message The plain text to post.
** @param message_size The length in bytes of message.
** @return A secret box string.  Free with tf_free().
*/
char* tf_ssb_private_message_encrypt(uint8_t* private_key, const char** recipients, int recipients_count, const char* message, size_t message_size);

/**
** Send any createHistoryStream RPC messages appropriate for a newly stored message.
** @param ssb The SSB instance.
** @param id The message ID.
** @param previous The previous message ID.
** @param author The author.
** @param sequence The message sequence number.
** @param timestamp The message timestamp.
** @param hash The message hash type.
** @param content The message content.
** @param signature The message signature.
** @param flags Message flags.
*/
void tf_ssb_send_create_history_stream_responses(tf_ssb_t* ssb, const char* id, const char* previous, const char* author, int32_t sequence, double timestamp, const char* hash,
	const char* content, const char* signature, int flags);

/**
** Get a connection's unique index.
** @param connection The SSB connection.
** @return The index.
*/
int tf_ssb_connection_get_index(tf_ssb_connection_t* connection);

/**
** Get the connection index of the parent connection for a tunneled connection.
** @param connection The tunneled connection.
** @return The parent connection index.
*/
int tf_ssb_connection_get_tunnel_index(tf_ssb_connection_t* connection);

/** @} */
