/*
 *			GPAC - MPEG-4 Systems C Development Kit
 *
 *			Copyright (c) Jean Le Feuvre 2000-2005 
 *					All rights reserved
 *
 *  This file is part of GPAC / AMR decoder plugin
 *
 *  GPAC 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 2, or (at your option)
 *  any later version.
 *   
 *  GPAC 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 GNU Make; see the file COPYING.  If not, write to
 *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. 
 *
 */



/*decoder Interface*/
#include <gpac/m4_decoder.h>
#include <gpac/m4_network.h>
#include <gpac/m4_author.h>

#include "AMR_NB/sp_dec.h"
#include "AMR_NB/d_homing.h"


/*default size in CU of composition memory for audio*/
#define DEFAULT_AUDIO_CM_SIZE			12
/*default critical size in CU of composition memory for audio*/
#define DEFAULT_AUDIO_CM_TRIGGER		4

typedef struct
{
	u32 sample_rate, out_size, num_samples;
	u8 num_channels;
	/*no support for scalability in AMR*/
	u16 ES_ID;
	u32 cb_size, cb_trig;

	u32 frame_count;

	/*AMR NB state vars*/
	Speech_Decode_FrameState * speech_decoder_state;
    enum RXFrameType rx_type;
    enum Mode mode;
   
	s16 reset_flag, reset_flag_old;

} AMRDec;

#define AMRCTX() AMRDec *ctx = (AMRDec *) ifcg->privateStack


static M4Err AMR_AttachStream(BaseDecoder *ifcg, u16 ES_ID, unsigned char *decSpecInfo, u32 decSpecInfoSize, u16 DependsOnES_ID, u32 objectTypeIndication, Bool UpStream)
{
	BitStream *bs;
	char name[5];
	u32 codec_4cc;
	AMRCTX();
	if (ctx->ES_ID && ctx->ES_ID!=ES_ID) return M4NotSupported;

	bs = NewBitStream(decSpecInfo, decSpecInfoSize, BS_READ);
	codec_4cc = BS_ReadU32(bs);
	DeleteBitStream(bs);
	FCC_TO_STR(codec_4cc, name);
	
	if (stricmp(name, "samr") && stricmp(name, "amr ")) return M4NotSupported;

	ctx->frame_count = 0;
	ctx->reset_flag = 0;
	ctx->reset_flag_old = 1;
	ctx->mode = 0;
	ctx->rx_type = 0;
	ctx->speech_decoder_state = NULL;
	if (Speech_Decode_Frame_init(&ctx->speech_decoder_state, "Decoder")) return M4IOErr;
	ctx->num_channels = 1;
	ctx->num_samples = 160;
	ctx->sample_rate = 8000;
	
	/*max possible frames in a sample are seen in MP4, that's 15*/
	ctx->out_size = 15 * 2 * ctx->num_samples * ctx->num_channels;
	ctx->ES_ID = ES_ID;

	return M4OK;
}

