#include "p_apps.h"
#include "p_sip.h"
#include "v_task.h"
#include "my_sip.h"

// client transaction state
#define TRANS_STATE_TERMINATED		0
#define TRANS_STATE_TRYING			1
#define TRANS_STATE_PROCEEDING		2
#define TRANS_STATE_COMPLETED		3

#define TRANS_DATA_STATE		0
#define TRANS_DATA_METHOD		1
#define TRANS_DATA_RXINDEX		2
#define TRANS_DATA_T0_COUNT		3
#define TRANS_DATA_T1_COUNT		4
#define TRANS_DATA_T2_COUNT		5
#define TRANS_DATA_UDPHEAD		6
#define TRANS_DATA_FLAG			7
#define TRANS_DATA_LEN			8

#define TRANS_FLAG_NONE		0
#define TRANS_FLAG_100REL	1

#define TRANS_TIMER_A		2	// INVITE request retransmit interval, for UDP only
#define TRANS_TIMER_B		11	// INVITE transaction timeout timer
#define TRANS_TIMER_D		11	// Wait time for response retransmits, 0s for TCP/SCTP	
#define TRANS_TIMER_E		2	// non-INVITE request retransmit interval, UDP only
#define TRANS_TIMER_F		11	// non-INVITE transaction timeout timer
#define TRANS_TIMER_K		5	// Wait time for response retransmits, 0s for TCP/SCTP

#define TRANS_TIMER_G		2	// INVITE response retransmit interval
#define TRANS_TIMER_H		33	// Wait time for ACK receipt
#define TRANS_TIMER_I		5	// Wait time for ACK retransmits, 0s for TCP/SCTP
#define TRANS_TIMER_J		33	// Wait time for non-INVITE request retransmits, 0s for TCP/SCTP

static XCHAR _pTransData[TRANS_DATA_LEN];

/****************************************************************************
****************************************************************************/
void _TransLoadData(UCHAR iTransNo)
{
	SdramReadData((USHORT)(SIP_TRANS_SDRAM_POS+iTransNo));
	p_memcpy_x2x(_pTransData, (PXCHAR)Sdram_pDataCache, TRANS_DATA_LEN);
}

void _TransStoreData(UCHAR iTransNo)
{
	p_memcpy_x2x((PXCHAR)Sdram_pDataCache, _pTransData, TRANS_DATA_LEN);
	SdramWriteData((USHORT)(SIP_TRANS_SDRAM_POS+iTransNo), TRANS_DATA_LEN);
}

void _DeallocTrans()
{
	SipRxDealloc(_pTransData[TRANS_DATA_RXINDEX]);
	_pTransData[TRANS_DATA_STATE] = TRANS_STATE_TERMINATED;
}

BOOLEAN _TransCmpData(UCHAR iDstIndex, UCHAR iSrcIndex)
{
	DCHAR iSrcLen, iDstLen;

	iSrcLen = SdramReadData((USHORT)(SIP_TRANS_SDRAM_POS+iSrcIndex));
	p_memcpy_x2x((PXCHAR)Sdram_pDataCache2, (PXCHAR)Sdram_pDataCache, iSrcLen);
	iDstLen = SdramReadData((USHORT)(SIP_TRANS_SDRAM_POS+iDstIndex));
	if (iSrcLen == iDstLen)
	{
		if (p_memcmp_nocase_x2x((PXCHAR)Sdram_pDataCache2, (PXCHAR)Sdram_pDataCache, iSrcLen))
			return TRUE;
	}
	return FALSE;
}

UCHAR _GetMatchingTrans(UCHAR iMethod, BOOLEAN bClient)
{
	DCHAR i, iBaseIndex, iBranchIndex;

	if (bClient)	
	{
		iBaseIndex = SIP_CT_INFO;
		iBranchIndex = SIP_CT_BRANCH; 
	}
	else
	{
		iBaseIndex = SIP_ST_INFO;
		iBranchIndex = SIP_ST_BRANCH;
	}

	for (i = iBaseIndex; i < (UCHAR)(iBaseIndex + SIP_MAX_TRANS_NUM); i ++)
	{
		_TransLoadData(i);
		if (_pTransData[TRANS_DATA_STATE] == TRANS_STATE_TERMINATED)
			continue;
		if (_pTransData[TRANS_DATA_METHOD] == iMethod && _TransCmpData((UCHAR)(i - iBaseIndex + iBranchIndex), SIP_TRANS_BRANCH))
			return i;
	}

	return SIP_TRANS_ERROR;
}

