#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>
*
* The original file was part of Open Source Doubango Framework
* Copyright (C) 2010-2011 Mamadou Diop.
* Copyright (C) 2012 Doubango Telecom <http://doubango.org>
*
* This file is part of Open Source Doubango Framework.
*
* DOUBANGO 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.
*
* DOUBANGO 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 DOUBANGO.
*
*/
/**@file trtp_rtp_packet.c
 * @brief RTP packet.
 *
 * @author Mamadou Diop <diopmamadou(at)doubango.org>
 *

 */
#include "tinyrtp/rtp/trtp_rtp_packet.h"

#include "tnet_endianness.h"

#include "tsk_memory.h"
#include "tsk_debug.h"

#include <string.h> /* memcpy() */


/** Create new RTP packet */
trtp_rtp_packet_t* trtp_rtp_packet_create_null()
{
	return tsk_object_new(trtp_rtp_packet_def_t);
}

trtp_rtp_packet_t* trtp_rtp_packet_create(uint32_t ssrc, uint16_t seq_num, uint32_t timestamp, uint8_t payload_type, tsk_bool_t marker)
{
	trtp_rtp_packet_t* packet;
	if((packet = tsk_object_new(trtp_rtp_packet_def_t))){
		packet->header = trtp_rtp_header_create(ssrc, seq_num, timestamp, payload_type, marker);
	}
	return packet;
}

trtp_rtp_packet_t* trtp_rtp_packet_create_2(const trtp_rtp_header_t* header)
{
	trtp_rtp_packet_t* packet;

	if(!header){
		TSK_DEBUG_ERROR("Invalid parameter");
		return tsk_null;
	}
	if((packet = tsk_object_new(trtp_rtp_packet_def_t))){
		packet->header = tsk_object_ref(TSK_OBJECT(header));
	}
	return packet;
}

/* guess what is the minimum required size to serialize the packet */
tsk_size_t trtp_rtp_packet_guess_serialbuff_size(const trtp_rtp_packet_t *self)
{	
	tsk_size_t size = 0;
	if(!self){
		TSK_DEBUG_ERROR("Invalid parameter");
		return 0;
	}
	size += trtp_rtp_header_guess_serialbuff_size(self->header);
	if(self->extension.data && self->extension.size && self->header->extension){
		size += self->extension.size;
	}
	size += self->payload.size;
	return size;
}

/* serialize the RTP packet to a buffer */
// the buffer size must be at least equal to "trtp_rtp_packet_guess_serialbuff_size()"
// returns the number of written bytes
tsk_size_t trtp_rtp_packet_serialize_to(const trtp_rtp_packet_t *self, void* buffer, tsk_size_t size)
{
	tsk_size_t ret;
	tsk_size_t s;
	uint8_t* pbuff = (uint8_t*)buffer;

	if(!buffer || (size < (ret = trtp_rtp_packet_guess_serialbuff_size(self)))){
		TSK_DEBUG_ERROR("Invalid parameter");
		return 0;
	}

	s = trtp_rtp_header_serialize_to(self->header, pbuff, size);
	pbuff += s;

	/* extension */
	if(self->extension.data && self->extension.size && self->header->extension){
		memcpy(pbuff, self->extension.data, self->extension.size);
		pbuff += self->extension.size;
	}
	/* append payload */
	memcpy(pbuff, self->payload.data_const ? self->payload.data_const : self->payload.data, self->payload.size);

	return ret;
}

/** Serialize rtp packet object into binary buffer */
// num_bytes_pad: number of bytes to add to the buffer. Useful to have the packet byte aligned or to prepare for SRTP protection
// the padding bytes will not be added to the final buffer size
tsk_buffer_t* trtp_rtp_packet_serialize(const trtp_rtp_packet_t *self, tsk_size_t num_bytes_pad)
{
	tsk_buffer_t* buffer = tsk_null;
	tsk_size_t size;

	if(!self || !self->header){
		TSK_DEBUG_ERROR("Invalid parameter");
		return tsk_null;
	}

	size = (trtp_rtp_packet_guess_serialbuff_size(self) + num_bytes_pad);
	if(size & 0x03) size += (4 - (size & 0x03));
	
	if(!(buffer = tsk_buffer_create(tsk_null, size))){
		TSK_DEBUG_ERROR("Failed to create buffer with size = %u", size);
		return tsk_null;
	}
	// shorten the buffer to hide the padding
	buffer->size = trtp_rtp_packet_serialize_to(self, buffer->data, buffer->size);
	return buffer;
}