static M4Err AMR_DetachStream(BaseDecoder *ifcg, u16 ES_ID)
{
	AMRCTX();
	if (ES_ID != ctx->ES_ID) return M4BadParam;
	Speech_Decode_Frame_exit(&ctx->speech_decoder_state);
	ctx->speech_decoder_state = NULL;
	ctx->ES_ID = 0;
	ctx->sample_rate = ctx->out_size = ctx->num_samples = 0;
	ctx->num_channels = 0;
	return M4OK;
}
static M4Err AMR_GetCapabilities(BaseDecoder *ifcg, CapObject *capability)
{
	AMRCTX();
	switch (capability->CapCode) {
	/*not tested yet*/
	case CAP_HASRESILIENCE:
		capability->cap.valueInt = 1;
		break;
	case CAP_OUTPUTSIZE:
		capability->cap.valueInt = ctx->out_size;
		break;
	case CAP_SAMPLERATE:
		capability->cap.valueInt = ctx->sample_rate;
		break;
	case CAP_NBCHANNELS:
		capability->cap.valueInt = ctx->num_channels;
		break;
	case CAP_BITSPERSAMPLE:
		capability->cap.valueInt = 16;
		break;
	case CAP_BUFFER_MIN:
		capability->cap.valueInt = ctx->cb_trig;
		break;
	case CAP_BUFFER_MAX:
		capability->cap.valueInt = ctx->cb_size;
		break;
	/*FIXME: get exact sampling window*/
	case CAP_CU_DURATION:
		capability->cap.valueInt = ctx->num_samples;
		break;
	case CAP_PADDING_BYTES:
		capability->cap.valueInt = 0;
		break;
	case CAP_CHANNEL_CONFIG:
		if (ctx->num_channels==1) {
			capability->cap.valueInt = CHANNEL_FRONT_CENTER;
		} else {
			capability->cap.valueInt = CHANNEL_FRONT_LEFT | CHANNEL_FRONT_RIGHT;
		}
		break;
	default:
		capability->cap.valueInt = 0;
		break;
	}
	return M4OK;
}

static M4Err AMR_SetCapabilities(BaseDecoder *ifcg, CapObject capability)
{
	/*return unsupported to avoid confusion by the player (like SR changing ...) */
	return M4NotSupported;
}


/* frame size in serial bitstream file (frame type + serial stream + flags) */
#define SERIAL_FRAMESIZE (270)

static M4Err AMR_ProcessData(MediaDecoder *ifcg, 
		unsigned char *inBuffer, u32 inBufferLength,
		u16 ES_ID,
		unsigned char *outBuffer, u32 *outBufferLength,
		u8 PaddingBits, u32 mmlevel)
{
    u32 offset = 0;
    u8 toc, q, ft;
    s16 *synth;
    u8 *packed_bits;
    s16 serial[SERIAL_FRAMESIZE];
    s32 i;
	AMRCTX();

	/*not using scalabilty*/
	assert(ctx->ES_ID == ES_ID);

	/*if late or seeking don't decode (each frame is a RAP)*/
	/*	switch (mmlevel) {
	case MM_LEVEL_SEEK:
	case MM_LEVEL_DROP:
		*outBufferLength = 0;
		return M4OK;
	default:
		break;
	}
	*/
	if (ctx->out_size > *outBufferLength) {
		*outBufferLength = ctx->out_size;
		return M4BufferTooSmall;
	}
	
    synth = (s16 *) outBuffer;
	*outBufferLength = 0;

    while (inBufferLength) {
        toc = inBuffer[0];
        /* read rest of the frame based on ToC byte */
        q = (toc >> 2) & 0x01;
        ft = (toc >> 3) & 0x0F;

		packed_bits = inBuffer + 1;
		offset = 1 + amr_nb_frame_size[ft];

		/*Unsort and unpack bits*/
		ctx->rx_type = UnpackBits(q, ft, packed_bits, &ctx->mode, &serial[1]);
		ctx->frame_count++;

		if (ctx->rx_type == RX_NO_DATA) {
			ctx->mode = ctx->speech_decoder_state->prev_mode;
		} else {
			ctx->speech_decoder_state->prev_mode = ctx->mode;
		}
    
		/* if homed: check if this frame is another homing frame */
		if (ctx->reset_flag_old == 1) {
			/* only check until end of first subframe */
			ctx->reset_flag = decoder_homing_frame_test_first(&serial[1], ctx->mode);
		}
		/* produce encoder homing frame if homed & input=decoder homing frame */
		if ((ctx->reset_flag != 0) && (ctx->reset_flag_old != 0)) {
			for (i = 0; i < L_FRAME; i++) {
				synth[i] = EHF_MASK;
			}
		} else {     
			/* decode frame */
			Speech_Decode_Frame(ctx->speech_decoder_state, ctx->mode, &serial[1], ctx->rx_type, synth);
		}

		*outBufferLength += 160*2;
		synth += 160;
    
		/* if not homed: check whether current frame is a homing frame */
		if (ctx->reset_flag_old == 0) {
			ctx->reset_flag = decoder_homing_frame_test(&serial[1], ctx->mode);
		}
		/* reset decoder if current frame is a homing frame */
		if (ctx->reset_flag != 0) {
			Speech_Decode_Frame_reset(ctx->speech_decoder_state);
		}
		ctx->reset_flag_old = ctx->reset_flag;

		if (inBufferLength < offset) return M4OK;
		inBufferLength -= offset;
		inBuffer += offset;

		if (*outBufferLength > ctx->out_size) return M4NonCompliantBitStream;
    }
	return M4OK;
}