void _CtStateMachine(UCHAR iVal, USHORT iRespVal)
{
	DCHAR iTransNo;
	DSHORT iResp;
	BOOLEAN bEffective;

	iTransNo = iVal;
	iResp = iRespVal;
	_TransLoadData(iTransNo);
	switch (_pTransData[TRANS_DATA_STATE])
	{
	case TRANS_STATE_TRYING:
	case TRANS_STATE_PROCEEDING:
		if (iResp < 200)
		{
			_pTransData[TRANS_DATA_STATE] = TRANS_STATE_PROCEEDING;
			_pTransData[TRANS_DATA_T1_COUNT] = 0;
		}
		else
		{
			if (Sip_bUsingUdp)
				_pTransData[TRANS_DATA_STATE] = TRANS_STATE_COMPLETED;
			else
				_DeallocTrans();
		}
		_TransStoreData(iTransNo);
		// inform TU of this response ...
		bEffective = SipTUHandleResp(_pTransData[TRANS_DATA_METHOD], iResp);
		if (!bEffective)
		{
			_DeallocTrans();
			_TransStoreData(iTransNo);
		}
		break;
	case TRANS_STATE_COMPLETED:
		if (_pTransData[TRANS_DATA_METHOD] == SIP_REQ_INVITE && iResp >= 200)
		{
			SipRxSend(_pTransData[TRANS_DATA_RXINDEX], _pTransData[TRANS_DATA_UDPHEAD]);
		}
		break;
	}
}

void _IstStateMachine(UCHAR iVal, USHORT iEventVal)
{
	DCHAR iTransNo;
	DSHORT iEvent;

	iTransNo = iVal;
	iEvent = iEventVal;
	_TransLoadData(iTransNo);
	switch (_pTransData[TRANS_DATA_STATE])
	{
	case TRANS_STATE_TRYING:
		if (iEvent == SIP_REQ_INVITE)
		{
			SipRxSend(_pTransData[TRANS_DATA_RXINDEX], _pTransData[TRANS_DATA_UDPHEAD]);
		}
		else if (iEvent >= 200)
		{
			_pTransData[TRANS_DATA_STATE] = TRANS_STATE_PROCEEDING;
			_pTransData[TRANS_DATA_T0_COUNT] = 0;
		}
		break;
	case TRANS_STATE_PROCEEDING:
		if (iEvent == SIP_REQ_INVITE)
		{
			SipRxSend(_pTransData[TRANS_DATA_RXINDEX], _pTransData[TRANS_DATA_UDPHEAD]);
		}
		break;
	}
	_TransStoreData(iTransNo);
}

void _NistStateMachine(UCHAR iVal, USHORT iEventVal)
{
	DCHAR iTransNo;
	DSHORT iEvent;

	iTransNo = iVal;
	iEvent = iEventVal;

	_TransLoadData(iTransNo);
	switch (_pTransData[TRANS_DATA_STATE])
	{
	case TRANS_STATE_TRYING:
		if (iEvent >= 100 && iEvent < 200)
		{
			_pTransData[TRANS_DATA_STATE] = TRANS_STATE_PROCEEDING;
		}
		else if (iEvent >= 200)
		{
			if (Sip_bUsingUdp)
				_pTransData[TRANS_DATA_STATE] = TRANS_STATE_COMPLETED;
			else
				_DeallocTrans();
		}
		break;
	case TRANS_STATE_PROCEEDING:
		if (iEvent == _pTransData[TRANS_DATA_METHOD])
		{
			SipRxSend(_pTransData[TRANS_DATA_RXINDEX], _pTransData[TRANS_DATA_UDPHEAD]);
		}
		else if (iEvent >= 200)
		{
			if (Sip_bUsingUdp)
				_pTransData[TRANS_DATA_STATE] = TRANS_STATE_COMPLETED;
			else
				_DeallocTrans();
		}
		break;
	case TRANS_STATE_COMPLETED:
		if (iEvent == _pTransData[TRANS_DATA_METHOD])
		{
			SipRxSend(_pTransData[TRANS_DATA_RXINDEX], _pTransData[TRANS_DATA_UDPHEAD]);
		}
		break;
	}
	_TransStoreData(iTransNo);
}

