/*
* Copyright (C) 2011 Mamadou Diop.
*
* Contact: Mamadou Diop <diopmamadou(at)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 tdav_speex_jitterbuffer.c
 * @brief Speex Audio jitterbuffer Plugin
 *
 * @author Mamadou Diop <diopmamadou(at)doubango.org>

 */
#include "tinydav/audio/tdav_speex_jitterbuffer.h"
#include "tinyrtp/rtp/trtp_rtp_header.h"

#if HAVE_SPEEX_DSP && HAVE_SPEEX_JB

#include "tinymedia/tmedia_defaults.h"

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

static int tdav_speex_jitterbuffer_set(tmedia_jitterbuffer_t *self, const tmedia_param_t* param)
{
	TSK_DEBUG_ERROR("Not implemented");
	return -2;
}

static int tdav_speex_jitterbuffer_open(tmedia_jitterbuffer_t* self, uint32_t frame_duration, uint32_t rate)
{
	tdav_speex_jitterbuffer_t *jitterbuffer = (tdav_speex_jitterbuffer_t *)self;
	spx_int32_t tmp;

	if(!(jitterbuffer->state = jitter_buffer_init((int)frame_duration))){
		TSK_DEBUG_ERROR("jitter_buffer_init() failed");
		return -2;
	}
	jitterbuffer->rate = rate;
	jitterbuffer->frame_duration = frame_duration;

	jitter_buffer_ctl(jitterbuffer->state, JITTER_BUFFER_GET_MARGIN, &tmp);
	TSK_DEBUG_INFO("Default Jitter buffer margin=%d", tmp);
	jitter_buffer_ctl(jitterbuffer->state, JITTER_BUFFER_GET_MAX_LATE_RATE, &tmp);
	TSK_DEBUG_INFO("Default Jitter max late rate=%d", tmp);
	
	if((tmp = tmedia_defaults_get_jb_margin()) >= 0){
		jitter_buffer_ctl(jitterbuffer->state, JITTER_BUFFER_SET_MARGIN, &tmp);
		TSK_DEBUG_INFO("New Jitter buffer margin=%d", tmp);
	}
	if((tmp = tmedia_defaults_get_jb_max_late_rate()) >= 0){
		jitter_buffer_ctl(jitterbuffer->state, JITTER_BUFFER_SET_MAX_LATE_RATE, &tmp);
		TSK_DEBUG_INFO("New Jitter buffer max late rate=%d", tmp);
	}

	return 0;
}

static int tdav_speex_jitterbuffer_tick(tmedia_jitterbuffer_t* self)
{
	tdav_speex_jitterbuffer_t *jitterbuffer = (tdav_speex_jitterbuffer_t *)self;
	if(!jitterbuffer->state){
		TSK_DEBUG_ERROR("Invalid state");
		return -1;
	}
	jitter_buffer_tick(jitterbuffer->state);
	return 0;
}

static int tdav_speex_jitterbuffer_put(tmedia_jitterbuffer_t* self, void* data, tsk_size_t data_size, const tsk_object_t* proto_hdr)
{
	tdav_speex_jitterbuffer_t *jitterbuffer = (tdav_speex_jitterbuffer_t *)self;
	const trtp_rtp_header_t* rtp_hdr;
	JitterBufferPacket jb_packet;
	if(!data || !data_size || !proto_hdr){
		TSK_DEBUG_ERROR("Invalid parameter");
		return -1;
	}
	
	if(!jitterbuffer->state){
		TSK_DEBUG_ERROR("Invalid state");
		return -2;
	}
	
	rtp_hdr = TRTP_RTP_HEADER(proto_hdr);
	
	jb_packet.data = data;
	jb_packet.len = data_size;
	jb_packet.span = (data_size * 500)/jitterbuffer->rate;
	jb_packet.timestamp = (rtp_hdr->seq_num * jb_packet.span);
	
	jb_packet.sequence = rtp_hdr->seq_num;
	jb_packet.user_data = 0;
	jitter_buffer_put(jitterbuffer->state, &jb_packet);
	
	return 0;
}

