/*
    Copyright Copyright (C) 2013 Andrey Uzunov

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/

/**
 * @file mhd2spdy_structures.h
 * @brief  Common structures, functions, macros and global variables.
 * @author Andrey Uzunov
 */
#ifndef STRUCTURES_H
#define STRUCTURES_H

#define _GNU_SOURCE
 
#include <unistd.h>
#include <stdlib.h>
#include <stdint.h>
#include <stdbool.h>
#include <string.h>
#include <stdio.h>
#include <ctype.h>
#include <errno.h>
#include <assert.h>
#include <microhttpd.h>
#include <signal.h>
#include <poll.h>
#include <fcntl.h>
#include <regex.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>
#include <netinet/in.h>
#include <netinet/tcp.h>
#include <openssl/ssl.h>
#include <openssl/err.h>
#include <spdylay/spdylay.h>
#include <getopt.h>


/* WANT_READ if SSL connection needs more input; or WANT_WRITE if it
   needs more output; or IO_NONE. This is necessary because SSL/TLS
   re-negotiation is possible at any time. Spdylay API offers
   similar functions like spdylay_session_want_read() and
   spdylay_session_want_write() but they do not take into account
   SSL connection. */
enum
{
  IO_NONE,
  WANT_READ,
  WANT_WRITE
};


struct Proxy;


struct SPDY_Connection {
  SSL *ssl;
  spdylay_session *session;
  struct SPDY_Connection *prev;
  struct SPDY_Connection *next;
  struct Proxy *proxies_head;
  struct Proxy *proxies_tail;
  char *host;
  int fd;
  int want_io;
  uint counter;
  uint streams_opened;
  bool is_tls;
};


struct URI
{
  char * full_uri;
  char * scheme;
  char * host_and_port;
  char * host;
  char * path;
  char * path_and_more;
  char * query;
  char * fragment;
  uint16_t port;
};


struct HTTP_URI;


struct Proxy
{
	struct MHD_Connection *http_connection;
	struct MHD_Response *http_response;
	struct URI *uri;
  struct HTTP_URI *http_uri;
  struct SPDY_Connection *spdy_connection;
  struct Proxy *next;
  struct Proxy *prev;
	char *url;
	char *version;
	void *http_body;
	void *received_body;
	size_t http_body_size;
	size_t received_body_size;
	ssize_t length;
	int status;
	int id;
  int32_t stream_id;
	bool done;
	bool http_error;
	bool spdy_error;
  bool http_active;
  bool spdy_active;
  bool receiving_done;
};


struct HTTP_URI
{
  char * uri;
  struct Proxy * proxy;
};


struct SPDY_Headers
{
  const char **nv;
  int num;
  int cnt;
};


struct global_options
{
  char *spdy2http_str;
  struct SPDY_Connection *spdy_connection;
  struct SPDY_Connection *spdy_connections_head;
  struct SPDY_Connection *spdy_connections_tail;
  int streams_opened;
  int responses_pending;
  regex_t uri_preg;
  size_t global_memory;
  SSL_CTX *ssl_ctx;
  uint32_t total_spdy_connections;
  uint16_t spdy_proto_version;
  uint16_t listen_port;
  bool verbose;
  bool only_proxy;
  bool spdy_data_received;
  bool statistics;
  bool ignore_rst_stream;
}
glob_opt;


struct global_statistics
{
  //unsigned long long http_bytes_sent;
  //unsigned long long http_bytes_received;
  unsigned long long spdy_bytes_sent;
  unsigned long long spdy_bytes_received;
  unsigned long long spdy_bytes_received_and_dropped;
}
glob_stat;


//forbidden headers
#define SPDY_HTTP_HEADER_TRANSFER_ENCODING "transfer-encoding"
#define SPDY_HTTP_HEADER_PROXY_CONNECTION "proxy-connection"
#define SPDY_HTTP_HEADER_KEEP_ALIVE "keep-alive"
#define SPDY_HTTP_HEADER_CONNECTION "connection"

#define MAX_SPDY_CONNECTIONS 100

#define SPDY_MAX_OUTLEN 4096

/**
 * Insert an element at the head of a DLL. Assumes that head, tail and
 * element are structs with prev and next fields.
 *
 * @param head pointer to the head of the DLL (struct ? *)
 * @param tail pointer to the tail of the DLL (struct ? *)
 * @param element element to insert (struct ? *)
 */
#define DLL_insert(head,tail,element) do { \
	(element)->next = (head); \
	(element)->prev = NULL; \
	if ((tail) == NULL) \
		(tail) = element; \
	else \
		(head)->prev = element; \
	(head) = (element); } while (0)


/**
 * Remove an element from a DLL. Assumes
 * that head, tail and element are structs
 * with prev and next fields.
 *
 * @param head pointer to the head of the DLL (struct ? *)
 * @param tail pointer to the tail of the DLL (struct ? *)
 * @param element element to remove (struct ? *)
 */
#define DLL_remove(head,tail,element) do { \
	if ((element)->prev == NULL) \
		(head) = (element)->next;  \
	else \
		(element)->prev->next = (element)->next; \
	if ((element)->next == NULL) \
		(tail) = (element)->prev;  \
	else \
		(element)->next->prev = (element)->prev; \
	(element)->next = NULL; \
	(element)->prev = NULL; } while (0)


#define PRINT_INFO(msg) do{\
  if(glob_opt.verbose){\
	printf("%i:%s\n", __LINE__, msg);\
	fflush(stdout);\
	}\
  }\
	while(0)


#define PRINT_INFO2(fmt, ...) do{\
  if(glob_opt.verbose){\
	printf("%i\n", __LINE__);\
	printf(fmt,##__VA_ARGS__);\
	printf("\n");\
	fflush(stdout);\
	}\
	}\
	while(0)
  

#define DIE(msg) do{\
	printf("FATAL ERROR (line %i): %s\n", __LINE__, msg);\
	fflush(stdout);\
  exit(EXIT_FAILURE);\
	}\
	while(0)
  
  
#define UPDATE_STAT(stat, value) do{\
  if(glob_opt.statistics)\
  {\
    stat += value;\
  }\
  }\
  while(0)


void
free_uri(struct URI * uri);


int
init_parse_uri(regex_t * preg);


void
deinit_parse_uri(regex_t * preg);


int
parse_uri(regex_t * preg,
          char * full_uri,
          struct URI ** uri);


void
free_proxy(struct Proxy *proxy);


void *
au_malloc(size_t size);


bool
copy_buffer(const void *src, size_t src_size, void **dst, size_t *dst_size);

#endif