static u32 AMR_CanHandleStream(BaseDecoder *dec, u32 StreamType, u32 ObjectType, unsigned char *decSpecInfo, u32 decSpecInfoSize, u32 PL)
{
	BitStream *bs;
	char name[5];
	u32 codec_4cc;

	/*we handle audio only*/
	if (!ObjectType) return (StreamType==M4ST_AUDIO) ? 1 : 0;

	/*audio dec*/
	if (!decSpecInfo || (StreamType != M4ST_AUDIO) || (ObjectType != GPAC_QT_CODECS_OTI)) return 0;
	bs = NewBitStream(decSpecInfo, decSpecInfoSize, BS_READ);
	codec_4cc = BS_ReadU32(bs);
	DeleteBitStream(bs);

	FCC_TO_STR(codec_4cc, name);

	if (!stricmp(name, "samr") || !stricmp(name, "amr ")) return 1;
	return 0;
}


static const char *AMR_GetCodecName(BaseDecoder *dec)
{
	return "3GPP Fixed-Point AMR NB";
}

MediaDecoder *NewAMRDecoder()
{
	AMRDec *dec;
	MediaDecoder *ifce;
	SAFEALLOC(ifce , sizeof(MediaDecoder));
	dec = malloc(sizeof(AMRDec));
	memset(dec, 0, sizeof(AMRDec));
	ifce->privateStack = dec;
	ifce->CanHandleStream = AMR_CanHandleStream;

	dec->cb_size = DEFAULT_AUDIO_CM_SIZE;
	dec->cb_trig = DEFAULT_AUDIO_CM_TRIGGER;

	/*setup our own interface*/	
	ifce->AttachStream = AMR_AttachStream;
	ifce->DetachStream = AMR_DetachStream;
	ifce->GetCapabilities = AMR_GetCapabilities;
	ifce->SetCapabilities = AMR_SetCapabilities;
	ifce->ProcessData = AMR_ProcessData;
	ifce->GetName = AMR_GetCodecName;

	M4_REG_PLUG(ifce, M4MEDIADECODERINTERFACE, "AMR-NB 3GPP decoder", "gpac distribution", 0);

	return ifce;
}

void DeleteAMRDecoder(BaseDecoder *ifcg)
{
	AMRCTX();
	free(ctx);
	free(ifcg);
}

Bool QueryInterface(u32 InterfaceType)
{
	switch (InterfaceType) {
	case M4MEDIADECODERINTERFACE: return 1;
	case M4STREAMINGCLIENT: return 1;
	default: return 0;
	}
}

NetClientPlugin *NewAESReader();
void DeleteAESReader(void *ifce);

void *LoadInterface(u32 InterfaceType)
{
	switch (InterfaceType) {
	case M4MEDIADECODERINTERFACE: return NewAMRDecoder();
	case M4STREAMINGCLIENT: return NewAESReader();
	default: return NULL;
	}
}

void ShutdownInterface(void *ifce)
{
	BaseDecoder *ifcd = (BaseDecoder *)ifce;
	switch (ifcd->InterfaceType) {
	case M4MEDIADECODERINTERFACE: DeleteAMRDecoder(ifcd); break;
	case M4STREAMINGCLIENT:  DeleteAESReader(ifce); break;
	}
}