static tsk_size_t tdav_speex_jitterbuffer_get(tmedia_jitterbuffer_t* self, void* out_data, tsk_size_t out_size)
{
	tdav_speex_jitterbuffer_t *jitterbuffer = (tdav_speex_jitterbuffer_t *)self;
	JitterBufferPacket jb_packet;
	int ret;

	if(!out_data || !out_size){
		TSK_DEBUG_ERROR("Invalid parameter");
		return 0;
	}
	if(!jitterbuffer->state){
		TSK_DEBUG_ERROR("Invalid state");
		return 0;
	}

	jb_packet.data = out_data;
	jb_packet.len = out_size;

	if ((ret = jitter_buffer_get(jitterbuffer->state, &jb_packet, (out_size*500)/jitterbuffer->rate, tsk_null)) != JITTER_BUFFER_OK) {
		switch(ret){
			case JITTER_BUFFER_MISSING: /*TSK_DEBUG_INFO("JITTER_BUFFER_MISSING - %d", ret);*/ break;
			case JITTER_BUFFER_INSERTION: /*TSK_DEBUG_INFO("JITTER_BUFFER_INSERTION - %d", ret);*/ break;
			default: TSK_DEBUG_INFO("jitter_buffer_get() failed - %d", ret);
		}
		jitter_buffer_update_delay(jitterbuffer->state, &jb_packet, NULL);
		return 0;
	}
	// jitter_buffer_update_delay(jitterbuffer->state, &jb_packet, NULL);

	return out_size;
}

static int tdav_speex_jitterbuffer_reset(tmedia_jitterbuffer_t* self)
{
	tdav_speex_jitterbuffer_t *jitterbuffer = (tdav_speex_jitterbuffer_t *)self;
	if(jitterbuffer->state){
		jitter_buffer_reset(jitterbuffer->state);
	}
	return 0;
}

static int tdav_speex_jitterbuffer_close(tmedia_jitterbuffer_t* self)
{
	tdav_speex_jitterbuffer_t *jitterbuffer = (tdav_speex_jitterbuffer_t *)self;
	if(jitterbuffer->state){
		jitter_buffer_destroy(jitterbuffer->state);
		jitterbuffer->state = tsk_null;
	}
	return 0;
}



//
//	Speex jitterbufferr Plugin definition
//

/* constructor */
static tsk_object_t* tdav_speex_jitterbuffer_ctor(tsk_object_t * self, va_list * app)
{
	tdav_speex_jitterbuffer_t *jitterbuffer = self;
	TSK_DEBUG_INFO("Create SpeexDSP jitter buffer");
	if(jitterbuffer){
		/* init base */
		tmedia_jitterbuffer_init(TMEDIA_JITTER_BUFFER(jitterbuffer));
		/* init self */
	}
	return self;
}
/* destructor */
static tsk_object_t* tdav_speex_jitterbuffer_dtor(tsk_object_t * self)
{ 
	tdav_speex_jitterbuffer_t *jitterbuffer = self;
	if(jitterbuffer){
		/* deinit base */
		tmedia_jitterbuffer_deinit(TMEDIA_JITTER_BUFFER(jitterbuffer));
		/* deinit self */
		if(jitterbuffer->state){
			jitter_buffer_destroy(jitterbuffer->state);
			jitterbuffer->state = tsk_null;
		}
	}

	return self;
}
/* object definition */
static const tsk_object_def_t tdav_speex_jitterbuffer_def_s = 
{
	sizeof(tdav_speex_jitterbuffer_t),
	tdav_speex_jitterbuffer_ctor, 
	tdav_speex_jitterbuffer_dtor,
	tsk_null, 
};
/* plugin definition*/
static const tmedia_jitterbuffer_plugin_def_t tdav_speex_jitterbuffer_plugin_def_s = 
{
	&tdav_speex_jitterbuffer_def_s,
	tmedia_audio,
	"Audio JitterBuffer based on Speex",
	
	tdav_speex_jitterbuffer_set,
	tdav_speex_jitterbuffer_open,
	tdav_speex_jitterbuffer_tick,
	tdav_speex_jitterbuffer_put,
	tdav_speex_jitterbuffer_get,
	tdav_speex_jitterbuffer_reset,
	tdav_speex_jitterbuffer_close,
};
const tmedia_jitterbuffer_plugin_def_t *tdav_speex_jitterbuffer_plugin_def_t = &tdav_speex_jitterbuffer_plugin_def_s;


#endif /* HAVE_SPEEX_DSP */
