#if HAVE_CRT
#define _CRTDBG_MAP_ALLOC 
#include <stdlib.h> 
#include <crtdbg.h>
#endif //HAVE_CRT
/*

*  Copyright (C) 2020, University of the Basque Country (UPV/EHU)
*
* Contact for licensing options: <licensing-mcpttclient(at)mcopenplatform(dot)com>
*
* This file is part of MCOP MCPTT Client
*
* This 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 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, write to the Free Software Foundation, Inc.,
* 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
*/


/**@file tmedia_content_multipart.c
 * @brief Base multipart content object.
 */
#include "tinymedia/content/tmedia_content_multipart.h"

#include "tsk_memory.h"
#include "tsk_string.h"
#include "tsk_debug.h"
#include "tsk_uuid.h"

tmedia_content_multipart_t* tmedia_content_multipart_create(const char* data, tsk_size_t data_size, const char* content_type, const char* content_disposition)
{
	tmedia_content_multipart_t* content = tsk_null;
	if(!(content = tsk_object_new(tmedia_content_multipart_def_t))){
		return tsk_null;
	}

	if(data == tsk_null || data_size == 0)
	{
		TSK_OBJECT_SAFE_FREE(content);
		return tsk_null;
	}
	#if HAVE_CRT //Debug memory
	content->data = (char*)calloc(data_size, sizeof(char));
		
	#else
	content->data = (char*)tsk_calloc(data_size, sizeof(char));
		
	#endif //HAVE_CRT
	memcpy(content->data, data, data_size);
	content->data_size = data_size;

	if(content_type != tsk_null)
		content->content_type = tsk_strdup(content_type);
	else
		content->content_type = tsk_null;

	if(content_disposition != tsk_null)
		content->content_disposition = tsk_strdup(content_disposition);
	else
		content->content_disposition = tsk_null;

	return content;
}

tmedia_content_multipart_t* tmedia_content_multipart_parse(const void* data, tsk_size_t size)
{
	//TO-DO
	tmedia_content_multipart_t* mp_content = tsk_null;
	const char* content_type_hdr = "Content-Type: ";
	char* content_type_value = tsk_null;
	const char* content_disp_hdr = "Content-Disposition: ";
	const char* CRLF = "\r\n";
	const char* DBL_CRLF = "\r\n\r\n";
	char* content_disp_value = tsk_null;
	int start_index = 0, stop_index = 0, value_size = 0;
	char* content_buffer = (char*)data;
	
	start_index = tsk_strindexOf(content_buffer, size, content_type_hdr);
	if(start_index >= 0)
	{
		start_index += strlen(content_type_hdr);
		value_size = tsk_strindexOf(&content_buffer[start_index], size - start_index, CRLF);
		if(value_size > 0)
		{
			#if HAVE_CRT //Debug memory
				content_type_value = (char*)calloc((value_size + 1), sizeof(char));
	
	#else
				content_type_value = (char*)tsk_calloc((value_size + 1), sizeof(char));
	
	#endif //HAVE_CRT
			memcpy(content_type_value, &content_buffer[start_index], value_size);
			content_type_value[value_size] = '\0';
		}
	}

	start_index = tsk_strindexOf(content_buffer, size, content_disp_hdr);
	if(start_index >= 0)
	{
		start_index += strlen(content_disp_hdr);
		value_size = tsk_strindexOf(&content_buffer[start_index], size - start_index, CRLF);
		if(value_size > 0)
		{
			#if HAVE_CRT //Debug memory
					content_disp_value = (char*)calloc((value_size + 1), sizeof(char));

	#else
					content_disp_value = (char*)tsk_calloc((value_size + 1), sizeof(char));

	#endif //HAVE_CRT
			memcpy(content_disp_value, &content_buffer[start_index], value_size);
			content_disp_value[value_size] = '\0';
		}
	}

	start_index = tsk_strindexOf(content_buffer, size, DBL_CRLF);

	if(start_index < 0)
	{
		TSK_FREE(content_type_value);
	    TSK_FREE(content_disp_value);
		return tsk_null;
	}
	
	start_index += strlen(DBL_CRLF);

	//If there is a final double CRLF, delete one
	stop_index = tsk_strindexOf(&content_buffer[start_index], size - start_index, DBL_CRLF);
	if(stop_index > 0)
		stop_index += start_index + 2;
	else
		stop_index = size;

	mp_content = tmedia_content_multipart_create(&content_buffer[start_index], stop_index - start_index, content_type_value, content_disp_value);
	
	TSK_FREE(content_type_value);
	TSK_FREE(content_disp_value);
	return mp_content;
}