/** Deserialize rtp packet object from binary buffer */
trtp_rtp_packet_t* trtp_rtp_packet_deserialize(const void *data, tsk_size_t size)
{
	trtp_rtp_packet_t* packet = tsk_null;
	trtp_rtp_header_t *header;
	tsk_size_t payload_size;
	const uint8_t* pdata = data;
	int bytes_nseq=0;

	if(!data){
		TSK_DEBUG_ERROR("Invalid parameter");
		return tsk_null;
	}

	if(size< TRTP_RTP_HEADER_MIN_SIZE){
		TSK_DEBUG_ERROR("Too short to contain RTP message");
		return tsk_null;
	}
	
	/* deserialize the RTP header (the packet itsel will be deserialized only if the header deserialization succeed) */
	if(!(header = trtp_rtp_header_deserialize(data, size))){
		TSK_DEBUG_ERROR("Failed to deserialize RTP header");
		return tsk_null;
	}
	else{
		/* create the packet */
		if(!(packet = trtp_rtp_packet_create_null())){
			TSK_DEBUG_ERROR("Failed to create new RTP packet");
			TSK_OBJECT_SAFE_FREE(header);
			return tsk_null;
		}
		/* set the header */
		packet->header = header,
			header = tsk_null;
		
		/* do not need to check overflow (have been done by trtp_rtp_header_deserialize()) */
		payload_size = (size - TRTP_RTP_HEADER_MIN_SIZE - (packet->header->csrc_count << 2));
		pdata = ((const uint8_t*)data) + (size - payload_size);

		/*	RFC 3550 - 5.3.1 RTP Header Extension
			If the X bit in the RTP header is one, a variable-length header
			extension MUST be appended to the RTP header, following the CSRC list
			if present.  The header extension contains a 16-bit length field that
			counts the number of 32-bit words in the extension, excluding the
			four-octet extension header (therefore zero is a valid length).  Only
			a single extension can be appended to the RTP data header.
			0                   1                   2                   3
			0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
		   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
		   |      defined by profile       |           length              |
		   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
		   |                        header extension                       |
		   |                             ....                              |
		*/
		if(packet->header->extension && payload_size>=4 /* extension min-size */){
			packet->extension.size = 4 /* first two 16-bit fields */ + (tnet_ntohs(*((uint16_t*)&pdata[2])) << 2/*words(32-bit)*/);
			#if HAVE_CRT //Debug memory
			if((packet->extension.data = calloc(packet->extension.size, sizeof(uint8_t)))){
		
	#else
			if((packet->extension.data = tsk_calloc(packet->extension.size, sizeof(uint8_t)))){
		
	#endif //HAVE_CRT
				memcpy(packet->extension.data, pdata, packet->extension.size);
			}
			payload_size -= packet->extension.size;
		}

		packet->payload.size = payload_size;
		#if HAVE_CRT //Debug memory
		if(payload_size && (packet->payload.data = calloc(packet->payload.size, sizeof(uint8_t)))){
		
	#else
		if(payload_size && (packet->payload.data = tsk_calloc(packet->payload.size+bytes_nseq, sizeof(uint8_t)))){
		
	#endif //HAVE_CRT
			memcpy(packet->payload.data, (pdata + packet->extension.size), packet->payload.size);

		}
		else{
			TSK_DEBUG_ERROR("Failed to allocate new buffer");
			packet->payload.size = 0;
		}
	}

	return packet;
}









//=================================================================================================
//	RTP packet object definition
//
static tsk_object_t* trtp_rtp_packet_ctor(tsk_object_t * self, va_list * app)
{
	trtp_rtp_packet_t *packet = self;
	if(packet){
	}
	return self;
}
static tsk_object_t* trtp_rtp_packet_dtor(tsk_object_t * self)
{ 
	trtp_rtp_packet_t *packet = self;
	if(packet){
		TSK_OBJECT_SAFE_FREE(packet->header);
		TSK_FREE(packet->payload.data);
		TSK_FREE(packet->extension.data);
		packet->payload.data_const = tsk_null;
	}

	return self;
}
// comparison must be by sequence number because of the jb
static int trtp_rtp_packet_cmp(const tsk_object_t *_p1, const tsk_object_t *_p2)
{
	const trtp_rtp_packet_t *p1 = _p1;
	const trtp_rtp_packet_t *p2 = _p2;

	if(p1 && p1->header && p2 && p2->header){
		return (int)(p1->header->seq_num - p2->header->seq_num);
	}
	else if(!p1 && !p2) return 0;
	else return -1;
}

static const tsk_object_def_t trtp_rtp_packet_def_s = 
{
	sizeof(trtp_rtp_packet_t),
	trtp_rtp_packet_ctor, 
	trtp_rtp_packet_dtor,
	trtp_rtp_packet_cmp, 
};
const tsk_object_def_t *trtp_rtp_packet_def_t = &trtp_rtp_packet_def_s;
