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

#define SESSION_WAIT_TIME		8

static CCHAR Sess_cUdpSocket[IAX_SESSION_NUM] = {IAX2_SESS0_SDRAM_LO, IAX2_SESS1_SDRAM_LO, IAX2_SESS2_SDRAM_LO, IAX2_SESS3_SDRAM_LO, IAX2_SESS4_SDRAM_LO};

void iax2_dbg_send(USHORT iLen, UCHAR iSession)
{
	OutputVals((PXCHAR)Adapter_pSendUdpData, iLen);
	UdpSend(iLen, Sess_cUdpSocket[iSession]);
}

void iax2_rel_send(USHORT iLen, UCHAR iSession)
{
	UdpSend(iLen, Sess_cUdpSocket[iSession]);
}

void iax2_open_socket(UCHAR iSession, PXCHAR pDstIP, USHORT iDstPort)
{
	DCHAR iSocket;
	DCHAR iIndex;

	iIndex = iSession;
	iSocket = Sess_cUdpSocket[iIndex];
	p_memcpy_ip(Sess_pPeerIP[iIndex], pDstIP);
	Sess_iPeerPort[iIndex] = iDstPort;
	UdpOpen(pDstIP, iDstPort, Sys_iRegisterPort, iSocket);
	UdpSetTos(Rtp_iTos, iSocket);
}

void iax2_session_startup()
{
	DCHAR i;

	/* sessions up to IAX_START_SESSION are reserved */
	for (i = 0; i < IAX_SESSION_NUM; i ++)
	{
		Sess_iState[i] = SESSION_STATE_IDLE;
	}
}

void iax2_session_init(UCHAR iIndex)
{
	DCHAR i;
	
	i = iIndex;
	Sess_iState[i] = SESSION_STATE_USED;
	Sess_lLastSent[i] = 0;
	Sess_lOffset[i] = Sys_lTicker - 3;
	Sess_iCallNo[i] = 0;
	Sess_iPeerCallNo[i] = 0;
	Sess_iOSeqNo[i] = 0;
	Sess_iRSeqNo[i] = 0;
	Sess_iISeqNo[i] = 0;
	Sess_iTimer[i] = 0;
	Sess_iQuelch[i] = 0;
	p_memset_x(Sess_pPeerIP[i], 0, IP_ALEN);
	Sess_iPeerPort[i] = 0;
	Sess_iTransSession[i] = IAX_SESSION_ERROR;
	Sess_iTransState[i] = IAX_TRANS_STATE_NONE;
	Sess_iTransCallNo[i] = 0;
	Sess_iCallNo[i] = Iax2_iCallNo;
	Iax2_iCallNo ++;
}

UCHAR iax2_session_new()
{
	DCHAR i;

	/* sessions up to IAX_START_SESSION are reserved */
	for (i = IAX_START_SESSION; i < IAX_SESSION_NUM; i ++)
	{
		if (Sess_iState[i] == SESSION_STATE_IDLE)
		{
			iax2_session_init(i);
			return i;
		}
	}
	return IAX_SESSION_ERROR;
}

void iax2_session_free(UCHAR iIndex)
{
	Sess_iState[iIndex] = SESSION_STATE_IDLE;
	iax2_frame_free(iIndex);
}

UCHAR iax2_session_find_full(USHORT iSrcCallNo, USHORT iDstCallNo)
{
	DCHAR i;

	for (i = 0; i < IAX_SESSION_NUM; i ++)
	{
		if (Sess_iState[i] == SESSION_STATE_IDLE)
			continue;
		if ((Sess_iCallNo[i] == iDstCallNo) && ((!Sess_iPeerCallNo[i]) || (Sess_iPeerCallNo[i] == iSrcCallNo)))
		{
			Sess_iPeerCallNo[i] = iSrcCallNo;
			return i;
		}
	}
	return IAX_SESSION_ERROR;
}

UCHAR iax2_session_find_new(USHORT iSrcCallNo)
{
	DCHAR i;

	for (i = 0; i < IAX_SESSION_NUM; i ++)
	{
		if (Sess_iState[i] == SESSION_STATE_IDLE)
			continue;
		if (Sess_iPeerCallNo[i] == iSrcCallNo)
		{
			return i;
		}
	}
	return IAX_SESSION_ERROR;
}

