/*
    This file is part of libmicrospdy
    Copyright Copyright (C) 2012 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 structures.c
 * @brief  Functions for handling most of the structures in defined
 * 			in structures.h
 * @author Andrey Uzunov
 */

#include "platform.h"
#include "structures.h"
#include "internal.h"
#include "session.h"
//TODO not for here?
#include <ctype.h>


int
SPDYF_name_value_is_empty(struct SPDY_NameValue *container)
{
  SPDYF_ASSERT(NULL != container, "NULL is not an empty container!");
  return (NULL == container->name && NULL == container->value) ? SPDY_YES : SPDY_NO;
}

struct SPDY_NameValue *
SPDY_name_value_create ()
{
	struct SPDY_NameValue *pair;

	if(NULL == (pair = malloc(sizeof(struct SPDY_NameValue))))
		return NULL;

	memset (pair, 0, sizeof (struct SPDY_NameValue));

	return pair;
}


int
SPDY_name_value_add (struct SPDY_NameValue *container,
					const char *name,
					const char *value)
{
	unsigned int i;
	unsigned int len;
	struct SPDY_NameValue *pair;
	struct SPDY_NameValue *temp;
	char **temp_value;
	char *temp_string;

	if(NULL == container || NULL == name || NULL == value || 0 == (len = strlen(name)))
		return SPDY_INPUT_ERROR;
  //TODO there is old code handling value==NULL
  //update it to handle strlen(value)==0

	for(i=0; i<len; ++i)
	{
	  if(isupper((int) name[i]))
			return SPDY_INPUT_ERROR;
	}

	if(SPDYF_name_value_is_empty(container))
	{
		//container is empty/just created
		if (NULL == (container->name = strdup (name)))
		{
			return SPDY_NO;
		}
		if (NULL == (container->value = malloc(sizeof(char *))))
		{
			free(container->name);
			return SPDY_NO;
		}
    /*if(NULL == value)
      container->value[0] = NULL;
		else */if (NULL == (container->value[0] = strdup (value)))
		{
			free(container->value);
			free(container->name);
			return SPDY_NO;
		}
		container->num_values = 1;
		return SPDY_YES;
	}

	pair = container;
	while(NULL != pair)
	{
		if(0 == strcmp(pair->name, name))
		{
			//the value will be added to this pair
			break;
		}
		pair = pair->next;
	}

	if(NULL == pair)
	{
		//the name doesn't exist in container, add new pair
		if(NULL == (pair = malloc(sizeof(struct SPDY_NameValue))))
			return SPDY_NO;

		memset(pair, 0, sizeof(struct SPDY_NameValue));

		if (NULL == (pair->name = strdup (name)))
		{
			free(pair);
			return SPDY_NO;
		}
		if (NULL == (pair->value = malloc(sizeof(char *))))
		{
			free(pair->name);
			free(pair);
			return SPDY_NO;
		}
    /*if(NULL == value)
      pair->value[0] = NULL;
		else */if (NULL == (pair->value[0] = strdup (value)))
		{
			free(pair->value);
			free(pair->name);
			free(pair);
			return SPDY_NO;
		}
		pair->num_values = 1;

		temp = container;
		while(NULL != temp->next)
			temp = temp->next;
		temp->next = pair;
		pair->prev = temp;

		return SPDY_YES;
	}

	//check for duplication (case sensitive)
	for(i=0; i<pair->num_values; ++i)
		if(0 == strcmp(pair->value[i], value))
			return SPDY_NO;

	if(strlen(pair->value[0]) > 0)
	{
		//the value will be appended to the others for this name
		if (NULL == (temp_value = malloc((pair->num_values + 1) * sizeof(char *))))
		{
			return SPDY_NO;
		}
		memcpy(temp_value, pair->value, pair->num_values * sizeof(char *));
		if (NULL == (temp_value[pair->num_values] = strdup (value)))
		{
			free(temp_value);
			return SPDY_NO;
		}
		free(pair->value);
		pair->value = temp_value;
		++pair->num_values;
		return SPDY_YES;
	}

	//just replace the empty value

	if (NULL == (temp_string = strdup (value)))
	{
		return SPDY_NO;
	}
	free(pair->value[0]);
	pair->value[0] = temp_string;

	return SPDY_YES;
}


