#pragma once

/**
** \defgroup http HTTP Server
** This is a HTTP server.
**
** It can listen on multiple ports.  It supports IPv4
** and IPv6.  It handles websocket connections.  Requests can be handled
** immediately or at a later time.  It is very bare bones, and that is a
** feature.
** @{
*/

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

/** An HTTP connection. */
typedef struct _tf_http_connection_t tf_http_connection_t;

/** An HTTP request. */
typedef struct _tf_http_request_t tf_http_request_t;

/** An HTTP instance. */
typedef struct _tf_http_t tf_http_t;

/** A trace instance. */
typedef struct _tf_trace_t tf_trace_t;

/** An event loop. */
typedef struct uv_loop_s uv_loop_t;

/**
** A callback called when receiving a websocket message.
** @param request The HTTP request.
** @param op_code The type of websocket message.
** @param data The payload.
** @param size The size of the payload in bytes.
*/
typedef void(tf_http_message_callback)(tf_http_request_t* request, int op_code, const void* data, size_t size);

/**
** A callback called when a request closes.
** @param request The HTTP request.
*/
typedef void(tf_http_close_callback)(tf_http_request_t* request);

/**
** A callback called when an HTTP request is received.
** @param request The HTTP request.
*/
typedef void(tf_http_callback_t)(tf_http_request_t* request);

/**
** A callback called when the HTTP instance is destroyed.
** @param user_data User data provided with the callback.
*/
typedef void(tf_http_cleanup_t)(void* user_data);

/**
** An HTTP request.
*/
typedef struct _tf_http_request_t
{
	/** The HTTP instance this request belongs to. */
	tf_http_t* http;
	/** The HTTP connection associated with this request. */
	tf_http_connection_t* connection;
	/** The HTTP method of the request (GET/POST/...). */
	const char* method;
	/** The HTTP request path. */
	const char* path;
	/** The raw HTTP query string. */
	const char* query;
	/** The HTTP request body. */
	void* body;
	/** The length of the HTTP request body. */
	size_t content_length;
	/** Header storage.  Can also be accessed with tf_http_request_get_header(). */
	struct phr_header* headers;
	/** The number of headers stored. */
	int headers_count;
	/** A callback to be called when receiving a websocket message. */
	tf_http_message_callback* on_message;
	/** A callback to be called when the connection is closed. */
	tf_http_close_callback* on_close;
	/** Extra storage for user data. */
	void* context;
	/** User data available to callbacks. */
	void* user_data;
	/** The number of times tf_http_request_ref() has been called without tf_http_request_unref(). */
	int ref_count;
} tf_http_request_t;

/**
** Create an HTTP server using the given libuv loop.
** @param loop A libuv loop to use.
** @return An HTTP server instance.
*/
tf_http_t* tf_http_create(uv_loop_t* loop);

/**
** Register a trace instance with the HTTP instance to record the begin and end
** time of request handlers.
** @param http The HTTP instance to trace.
** @param trace The trace instance to use, or NULL to disable.
*/
void tf_http_set_trace(tf_http_t* http, tf_trace_t* trace);

/**
** Begin listening for HTTP requests on a new port.  May be called multiple
** times to listen on multiple ports.
** @param http The HTTP instance.
** @param port The port on which to listen, or 0 to assign a free port.
** @param local_only Only access connections on localhost, otherwise any address.
** @param cleanup A function called when the HTTP instance is being cleaned up.
** @param user_data User data passed to the cleanup callback.
** @return The port number on which the HTTP instance is now listening.
*/
int tf_http_listen(tf_http_t* http, int port, bool local_only, tf_http_cleanup_t* cleanup, void* user_data);

/**
** Add an HTTP request handler.
** @param http The HTTP instance.
** @param pattern The prefix that the path of incoming requests must match to use this handler.
** @param callback The function to be called to handle the request.
** @param cleanup A function to be called when the request is complete.
** @param user_data User data to pass to the callbacks.
*/
void tf_http_add_handler(tf_http_t* http, const char* pattern, tf_http_callback_t* callback, tf_http_cleanup_t* cleanup, void* user_data);

/**
** Respond to an HTTP request.
** @param request The request.
** @param status The HTTP status with which to respond.
** @param headers Headers to include in the response.  Content-Length will be added internally.
** @param headers_count The number of headers.  The headers array must have
** twice as many entries as this value, since it is both keys and values.
** @param body The response body or NULL.
** @param content_length The length of the response body.
*/
void tf_http_respond(tf_http_request_t* request, int status, const char** headers, int headers_count, const void* body, size_t content_length);

/**
** Get the request body content.
** @param request An incoming request.
** @param out_data The request body content.  Valid until the request is completed.
** @return The size of the request body content.
*/
size_t tf_http_get_body(const tf_http_request_t* request, const void** out_data);

/**
** Destroy an HTTP instance.
** @param http The HTTP instance.
*/
void tf_http_destroy(tf_http_t* http);

/**
** Increment a requests refcount to keep it around after its callback.
** tf_http_respond() may be called at a later time, and tf_http_request_unref()
** must eventually be called in order to not leak the request.
** @param request The request to retain.
*/
void tf_http_request_ref(tf_http_request_t* request);

/**
** Decrement a requests refcount.  tf_http_request_ref() must have been previously called.
** @param request The request.
*/
void tf_http_request_unref(tf_http_request_t* request);

/**
** Get the value of a header from an HTTP request.
** @param request The request.
** @param name The header key.  Matched case insensitively.
** @return The value or NULL.
*/
const char* tf_http_request_get_header(tf_http_request_t* request, const char* name);

/**
** Get a cookie value from request headers.
** @param cookie_header The value of the "Cookie" header of the form
** "name1=value1; name2=value2".
** @param name The cookie name.
** @return The cookie value, if found, or NULL.  Must be freed with tf_free().
*/
const char* tf_http_get_cookie(const char* cookie_header, const char* name);

/**
** Send a websocket message.
** @param request The HTTP request which was previously updated to a websocket
** session with tf_http_request_websocket_upgrade().
** @param op_code Websocket op code.
** @param data The message data.
** @param size The size of data.
*/
void tf_http_request_websocket_send(tf_http_request_t* request, int op_code, const void* data, size_t size);

/**
** Close a websocket.
** @param request The HTTP request which was previously updated to a websocket
** session with tf_http_request_websocket_upgrade().
*/
void tf_http_request_websocket_close(tf_http_request_t* request);

/**
** Upgrade an HTTP request to a websocket session.
** @param request The HTTP request.
*/
void tf_http_request_websocket_upgrade(tf_http_request_t* request);

/**
** Get standard HTTP status text for common HTTP statuses.  200 = "OK", 404 =
** "File not found", etc.
** @param status The HTTP status.
** @return The status text or NULL.
*/
const char* tf_http_status_text(int status);

/**
** Match URL patterns.  "*" matches anything, and "{word}" matches [a-zA-Z][a-zA-Z0-9]*".
** @param pattern The pattern to match.
** @param path The path to test.
** @return true if the path matches the pattern.
*/
bool tf_http_pattern_matches(const char* pattern, const char* path);

/**
** Log debug information to diagnose shutdown problems.
*/
void tf_http_debug_destroy();

/**
** URI-decode a string.
** @param out Populated with the decoded string.
** @param size Size of the out buffer.
** @param uri The string.
** @param length The maximum length to decode.
*/
void tf_http_decode_uri(char* out, size_t size, const char* uri, size_t length);

/** @} */