UCHAR iax2_session_find_mini(USHORT iSrcCallNo)
{
	DCHAR i;

	// 0 is reserved for IAX_REG_SESSION and no mini for register session
	for (i = IAX_START_SESSION; i < IAX_SESSION_NUM; i ++)
	{
		if (Sess_iState[i] == SESSION_STATE_IDLE)
			continue;
		if (Sess_iPeerCallNo[i] == iSrcCallNo)
		{
			return i;
		}
	}
	return IAX_SESSION_ERROR;
}

void iax2_session_rxerror(UCHAR iIndex)
{
	DCHAR iSession;

	DebugCString((PCCHAR)"Retransmission failed");
	iSession = iIndex;
	Sess_iState[iSession] = SESSION_STATE_IDLE;
	if (iSession == IAX_REG_SESSION)
	{
		TaskLogonFailed(0);
	}
	else if (iSession == Iax2_iCallSess[0])
	{
		iax2_call_end(0);
	}
	else if (iSession == Iax2_iCallSess[1])
	{
		iax2_call_end(1);
	}
}

void iax2_session_set_state(UCHAR iIndex, UCHAR iState)
{
	Sess_iState[iIndex] = iState;
}

void iax2_session_finalack(UCHAR iIndex)
{
	if (Sess_iState[iIndex] == SESSION_STATE_FINALACK)
	{
//		iax2_session_free(iIndex);
		Sess_iState[iIndex] = SESSION_STATE_TIMEWAIT;
	}
}

void iax2_session_handle_timer()
{
	DCHAR i;

	for (i = 0; i < IAX_SESSION_NUM; i ++)
	{
		if (Sess_iState[i] == SESSION_STATE_TIMEWAIT || Sess_iState[i] == SESSION_STATE_FINALACK)
		{
			Sess_iTimer[i] ++;
			if (Sess_iTimer[i] > SESSION_WAIT_TIME)
			{
				iax2_session_free(i);
			}
		}
	}
}
/*********************************************************************************
 ***  functions to send IAX2 messages ********************************************
 ********************************************************************************/

void iax2_session_fill(UCHAR iIndex, UCHAR iType, UCHAR iSubClass)
{
	DCHAR i;

	i = iIndex;
	USHORT2PXCHAR((USHORT)(Sess_iCallNo[i] | IAX_FLAG_FULL), (PXCHAR)(Adapter_pSendUdpData + IAX_FULLFR_SCALLNO));
	USHORT2PXCHAR(Sess_iPeerCallNo[i], (PXCHAR)(Adapter_pSendUdpData + IAX_FULLFR_DCALLNO));
	X_VAL(Adapter_pSendUdpData, IAX_FULLFR_ISEQNO) = Sess_iISeqNo[i];
	X_VAL(Adapter_pSendUdpData, IAX_FULLFR_TYPE) = iType;
	X_VAL(Adapter_pSendUdpData, IAX_FULLFR_CSUB) = iSubClass;
}

void iax2_session_send_packet(UCHAR iIndex, UCHAR iType, UCHAR iCommand, USHORT iDataLen)
{
	DSHORT iLen;

	iax2_session_fill(iIndex, iType, iCommand);
	ULONG2PUCHAR((ULONG)(Sys_lTicker - Sess_lOffset[iIndex]), (PUCHAR)(PXCHAR)(Adapter_pSendUdpData + IAX_FULLFR_TSTAMP));
	X_VAL(Adapter_pSendUdpData, IAX_FULLFR_OSEQNO) = Sess_iOSeqNo[iIndex];
	iLen= (USHORT)(iDataLen + IAX_FULLFR_DATA);
	if (!(iType == AST_FRAME_IAX && iCommand == IAX_COMMAND_TXACC))
	{
		iax2_frame_save(iIndex, iLen, X_VAL(Adapter_pSendUdpData, IAX_FULLFR_OSEQNO));
		if(!(iType == AST_FRAME_IAX && iCommand == IAX_COMMAND_TXCNT))
		{
			Sess_iOSeqNo[iIndex] ++;
		}
	}
	iax2_debug_string(iType, iCommand);
	iax2_dbg_send(iLen, iIndex);
}
