/*
copyright: Boaz segev, 2016-2017
license: MIT

Feel free to copy, use and enjoy according to the license provided.
*/
#ifndef HTTP_H
#define HTTP_H
#define LIB_FACIL_HTTP_VERSION_MAJOR 0
#define LIB_FACIL_HTTP_VERSION_MINOR 4
#define LIB_FACIL_HTTP_VERSION_PATCH 0
#include "facil.h"

/** an HTTP/1.1 vs. HTTP/2 identifier. */
enum HTTP_VERSION { HTTP_V1 = 0, HTTP_V2 = 1 };

/** HTTP header information */
typedef struct {
  const char *name;
  union {
    const char *data;
    const char *value;
  };
  uint32_t name_len;
  union {
    uint32_t data_len;
    uint32_t value_len;
  };
} http_header_s;

/* *****************************************************************************
Core include files
*/
// clang-format off
#include <time.h>
#include "http_request.h"
#include "http_response.h"
// clang-format on
/* *****************************************************************************
Hard Coded Settings
*/

/** When a new connection is accepted, it will be immediately declined with a
 * 503 service unavailable (server busy) response unless the following number of
 * file descriptors is available.*/
#ifndef HTTP_BUSY_UNLESS_HAS_FDS
#define HTTP_BUSY_UNLESS_HAS_FDS 64
#endif

#ifndef HTTP_DEFAULT_BODY_LIMIT
#define HTTP_DEFAULT_BODY_LIMIT (1024 * 1024 * 50)
#endif

/* *****************************************************************************
HTTP Core API & data structure
*/

/** Manages protocol settings for the HTTP protocol */
typedef struct {
  /**
  The maximum size of an HTTP request's body (when posting data).

  Defaults to ~ 50Mb.
  */
  size_t max_body_size;
  /** the callback to be performed when requests come in. */
  void (*on_request)(http_request_s *request);
  /**
  A public folder for file transfers - allows to circumvent any application
  layer server and simply serve files.
  */
  const char *public_folder;
  /**
  The length of the public_folder string.
  */
  size_t public_folder_length;
  /** (optional)
   * Allows a an implementation for the transport layer (i.e. TLS) without
   * effecting the HTTP protocol.
   */
  sock_rw_hook_s *(*set_rw_hooks)(intptr_t fduuid, void *rw_udata);
  /** Opaque user data for `set_rw_hooks`. */
  void *rw_udata;
  /**
  Logging flag - set to TRUE to log static file requests.

  Dynamic request logging is always the dynamic application's responsibility.
  */
  uint8_t log_static;
  /** An HTTP connection timeout. For HTTP/1.1 this defaults to ~5 seconds.*/
  uint8_t timeout;
  /**
  The default HTTP version which a new connection will use. At the moment, only
  version HTTP/1.1 is supported.
  */
  enum HTTP_VERSION version;
  /**
  internal flag for library use.
  */
  uint8_t private_metaflags;
} http_settings_s;

typedef protocol_s *(*http_on_open_func)(intptr_t, void *);
typedef void (*http_on_finish_func)(void *);
/**
Return the callback used for creating the HTTP protocol in the `settings`.
*/
http_on_open_func http_get_on_open_func(http_settings_s *settings);
/**
Return the callback used for freeing the HTTP protocol in the `settings`.
*/
http_on_finish_func http_get_on_finish_func(http_settings_s *settings);

/**
Listens for incoming HTTP connections on the specified posrt and address,
implementing the requested settings.

Since facil.io doesn't support native TLS/SLL
*/
int http_listen(const char *port, const char *address,
                http_settings_s settings);

#define http_listen(port, address, ...)                                        \
  http_listen((port), (address), (http_settings_s){__VA_ARGS__})

/* *****************************************************************************
HTTP Helper functions that might be used globally
*/

/**
A faster (yet less localized) alternative to `gmtime_r`.

See the libc `gmtime_r` documentation for details.

Falls back to `gmtime_r` for dates before epoch.
*/
struct tm *http_gmtime(const time_t *timer, struct tm *tmbuf);

/**
Writes an HTTP date string to the `target` buffer.

This requires _____ bytes of space to be available at the target buffer.

Returns the number of bytes actually written.
*/
size_t http_date2str(char *target, struct tm *tmbuf);

/**
A fast, inline alternative to `sprintf(dest, "%lu", num)`.

Writes an **unsigned** number to a buffer, as a string. This is an unsafe
functions that assumes the buffer will have at least 21 bytes and isn't NULL.

A NULL terminating byte is written.

Returns the number of bytes actually written (excluding the NULL byte).
*/
static inline size_t http_ul2a(char *dest, size_t num) {
  uint8_t digits = 1;
  size_t tmp = num;
  while ((tmp /= 10))
    ++digits;

  dest += digits;
  *(dest--) = 0;
  for (size_t i = 0; i < digits; i++) {
    num = num - (10 * (tmp = (num / 10)));
    *(dest--) = '0' + num;
    num = tmp;
  }
  return digits;
}

/** Decodes a URL encoded string, no buffer overflow protection. */
ssize_t http_decode_url_unsafe(char *dest, const char *url_data);

/** Decodes a URL encoded string. */
ssize_t http_decode_url(char *dest, const char *url_data, size_t length);

#endif