char* tmedia_content_multipart_tostring(tmedia_content_multipart_t* self)
{
	char* string = tsk_null;

	if(!self) {
		return tsk_null;
	}

	if(self->content_type && !tsk_strempty(self->content_type))
		tsk_sprintf(&string, "Content-Type: %s", self->content_type);

	if(self->content_disposition && !tsk_strempty(self->content_disposition))
		tsk_strcat_2(&string, "\r\nContent-Disposition: %s", self->content_disposition);

	tsk_strcat_2(&string, "\r\n\r\n%.*s", self->data_size, self->data);

	return string;
}


int tmedia_content_multipart_init(tmedia_content_multipart_t* self)
{
	if (!self){
		TSK_DEBUG_ERROR("Invalid parameter");
		return -1;
	}

	return 0;
}

int tmedia_content_multipart_deinit(tmedia_content_multipart_t* self)
{
	if (!self){
		TSK_DEBUG_ERROR("Invalid parameter");
		return -1;
	}

	TSK_FREE(self->data);
	self->data_size = 0;
	TSK_FREE(self->content_type);
	TSK_FREE(self->content_disposition);

	return 0;
}

/* constructor */
static tsk_object_t* tmedia_content_multipart_ctor(tsk_object_t * self, va_list * app)
{
	tmedia_content_multipart_t *content = self;
	if (content){
	}
	return self;
}
/* destructor */
static tsk_object_t* tmedia_content_multipart_dtor(tsk_object_t * self)
{
	tmedia_content_multipart_t *content = self;
	if (content){
		tmedia_content_multipart_deinit(content);
	}

	return self;
}

static const tsk_object_def_t tmedia_content_multipart_def_s =
{
	sizeof(tmedia_content_multipart_t),
	tmedia_content_multipart_ctor,
	tmedia_content_multipart_dtor,
	tsk_null,
};
const tsk_object_def_t *tmedia_content_multipart_def_t = &tmedia_content_multipart_def_s;


tmedia_multipart_body_t* tmedia_content_multipart_body_create(const char* multipart_type, const char* boundary)
{
	tmedia_multipart_body_t* content = tsk_null;
	if(!(content = tsk_object_new(tmedia_multipart_body_def_t))){
		return tsk_null;
	}

	content->multipart_type = tsk_strdup(multipart_type);
	if(boundary)
		content->boundary = tsk_strdup(boundary);
	else
	{
		tsk_uuidstring_t uuid;
		tsk_uuidgenerate(&uuid);
		tsk_strupdate(&(content->boundary), uuid);
	}
	content->contents = tsk_list_create();

	return content;
}

tmedia_multipart_body_t* tmedia_content_multipart_body_parse(const void* data, tsk_size_t sizeconst, char* multipart_type, const char* boundary)
{
	//TO-DO
	tmedia_multipart_body_t* mp_body = tsk_null;
	tmedia_content_multipart_t* mp_content = tsk_null;
	const char* body_buffer = (char*)data;
	char* content_buffer = tsk_null;
	int start_index = 0, stop_index = 0, read_index = 0, final_index = 0;
	tsk_size_t boundary_size = 0, delimited_boundary_size = 0, delimited_final_boundary_size = 0, content_buffer_size = 0;
	char* delimited_boundary = tsk_null;
	char* delimited_final_boundary = tsk_null;

	if(data == tsk_null || sizeconst == 0 || multipart_type == tsk_null || boundary == tsk_null)
		return tsk_null;
	
	boundary_size = tsk_strlen(boundary);
	delimited_boundary_size = boundary_size + 3;
	#if HAVE_CRT //Debug memory
	delimited_boundary = (char*)calloc(delimited_boundary_size, sizeof(char));
		
	#else
	delimited_boundary = (char*)tsk_calloc(delimited_boundary_size, sizeof(char));
		
	#endif //HAVE_CRT
	tsk_sprintf(&delimited_boundary, "--%s", boundary);
	delimited_boundary[delimited_boundary_size - 1] = '\0';
	delimited_final_boundary_size = boundary_size + 5;
	#if HAVE_CRT //Debug memory
		delimited_final_boundary = (char*)calloc(delimited_final_boundary_size, sizeof(char));
	
	#else
		delimited_final_boundary = (char*)tsk_calloc(delimited_final_boundary_size, sizeof(char));
	
	#endif //HAVE_CRT
	tsk_sprintf(&delimited_final_boundary, "--%s--", boundary);
	delimited_final_boundary[delimited_final_boundary_size - 1] = '\0';

	final_index = tsk_strLastIndexOf(body_buffer, sizeconst, delimited_final_boundary);
	if(final_index <= 0)
	{
		TSK_DEBUG_ERROR("Error trying to get the end of the message");
		TSK_FREE(delimited_boundary);
		TSK_FREE(delimited_final_boundary);
		return tsk_null;
	}

	while(read_index < final_index)
	{
		start_index = tsk_strindexOf(&body_buffer[read_index], sizeconst - read_index, delimited_boundary);
		if(start_index < 0)
			break;
		start_index += read_index + (delimited_boundary_size - 1) + 2; //CRLF
		stop_index = tsk_strindexOf(&body_buffer[start_index], sizeconst - start_index, delimited_boundary);
		if(stop_index < 0) 
			break;
		stop_index += start_index;
		if(stop_index > final_index)
			break;
		content_buffer_size = stop_index - start_index;
		#if HAVE_CRT //Debug memory
		content_buffer = (char*)calloc(content_buffer_size, sizeof(char));
		
	#else
		content_buffer = (char*)tsk_calloc(content_buffer_size, sizeof(char));
		
	#endif //HAVE_CRT
		memcpy(content_buffer, &body_buffer[start_index], content_buffer_size);
		mp_content = tmedia_content_multipart_parse(content_buffer, content_buffer_size);
		if(mp_content != tsk_null)
		{
			if(mp_body == tsk_null)
				mp_body = tmedia_content_multipart_body_create(multipart_type, boundary);

			tsk_list_push_back_data(mp_body->contents, (void**)&mp_content);
		}
		read_index = stop_index;
		TSK_FREE(content_buffer);
		content_buffer = tsk_null;
		content_buffer_size = 0;
	}
	
	TSK_FREE(delimited_boundary);
	TSK_FREE(delimited_final_boundary);
	return mp_body;
}