const char * const *
SPDY_name_value_lookup (struct SPDY_NameValue *container,
						const char *name,
						int *num_values)
{
	struct SPDY_NameValue *temp = container;

	if(NULL == container || NULL == name || NULL == num_values)
		return NULL;
	if(SPDYF_name_value_is_empty(container))
		return NULL;

	do
	{
		if(strcmp(name, temp->name) == 0)
		{
			*num_values = temp->num_values;
			return (const char * const *)temp->value;
		}

		temp = temp->next;
	}
	while(NULL != temp);

	return NULL;
}


void
SPDY_name_value_destroy (struct SPDY_NameValue *container)
{
	unsigned int i;
	struct SPDY_NameValue *temp = container;

	while(NULL != temp)
	{
		container = container->next;
		free(temp->name);
		for(i=0; i<temp->num_values; ++i)
			free(temp->value[i]);
		free(temp->value);
		free(temp);
		temp=container;
	}
}


int
SPDY_name_value_iterate (struct SPDY_NameValue *container,
                           SPDY_NameValueIterator iterator,
                           void *iterator_cls)
{
	int count;
	int ret;
	struct SPDY_NameValue *temp = container;

	if(NULL == container)
		return SPDY_INPUT_ERROR;

	//check if container is an empty struct
	if(SPDYF_name_value_is_empty(container))
		return 0;

	count = 0;

	if(NULL == iterator)
	{
		do
		{
			++count;
			temp=temp->next;
		}
		while(NULL != temp);

		return count;
	}

	//code duplication for avoiding if here
	do
	{
		++count;
		ret = iterator(iterator_cls, temp->name, (const char * const *)temp->value, temp->num_values);
		temp=temp->next;
	}
	while(NULL != temp && SPDY_YES == ret);

	return count;
}

void
SPDY_destroy_response(struct SPDY_Response *response)
{
  if(NULL == response)
    return;
	free(response->data);
	free(response->headers);
	free(response);
}