/****************************************************************************
****************************************************************************/
void SipTransStartup()
{
	DCHAR i;

	X_VAL(Sdram_pDataCache, TRANS_DATA_STATE) = TRANS_STATE_TERMINATED;
	for (i = SIP_CT_INFO; i < (SIP_CT_INFO + SIP_MAX_TRANS_NUM); i ++)
	{
		SdramWriteData((USHORT)(SIP_TRANS_SDRAM_POS+i), TRANS_DATA_LEN);
	}
	for (i = SIP_ST_INFO; i < (SIP_ST_INFO + SIP_MAX_TRANS_NUM); i ++)
	{
		SdramWriteData((USHORT)(SIP_TRANS_SDRAM_POS+i), TRANS_DATA_LEN);
	}
}

UCHAR SipAllocTrans(BOOLEAN bClient)
{
	DCHAR i, iBaseIndex;

	if (bClient)	iBaseIndex = SIP_CT_INFO;
	else			iBaseIndex = SIP_ST_INFO;

	for (i = iBaseIndex; i < (UCHAR)(iBaseIndex + SIP_MAX_TRANS_NUM); i ++)
	{
		SdramReadData((USHORT)(SIP_TRANS_SDRAM_POS+i));
		if (X_VAL(Sdram_pDataCache, TRANS_DATA_STATE) == TRANS_STATE_TERMINATED)
		{
			return i;
		}
	}

	OutputString(IDS_SIP_NO_FREE_TRANS);
	return SIP_TRANS_ERROR;
}

void SipCtSendReq(UCHAR iMethod, UCHAR iUdpIndex, UCHAR iTransNo)
{
	DCHAR iLen, iRxIndex;
	
	// Save Transaction Information
	iRxIndex = SipRxAlloc();
	_pTransData[TRANS_DATA_STATE] = TRANS_STATE_TRYING;
	_pTransData[TRANS_DATA_METHOD] = iMethod;
	_pTransData[TRANS_DATA_RXINDEX] = iRxIndex;
	_pTransData[TRANS_DATA_T0_COUNT] = 0;
	_pTransData[TRANS_DATA_T1_COUNT] = 0;
	_pTransData[TRANS_DATA_T2_COUNT] = 0;
	_pTransData[TRANS_DATA_UDPHEAD] = iUdpIndex;
	_pTransData[TRANS_DATA_FLAG] = TRANS_FLAG_NONE;
	_TransStoreData(iTransNo);

	Sip_iSendLen = p_line_get_len();

	// save retransmission packet
	SipRxSave(iRxIndex);

	// save branch parameter
	iLen = SdramReadData(PROTOCOL_SDRAM_POS+SIP_CUR_BRANCH);
	SdramWriteData((USHORT)(SIP_TRANS_SDRAM_POS+iTransNo - SIP_CT_INFO + SIP_CT_BRANCH), iLen);

	// send out data
	SipUdpSend(iUdpIndex);
}

void SipCtRecvResp(UCHAR iMethod, USHORT iResponse)
{
	DCHAR iTransNo;

	iTransNo = _GetMatchingTrans(iMethod, TRUE);
	if (iTransNo == SIP_TRANS_ERROR)
	{
		OutputString(IDS_SIP_NO_MATCH_TRANS);
		return;
	}

	_CtStateMachine(iTransNo, iResponse);
}

void SipCtAckInv(UCHAR iTransNo)
{
	_TransLoadData(iTransNo);
	Sip_iSendLen = p_line_get_len();
	SipRxSave(_pTransData[TRANS_DATA_RXINDEX]);
	SipUdpSend(_pTransData[TRANS_DATA_UDPHEAD]);
}