char* tmedia_content_multipart_body_tostring(tmedia_multipart_body_t* self)
{
	char* string = tsk_null;
	tmedia_content_multipart_t *content;
	const tsk_list_item_t* item;

	if(!self || !self->boundary) {
		return tsk_null;
	}

	tsk_list_foreach(item, self->contents){
		char* content_string = tsk_null;
		content = (tmedia_content_multipart_t*)item->data;
		content_string = tmedia_content_multipart_tostring(content);
		if(content_string)
			tsk_strcat_2(&string, "--%s\r\n%s\r\n", self->boundary, content_string);
	}

	tsk_strcat_2(&string, "--%s--\r\n", self->boundary);

	return string;
}


int tmedia_content_multipart_body_init(tmedia_multipart_body_t* self)
{
	if (!self){
		TSK_DEBUG_ERROR("Invalid parameter");
		return -1;
	}

	return 0;
}

char* tmedia_content_multipart_body_get_header(tmedia_multipart_body_t* self)
{
	char* content_type_hdr = tsk_null;

	if(!self){
		return tsk_null;
	}

	tsk_sprintf(&content_type_hdr, "%s;boundary=%s", self->multipart_type, self->boundary);//change  "%s;boundary=\"%s\"" to "%s;boundary=%s"

    return content_type_hdr;
}

int tmedia_content_multipart_body_add_content(tmedia_multipart_body_t* self, tmedia_content_multipart_t* content)
{
	if(!self || !content)
		return -1;

	if(!self->contents)
		self->contents = tsk_list_create();

	tsk_list_push_back_data(self->contents, (void**)&content);

	return 0;
}

tmedia_content_multipart_t* tmedia_content_multipart_body_get_content(tmedia_multipart_body_t* self, const char* content_type)
{
	tmedia_content_multipart_t *content = tsk_null;
	tmedia_content_multipart_t *ret = tsk_null;
	const tsk_list_item_t* item;

	if(!self || !self->contents) {
		return tsk_null;
	}

	tsk_list_foreach(item, self->contents)
	{
        content = (tmedia_content_multipart_t*)item->data;
        if(memcmp(content->content_type, content_type, strlen(content->content_type)) == 0)
		{
			ret = content;
			break;
		}
	}

	return ret;
}

int tmedia_content_multipart_body_deinit(tmedia_multipart_body_t* self)
{
	if (!self){
		TSK_DEBUG_ERROR("Invalid parameter");
		return -1;
	}

	TSK_FREE(self->multipart_type);
	TSK_FREE(self->boundary);
	TSK_FREE(self->contents);

	return 0;
}

/* constructor */
static tsk_object_t* tmedia_content_multipart_body_ctor(tsk_object_t * self, va_list * app)
{
	tmedia_multipart_body_t *content = self;
	if (content){
	}
	return self;
}
/* destructor */
static tsk_object_t* tmedia_content_multipart_body_dtor(tsk_object_t * self)
{
	tmedia_multipart_body_t *content = self;
	if (content){
		tmedia_content_multipart_body_deinit(content);
	}

	return self;
}

static const tsk_object_def_t tmedia_multipart_body_def_s =
{
	sizeof(tmedia_multipart_body_t),
	tmedia_content_multipart_body_ctor,
	tmedia_content_multipart_body_dtor,
	tsk_null,
};
const tsk_object_def_t *tmedia_multipart_body_def_t = &tmedia_multipart_body_def_s;