#include "PalmType.h"
#include "Palm1lib.h"
#include "p_tcpip.h"
#include "my_iax2.h"
#include "p_iax2.h"
#include "v_task.h"
#include "p_dsp.h"
#include "p_rtp.h"

static XCHAR _iG711RemainLen;
static XCHAR _iCurSendFrames;
static XSHORT _iCurSendLen;

static CLONG _cAudioFormat[CODEC_NULL]= {AST_FORMAT_G729A, AST_FORMAT_G723_1, AST_FORMAT_ULAW, AST_FORMAT_ALAW, AST_FORMAT_GSM, AST_FORMAT_ILBC};

void iax2_voice_init()
{
	_iG711RemainLen = 0;
	_iCurSendFrames = 0;
	_iCurSendLen = 0;
}

ULONG iax2_uncompress_subclass(UCHAR iSubClass)
{
	/* If the SC_LOG flag is set, return 2^csub otherwise csub */
	if (iSubClass & IAX_FLAG_SC_LOG)
		return (ULONG)(1 << (iSubClass & ~IAX_FLAG_SC_LOG & IAX_MAX_SHIFT));
	else
		return (ULONG)iSubClass;
}

UCHAR iax2_compress_subclass(ULONG iSubClass)
{
	DCHAR i;

	/* If it's 128 or smaller, just return it */
	if (iSubClass < IAX_FLAG_SC_LOG)
		return (UCHAR)iSubClass;

	/* Otherwise find its power */
	for (i = 0; i < IAX_MAX_SHIFT; i ++) 
	{
		if (iSubClass & (1 << i)) 
		{
			break;
		}
	}
	return (UCHAR)(i | IAX_FLAG_SC_LOG);
}

UCHAR iax2_get_codec(ULONG lFormat)
{
	DCHAR i;

	for (i = 0; i < CODEC_NULL; i ++)
	{
		if (lFormat == _cAudioFormat[i])
		{
			return i;
		}
	}
	return CODEC_NULL;
}

ULONG iax2_get_format(UCHAR iCodecType)
{
	if (iCodecType < CODEC_NULL)
	{
		return _cAudioFormat[iCodecType];
	}
	else
	{
		return 0;
	}
}


ULONG iax2_get_capability()
{
	DCHAR i;
	DLONG lFormat;

	lFormat = 0;
	for (i = 0; i < SYS_CODEC_NUM; i ++)
	{
		if (Sys_pCodecType[i] != CODEC_NULL)
		{
			lFormat |= _cAudioFormat[Sys_pCodecType[i]];
		}
	}
	return lFormat;
}

UCHAR iax2_audio_match(UCHAR iType)
{
	DCHAR i;

	if (iType < CODEC_NULL)
	{
		for (i = 0; i < SYS_CODEC_NUM; i ++)
		{
			if (Sys_pCodecType[i] != CODEC_NULL && Sys_pCodecType[i] == iType)
			{
				return iType;
			}
		}
	}
	return CODEC_NULL;
}