void SipCtHandleTimer()
{
	DCHAR i, iTimer;

	for (i = SIP_CT_INFO; i < (SIP_CT_INFO + SIP_MAX_TRANS_NUM); i ++)
	{
		_TransLoadData(i);

		if (_pTransData[TRANS_DATA_STATE] == TRANS_STATE_TERMINATED)
			continue;

		switch (_pTransData[TRANS_DATA_STATE])
		{
		case TRANS_STATE_TRYING:
		case TRANS_STATE_PROCEEDING:
			if (_pTransData[TRANS_DATA_STATE] == TRANS_STATE_TRYING || _pTransData[TRANS_DATA_METHOD] != SIP_REQ_INVITE)
			{
				_pTransData[TRANS_DATA_T1_COUNT] ++;
				if (_pTransData[TRANS_DATA_T1_COUNT] > TRANS_TIMER_B)
				{
					// Timer B fires, inform TU of transmission error ...
					SipTUHandleTxError(_pTransData[TRANS_DATA_METHOD], i);
					_DeallocTrans();
					break;
				}
				if (Sip_bUsingUdp)
				{
					_pTransData[TRANS_DATA_T0_COUNT] ++;
					if (_pTransData[TRANS_DATA_T0_COUNT] > TRANS_TIMER_A)
					{
						SipRxSend(_pTransData[TRANS_DATA_RXINDEX], _pTransData[TRANS_DATA_UDPHEAD]);
						_pTransData[TRANS_DATA_T0_COUNT] = 0;
					}
				}
			}
			break;
		case TRANS_STATE_COMPLETED:
			_pTransData[TRANS_DATA_T2_COUNT] ++;
			if (_pTransData[TRANS_DATA_METHOD] == SIP_REQ_INVITE)
			{
				iTimer = TRANS_TIMER_D;
			}
			else
			{
				iTimer = TRANS_TIMER_K;
			}
			if (_pTransData[TRANS_DATA_T2_COUNT] > iTimer)
			{
				_DeallocTrans();
			}
			break;
		}
		_TransStoreData(i);
	}
}

/****************************************************************************
****************************************************************************/
void SipStSave(UCHAR iTransNo, UCHAR iMethod, UCHAR iUdpIndex)
{
	DCHAR iLen;

	if (iTransNo == SIP_TRANS_ERROR)
		return;
	// Save Transaction Information
	_pTransData[TRANS_DATA_STATE] = TRANS_STATE_TRYING;
	_pTransData[TRANS_DATA_METHOD] = iMethod;
	_pTransData[TRANS_DATA_RXINDEX] = SipRxAlloc();
	_pTransData[TRANS_DATA_T0_COUNT] = 0;
	_pTransData[TRANS_DATA_T1_COUNT] = 0;
	_pTransData[TRANS_DATA_T2_COUNT] = 0;
	_pTransData[TRANS_DATA_UDPHEAD] = iUdpIndex;
	_pTransData[TRANS_DATA_FLAG] = TRANS_FLAG_NONE;
	_TransStoreData(iTransNo);

	// save branch parameter
	iLen = SdramReadData(SIP_TRANS_SDRAM_POS+SIP_TRANS_BRANCH);
	SdramWriteData((USHORT)(SIP_TRANS_SDRAM_POS+iTransNo - SIP_ST_INFO + SIP_ST_BRANCH), iLen);
}

void SipStRecvReq(UCHAR iVal)
{
	DCHAR iTransNo, iMethod;

	iMethod = iVal;
	iTransNo = _GetMatchingTrans(iMethod, FALSE);
	if (iTransNo == SIP_TRANS_ERROR)
	{
		SipTUHandleReq(iMethod);
	}
	else
	{
		if (iMethod == SIP_REQ_INVITE)
		{
			_IstStateMachine(iTransNo, iMethod);
		}
		else
		{
			_NistStateMachine(iTransNo, iMethod);
		}
	}
}

BOOLEAN SipStRecvAck(UCHAR iVal)
{
	DCHAR iTransNo;

	iTransNo = iVal;
	if (iTransNo == SIP_TRANS_ERROR)
		return FALSE;

	_TransLoadData(iTransNo);
	if (_pTransData[TRANS_DATA_STATE] == TRANS_STATE_PROCEEDING)
	{
		if (Sip_bUsingUdp)
			_pTransData[TRANS_DATA_STATE] = TRANS_STATE_COMPLETED;
		else
			_DeallocTrans();
		_TransStoreData(iTransNo);
		return TRUE;
	}

	return FALSE;
}