struct SPDYF_Response_Queue *
SPDYF_response_queue_create(bool is_data,
						void *data,
						size_t data_size,
						struct SPDY_Response *response,
						struct SPDYF_Stream *stream,
						bool closestream,
						SPDYF_ResponseQueueResultCallback frqcb,
						void *frqcb_cls,
						SPDY_ResponseResultCallback rrcb,
						void *rrcb_cls)
{
	struct SPDYF_Response_Queue *head = NULL;
	struct SPDYF_Response_Queue *prev;
	struct SPDYF_Response_Queue *response_to_queue;
	struct SPDYF_Control_Frame *control_frame;
	struct SPDYF_Data_Frame *data_frame;
	unsigned int i;
	bool is_last;

	SPDYF_ASSERT((! is_data)
		     || ((0 == data_size) && (NULL != response->rcb))
		     || ((0 < data_size) && (NULL == response->rcb)),
		     "either data or request->rcb must not be null");

	if (is_data && (data_size > SPDY_MAX_SUPPORTED_FRAME_SIZE))
	{
		//separate the data in more frames and add them to the queue

		prev=NULL;
		for(i = 0; i < data_size; i += SPDY_MAX_SUPPORTED_FRAME_SIZE)
		{
			is_last = (i + SPDY_MAX_SUPPORTED_FRAME_SIZE) >= data_size;

			if(NULL == (response_to_queue = malloc(sizeof(struct SPDYF_Response_Queue))))
				goto free_and_fail;

			memset(response_to_queue, 0, sizeof(struct SPDYF_Response_Queue));
			if(0 == i)
				head = response_to_queue;

			if(NULL == (data_frame = malloc(sizeof(struct SPDYF_Data_Frame))))
			{
				free(response_to_queue);
				goto free_and_fail;
			}
			memset(data_frame, 0, sizeof(struct SPDYF_Data_Frame));
			data_frame->control_bit = 0;
			data_frame->stream_id = stream->stream_id;
			if(is_last && closestream)
				data_frame->flags |= SPDY_DATA_FLAG_FIN;

			response_to_queue->data_frame = data_frame;
			response_to_queue->process_response_handler = &SPDYF_handler_write_data;
			response_to_queue->is_data = is_data;
			response_to_queue->stream = stream;
			if(is_last)
			{
				response_to_queue->frqcb = frqcb;
				response_to_queue->frqcb_cls = frqcb_cls;
				response_to_queue->rrcb = rrcb;
				response_to_queue->rrcb_cls = rrcb_cls;
			}
			response_to_queue->data = data + i;
			response_to_queue->data_size = is_last
				? (data_size - 1) % SPDY_MAX_SUPPORTED_FRAME_SIZE + 1
				: SPDY_MAX_SUPPORTED_FRAME_SIZE;
			response_to_queue->response = response;

			response_to_queue->prev = prev;
			if(NULL != prev)
				prev->next = response_to_queue;
			prev = response_to_queue;
		}

		return head;

		//for GOTO
		free_and_fail:
		while(NULL != head)
		{
			response_to_queue = head;
			head = head->next;
			free(response_to_queue->data_frame);
			free(response_to_queue);
		}
		return NULL;
	}

	//create only one frame for data, data with callback or control frame

	if(NULL == (response_to_queue = malloc(sizeof(struct SPDYF_Response_Queue))))
	{
		return NULL;
	}
	memset(response_to_queue, 0, sizeof(struct SPDYF_Response_Queue));

	if(is_data)
	{
		if(NULL == (data_frame = malloc(sizeof(struct SPDYF_Data_Frame))))
		{
			free(response_to_queue);
			return NULL;
		}
		memset(data_frame, 0, sizeof(struct SPDYF_Data_Frame));
		data_frame->control_bit = 0;
		data_frame->stream_id = stream->stream_id;
		if(closestream && NULL == response->rcb)
			data_frame->flags |= SPDY_DATA_FLAG_FIN;

		response_to_queue->data_frame = data_frame;
		response_to_queue->process_response_handler = &SPDYF_handler_write_data;
	}
	else
	{
		if(NULL == (control_frame = malloc(sizeof(struct SPDYF_Control_Frame))))
		{
			free(response_to_queue);
			return NULL;
		}
		memset(control_frame, 0, sizeof(struct SPDYF_Control_Frame));
		control_frame->control_bit = 1;
		control_frame->version = SPDY_VERSION;
		control_frame->type = SPDY_CONTROL_FRAME_TYPES_SYN_REPLY;
		if(closestream)
			control_frame->flags |= SPDY_SYN_REPLY_FLAG_FIN;

		response_to_queue->control_frame = control_frame;
		response_to_queue->process_response_handler = &SPDYF_handler_write_syn_reply;
	}

	response_to_queue->is_data = is_data;
	response_to_queue->stream = stream;
	response_to_queue->frqcb = frqcb;
	response_to_queue->frqcb_cls = frqcb_cls;
	response_to_queue->rrcb = rrcb;
	response_to_queue->rrcb_cls = rrcb_cls;
	response_to_queue->data = data;
	response_to_queue->data_size = data_size;
	response_to_queue->response = response;

	return response_to_queue;
}


void
SPDYF_response_queue_destroy(struct SPDYF_Response_Queue *response_queue)
{
	//data is not copied to the struct but only linked
	//but this is not valid for GOAWAY and RST_STREAM
	if(!response_queue->is_data
		&& (SPDY_CONTROL_FRAME_TYPES_RST_STREAM == response_queue->control_frame->type
		|| SPDY_CONTROL_FRAME_TYPES_GOAWAY == response_queue->control_frame->type))
	{
		free(response_queue->data);
	}
	if(response_queue->is_data)
		free(response_queue->data_frame);
	else
		free(response_queue->control_frame);

	free(response_queue);
}


/* Needed by testcase to be extern -- should this be
   in the header? */