void iax2_recv_voice(PXCHAR pData, USHORT iLen)
{
	DCHAR iFrameLen;
	DSHORT iDataLen;
	PXCHAR_D pCur;

	if (Sys_iAudioType == CODEC_NULL)	return;
	if (Task_bCallHold)	return;
	if (Iax2_iCallState[Iax2_iCurCall] != IAX_CALL_STATE_CONNECTED && Iax2_iCallState[Iax2_iCurCall] != IAX_CALL_STATE_CONNECTING)	return;

	pCur = pData;
	iDataLen = iLen;

	if (_iG711RemainLen)
	{
		if (Sys_iAudioType == CODEC_G711A || Sys_iAudioType == CODEC_G711U)
		{
			SdramReadData(OPTIONS_SDRAM_POS+RTP_G711_SDRAM_LO);
			iFrameLen = G711_LEN - _iG711RemainLen;
			p_memcpy_x2x((PXCHAR)(Sdram_pDataCache + _iG711RemainLen), pCur, iFrameLen);
			TaskIncomingData(G711_LEN, (PXCHAR)Sdram_pDataCache);
			pCur += iFrameLen;
			if (iDataLen >= iFrameLen)
			{
				iDataLen -= iFrameLen;
			}
			else
			{
				iDataLen = 0;
			}
		}
		_iG711RemainLen = 0;
	}

	while (iDataLen)
	{
		switch (Sys_iAudioType)
		{
		case CODEC_G7231:
			iFrameLen = G7231GetFrameLen(pCur[0]);
			break;
		case CODEC_G729:
			iFrameLen = (iDataLen > G729_R8_LEN) ? G729_R8_LEN : iDataLen;
			break;
		case CODEC_G711A:
		case CODEC_G711U:
			iFrameLen = G711_LEN;
			break;
		case CODEC_GSM610:
			iFrameLen = GSM610_LEN;
			break;
		case CODEC_ILBC:
			iFrameLen = ILBC_LEN;
			break;
		}

		if (iDataLen >= iFrameLen)
		{
			iDataLen -= iFrameLen;
		}
		else
		{
			if (iDataLen)
			{
				if (Sys_iAudioType == CODEC_G711A || Sys_iAudioType == CODEC_G711U)
				{
					_iG711RemainLen = (UCHAR)iDataLen;
					SdramSaveData(OPTIONS_SDRAM_POS+RTP_G711_SDRAM_LO, pCur, (UCHAR)iDataLen);
				}
			}
			return;
		}

		TaskIncomingData(iFrameLen, pCur);
		pCur += iFrameLen;
	}
}


 void Iax2SendVoice(UCHAR iLen, PXCHAR pData)
{
	DCHAR iSession, iSubClass;
	DLONG lTimeStamp;
	BOOLEAN bVad;
	PXCHAR pCur;

	if (Sys_iAudioType == CODEC_NULL)	return;
	if (Iax2_iCallState[Iax2_iCurCall] != IAX_CALL_STATE_CONNECTED
		&& (Iax2_iCallState[Iax2_iCurCall] != IAX_CALL_STATE_CONNECTING || Iax2_iIsCaller[Iax2_iCurCall]))	return;

	iSession = Iax2_iCallSess[Iax2_iCurCall];
	if (iSession == IAX_SESSION_ERROR)		return;
	if (Sess_iQuelch[iSession])				return;
	if (iLen < Dsp_iVadLen[Sys_iAudioType])	return;

	if (Sys_iAudioFrames == 1)
	{
		pCur = pData;
		_iCurSendFrames = 1;
		_iCurSendLen = iLen;
	}
	else
	{
		p_wmemcpy_x2x((PXCHAR)(Adapter_pSendUdpData + _iCurSendLen), pData, iLen);
		if (_iCurSendFrames)
		{
			SdramReadAll(OPTIONS_SDRAM_POS+RTP_DATA_SDRAM_LO);
			p_wmemcpy_x2x((PXCHAR)Adapter_pSendUdpData, (PXCHAR)Sdram_pDataCache, _iCurSendLen);
		}
	//	p_memcpy_x2x((PXCHAR)(_pDataOut+_iCurSendLen), pData, iLength);
		_iCurSendFrames ++;
 		_iCurSendLen += iLen;

		p_wmemcpy_x2x(Sdram_pDataCache, (PXCHAR)Adapter_pSendUdpData, _iCurSendLen);
		SdramWriteAll(OPTIONS_SDRAM_POS+RTP_DATA_SDRAM_LO);
		
		bVad = (iLen >= Rtp_cVoiceLen[Sys_iAudioType]) ? FALSE : TRUE;
		if (_iCurSendFrames < Sys_iAudioFrames)
		{
			if (!bVad)	// if VAD frame, we will send out even the total number is not enough
			{
				return;
			}
		}
		SdramReadAll(OPTIONS_SDRAM_POS+RTP_DATA_SDRAM_LO);
		pCur = (PXCHAR)Sdram_pDataCache;
	}

	if (!Sess_lLastSent[iSession])
	{
		lTimeStamp = Sys_lTicker - Sess_lOffset[iSession];
	}
	else
	{
		lTimeStamp = Sess_lLastSent[iSession] + Rtp_cFrameTime[Sys_iAudioType] * _iCurSendFrames;
	}
	if (Sess_lLastSent[iSession] && ((lTimeStamp & 0xFFFF0000) == (Sess_lLastSent[iSession] & 0xFFFF0000)))
	{
		USHORT2PXCHAR(Sess_iCallNo[iSession], (PXCHAR)(Adapter_pSendUdpData + IAX_MINIFR_SCALLNO));
		USHORT2PXCHAR((USHORT)(lTimeStamp & 0xffff), (PXCHAR)(Adapter_pSendUdpData + IAX_MINIFR_TSTAMP));
		p_wmemcpy_x2x((PXCHAR)(Adapter_pSendUdpData + IAX_MINIFR_DATA), pCur, _iCurSendLen);
		iax2_rel_send((USHORT)(_iCurSendLen + IAX_MINIFR_DATA), iSession);
	}
	else
	{
		iSubClass = iax2_compress_subclass(_cAudioFormat[Sys_iAudioType]);
		p_wmemcpy_x2x((PXCHAR)(Adapter_pSendUdpData + IAX_FULLFR_DATA), pCur, _iCurSendLen);

		iax2_session_fill(iSession, AST_FRAME_VOICE, iSubClass);
		ULONG2PUCHAR(lTimeStamp, (PUCHAR)(PXCHAR)(Adapter_pSendUdpData + IAX_FULLFR_TSTAMP));
		X_VAL(Adapter_pSendUdpData, IAX_FULLFR_OSEQNO) = Sess_iOSeqNo[iSession];
		Sess_iOSeqNo[iSession] ++;
		_iCurSendLen += IAX_FULLFR_DATA;
		iax2_frame_save(iSession, _iCurSendLen, X_VAL(Adapter_pSendUdpData, IAX_FULLFR_OSEQNO));
		iax2_rel_send(_iCurSendLen, iSession);
	}

	Sess_lLastSent[iSession] = lTimeStamp;
	_iCurSendFrames = 0;
	_iCurSendLen = 0;
}