void SipStSendResp(UCHAR iVal, USHORT iResp)
{
	DCHAR iTransNo;

	iTransNo = iVal;
	_TransLoadData(iTransNo);
	if (_pTransData[TRANS_DATA_METHOD] == SIP_REQ_INVITE)
	{
		_IstStateMachine(iTransNo, iResp);
	}
	else
	{
		_NistStateMachine(iTransNo, iResp);
	}
	Sip_iSendLen = p_line_get_len();
	SipRxSave(_pTransData[TRANS_DATA_RXINDEX]);
	SipUdpSend(_pTransData[TRANS_DATA_UDPHEAD]);
}

void SipStHandleTimer()
{
	DCHAR i;

	for (i = SIP_ST_INFO; i < (SIP_ST_INFO + SIP_MAX_TRANS_NUM); i ++)
	{
		_TransLoadData(i);
		if (_pTransData[TRANS_DATA_STATE] == TRANS_STATE_TERMINATED)
			continue;
		if (_pTransData[TRANS_DATA_METHOD] == SIP_REQ_INVITE)
		{
			switch (_pTransData[TRANS_DATA_STATE])
			{
			case TRANS_STATE_TRYING:
				if (_pTransData[TRANS_DATA_FLAG] & TRANS_FLAG_100REL)
				{
					if (Sip_bUsingUdp)
					{
						_pTransData[TRANS_DATA_T0_COUNT] ++;
						if (_pTransData[TRANS_DATA_T0_COUNT] > TRANS_TIMER_G)
						{
							SipRxSend(_pTransData[TRANS_DATA_RXINDEX], _pTransData[TRANS_DATA_UDPHEAD]);
							_pTransData[TRANS_DATA_T0_COUNT] = 0;
						}
					}
				}
				break;
			case TRANS_STATE_PROCEEDING:
				_pTransData[TRANS_DATA_T1_COUNT] ++;
				if (_pTransData[TRANS_DATA_T1_COUNT] > TRANS_TIMER_H)
				{
					// Timer B fires, inform TU of transmission error ...
					SipTUHandleTxError(_pTransData[TRANS_DATA_METHOD], i);
					_DeallocTrans();
					break;
				}
				if (Sip_bUsingUdp)
				{
					_pTransData[TRANS_DATA_T0_COUNT] ++;
					if (_pTransData[TRANS_DATA_T0_COUNT] > TRANS_TIMER_G)
					{
						SipRxSend(_pTransData[TRANS_DATA_RXINDEX], _pTransData[TRANS_DATA_UDPHEAD]);
						_pTransData[TRANS_DATA_T0_COUNT] = 0;
					}
				}
				break;
			case TRANS_STATE_COMPLETED:
				_pTransData[TRANS_DATA_T2_COUNT] ++;
				if (_pTransData[TRANS_DATA_T2_COUNT] > TRANS_TIMER_I)
				{
					_DeallocTrans();
				}
				break;
			}
		}
		else
		{
			if (_pTransData[TRANS_DATA_STATE] == TRANS_STATE_COMPLETED)
			{
				_pTransData[TRANS_DATA_T2_COUNT] ++;
				if (_pTransData[TRANS_DATA_T2_COUNT] > TRANS_TIMER_J)
				{
					_DeallocTrans();
				}
			}
		}
		_TransStoreData(i);
	}
}

void SipTransSetFlag100rel(UCHAR iVal)
{
	DCHAR iTransNo;

	iTransNo = iVal;
	_TransLoadData(iTransNo);
	_pTransData[TRANS_DATA_FLAG] |= TRANS_FLAG_100REL;
	_TransStoreData(iTransNo);
}

void SipTransClearFlag100rel(UCHAR iVal)
{
	DCHAR iTransNo;

	iTransNo = iVal;
	_TransLoadData(iTransNo);
	_pTransData[TRANS_DATA_FLAG] &= ~TRANS_FLAG_100REL;
	_TransStoreData(iTransNo);
}