_MHD_EXTERN ssize_t
SPDYF_name_value_to_stream(struct SPDY_NameValue * container[],
                           int num_containers,
                           void **stream)
{
	size_t size;
	int32_t num_pairs = 0;
	int32_t value_size;
	int32_t name_size;
	int32_t temp;
	unsigned int i;
	unsigned int offset;
	unsigned int value_offset;
	struct SPDY_NameValue * iterator;
	int j;

	size = 4; //for num pairs

	for(j=0; j<num_containers; ++j)
	{
    iterator = container[j];
    while(iterator != NULL)
    {
      ++num_pairs;
      size += 4 + strlen(iterator->name); //length + string

      SPDYF_ASSERT(iterator->num_values>0, "num_values is 0");

      size += 4; //value length

      for(i=0; i<iterator->num_values; ++i)
      {
        //if(NULL == iterator->value[i])
        //  continue;
        size += strlen(iterator->value[i]); // string
        if(i/* || !strlen(iterator->value[i])*/) ++size; //NULL separator
      }

      iterator = iterator->next;
    }
  }

	if(NULL == (*stream = malloc(size)))
	{
		return -1;
	}

	//put num_pairs to the stream
	num_pairs = htonl(num_pairs);
	memcpy(*stream, &num_pairs, 4);
	offset = 4;

	//put all other headers to the stream
	for(j=0; j<num_containers; ++j)
	{
    iterator = container[j];
    while(iterator != NULL)
    {
      name_size = strlen(iterator->name);
      temp = htonl(name_size);
      memcpy(*stream + offset, &temp, 4);
      offset += 4;
      strncpy(*stream + offset, iterator->name, name_size);
      offset += name_size;

      value_offset = offset;
      offset += 4;
      for(i=0; i<iterator->num_values; ++i)
      {
        if(i /*|| !strlen(iterator->value[0])*/)
        {
          memset(*stream + offset, 0, 1);
          ++offset;
          //if(!i) continue;
        }
        //else if(NULL != iterator->value[i])
        //{
          strncpy(*stream + offset, iterator->value[i], strlen(iterator->value[i]));
          offset += strlen(iterator->value[i]);
        //}
      }
      value_size = offset - value_offset - 4;
      value_size = htonl(value_size);
      memcpy(*stream + value_offset, &value_size, 4);

      iterator = iterator->next;
    }
  }

	SPDYF_ASSERT(offset == size,"offset is wrong");

	return size;
}


/* Needed by testcase to be extern -- should this be
   in the header? */
_MHD_EXTERN int
SPDYF_name_value_from_stream(void *stream,
							size_t size,
							struct SPDY_NameValue ** container)
{
	int32_t num_pairs;
	int32_t value_size;
	int32_t name_size;
	int i;
	unsigned int offset = 0;
	unsigned int value_end_offset;
	char *name;
	char *value;

	if(NULL == (*container = SPDY_name_value_create ()))
	{
		return SPDY_NO;
	}

	//get number of pairs
	memcpy(&num_pairs, stream, 4);
	offset = 4;
	num_pairs = ntohl(num_pairs);

	if(num_pairs > 0)
	{
		for(i = 0; i < num_pairs; ++i)
		{
			//get name size
			memcpy(&name_size, stream + offset, 4);
			offset += 4;
			name_size = ntohl(name_size);
			//get name
			if(NULL == (name = strndup(stream + offset, name_size)))
			{
				SPDY_name_value_destroy(*container);
				return SPDY_NO;
			}
			offset+=name_size;

			//get value size
			memcpy(&value_size, stream + offset, 4);
			offset += 4;
			value_size = ntohl(value_size);
			value_end_offset = offset + value_size;
			//get value
			do
			{
				if(NULL == (value = strndup(stream + offset, value_size)))
				{
					free(name);
					SPDY_name_value_destroy(*container);
					return SPDY_NO;
				}
				offset += strlen(value);
				if(offset < value_end_offset)
					++offset; //NULL separator

				//add name/value to the struct
				if(SPDY_YES != SPDY_name_value_add(*container, name, value))
				{
					free(name);
					free(value);
					SPDY_name_value_destroy(*container);
					return SPDY_NO;
				}
				free(value);
			}
			while(offset < value_end_offset);

			free(name);

			if(offset != value_end_offset)
			{
				SPDY_name_value_destroy(*container);
				return SPDY_INPUT_ERROR;
			}
		}
	}

	if(offset == size)
		return SPDY_YES;

	SPDY_name_value_destroy(*container);
	return SPDY_INPUT_ERROR;
}