#pragma once

/**
** \defgroup tls TLS
** A minimal wrapper around OpenSSL.
** @{
*/

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

/**
** A TLS context.  May have many tf_tls_session_t instances.
*/
typedef struct _tf_tls_context_t tf_tls_context_t;

/**
** A TLS session.  Belongs to one tf_tls_context_t and represents a single connection.
*/
typedef struct _tf_tls_session_t tf_tls_session_t;

/**
** The state of a TLS handshake.
*/
typedef enum _tf_tls_handshake_t
{
	k_tls_handshake_done,
	k_tls_handshake_more,
	k_tls_handshake_failed,
} tf_tls_handshake_t;

/**
** Possible error statuses from tf_tls_session_read_plain.
*/
typedef enum _tf_tls_read_t
{
	k_tls_read_zero = -1,
	k_tls_read_failed = -2,
} tf_tls_read_t;

/**
** Create a TLS context.  Clean up with tf_tls_context_destroy().
** @return A new TLS context.
*/
tf_tls_context_t* tf_tls_context_create();

/**
** Set the TLS context's server certificate.
** @param context The TLS context.
** @param certificate The certificate in PEM format.
** @return true if set successfully.
*/
bool tf_tls_context_set_certificate(tf_tls_context_t* context, const char* certificate);

/**
** Set the TLS context's server certificate's private key.
** @param context The TLS context.
** @param private_key The private key in PEM format.
** @return true if set successfully.
*/
bool tf_tls_context_set_private_key(tf_tls_context_t* context, const char* private_key);

/**
** Add a trusted certificate.
** @param context The TLS context.
** @param certificate The certificate in PEM format.
** @return true if the certificate was added to the trusted list successfully.
*/
bool tf_tls_context_add_trusted_certificate(tf_tls_context_t* context, const char* certificate);

/**
** Create a TLS session from a context.  Once created, call
** tf_tls_session_handshake() until it returns k_tls_handshake_done.  Call
** tf_tls_session_[read/write]_[plain/encrypted]() as data is available.
** @param context The TLS context.  Clean up with tf_tls_session_destroy().
** @return A new TLS session.
*/
tf_tls_session_t* tf_tls_context_create_session(tf_tls_context_t* context);

/**
** Destroy a TLS context.
** @param context The TLS contextx created by tf_tls_context_create().
*/
void tf_tls_context_destroy(tf_tls_context_t* context);

/**
** Destroy a TLS session.
** @param session A TLS sesssion created by tf_tls_context_create_session().
*/
void tf_tls_session_destroy(tf_tls_session_t* session);

/**
** Set the remote hostname for a session.
** @param session The TLS session.
** @param hostname The hostname.
*/
void tf_tls_session_set_hostname(tf_tls_session_t* session, const char* hostname);

/**
** Begin an outgoing TLS session.
** @param session The TLS session.
*/
void tf_tls_session_start_accept(tf_tls_session_t* session);

/**
** Begin an incoming TLS session.
** @param session The TLS session.
*/
void tf_tls_session_start_connect(tf_tls_session_t* session);

/**
** Begin the clean shutdown process for a TLS session.
** @param session The TLS session.
*/
void tf_tls_session_shutdown(tf_tls_session_t* session);

/**
** Get the certificate from the remote end of a TLS session if available.
** @param session The TLS session.
** @param buffer A buffer to receive the certificate.
** @param bytes The size of the buffer.
** @return The size of the returned certificate, or -1 on failure.
*/
int tf_tls_session_get_peer_certificate(tf_tls_session_t* session, char* buffer, size_t bytes);

/**
** Update the TLS handshake.  Call repeatedly as new data is available until it returns done.
** @param session The TLS session.
** @return The current state of the handshake process.
*/
tf_tls_handshake_t tf_tls_session_handshake(tf_tls_session_t* session);

/**
** Read decrypted data from the TLS session.
** @param session The TLS session.
** @param buffer A buffer to receive the data.
** @param bytes The size of the buffer.
** @return The number of bytes returned.
*/
int tf_tls_session_read_plain(tf_tls_session_t* session, char* buffer, size_t bytes);

/**
** Write unencrypted data to the TLS session.
** @param session The TLS session.
** @param buffer The data to encrypt.
** @param bytes The size of the data.
** @return 1 on success, 0 on failure.
*/
int tf_tls_session_write_plain(tf_tls_session_t* session, const char* buffer, size_t bytes);

/**
** Read encrypted data from the TLS session that needs to be sent.
** @param session The TLS session.
** @param buffer A buffer to receive the data.
** @param bytes The size of the buffer.
** @return The number of bytes returned.
*/
int tf_tls_session_read_encrypted(tf_tls_session_t* session, char* buffer, size_t bytes);

/**
** Write encrypted data to the TLS session.
** @param session The TLS session.
** @param buffer The encrypted data.
** @param bytes The number of bytes written.
*/
int tf_tls_session_write_encrypted(tf_tls_session_t* session, const char* buffer, size_t bytes);

/**
** Retrieve the last error from a TLS session.
** @param session The TLS session.
** @param buffer A buffer to receive the error text.
** @param bytes The size of the buffer.
** @return true if an error was retrieved.
*/
bool tf_tls_session_get_error(tf_tls_session_t* session, char* buffer, size_t bytes);

/** @} */
