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

#define SIP_REG_IDLE	0
#define SIP_REG_ADD		1
#define SIP_REG_UPDATE	2
#define SIP_REG_REMOVE	3

#define SIP_REG_RETRY	60

static XCHAR _iAuthRetry;
static XSHORT _iNatTimer;
static XCHAR _iRegState;
static XSHORT _iRegTimer;
static XSHORT _iRegTTL;
static XLONG _lRegSeq;


void _RegSend()
{
	DCHAR iTransNo;

	HttpClearAuth();
	if (SdramReadString(PROTOCOL_SDRAM_POS+SIP_REG_WWW_AUTH))
	{
		SipGenAuth(SIP_REQ_REGISTER, SIP_REG_REQUEST_URI, SIP_REG_WWW_AUTH, HTTP_AUTH_TYPE_WWW);
	}
	if (SdramReadString(PROTOCOL_SDRAM_POS+SIP_REG_PROXY_AUTH))
	{
		SipGenAuth(SIP_REQ_REGISTER, SIP_REG_REQUEST_URI, SIP_REG_PROXY_AUTH, HTTP_AUTH_TYPE_PROXY);
	}

	SipFillReqLine(SIP_REQ_REGISTER, SIP_REG_REQUEST_URI);
	SipNewBranch(SIP_CUR_BRANCH);
	SipFillVia(SIP_CUR_BRANCH);
	SipFillMaxForwards();
	SipFillUserAgent();

	SipNewLocalUri(SIP_REG_LOCAL_URI);
	if (!SdramReadData(PROTOCOL_SDRAM_POS+SIP_REG_FROM_TAG))
	{
		SipNewTag(SIP_REG_FROM_TAG);
	}
	SipFillFrom(SIP_REG_LOCAL_URI, SIP_REG_FROM_TAG);
	
	SdramWriteData(PROTOCOL_SDRAM_POS+SIP_TEMP_BUF, 0);
	SipFillTo(SIP_REG_LOCAL_URI, SIP_TEMP_BUF);

	if (!SdramReadData(PROTOCOL_SDRAM_POS+SIP_REG_CALL_ID))
	{
		SipNewCallId(SIP_REG_CALL_ID);
		_lRegSeq = (ULONG)p_rand();
	}
	SipFillCallId(SIP_REG_CALL_ID);
	
	SipFillCSeq(_lRegSeq, SIP_REQ_REGISTER);
	_lRegSeq ++;

	if (_iRegState == SIP_REG_REMOVE)
	{
		p_line_add_c((PCCHAR)"Contact: *");
		p_line_add_newline();
		p_line_add_c((PCCHAR)"Expires: 0");
		p_line_add_newline();
	}
	else
	{
		SipFillContact();
		SipFillExpires(_iRegTTL);
	}

	SipFillProxyRequire();
	SipFillAuth();
	SipFillContentLength(0);
	p_line_add_newline();

	iTransNo = SipAllocTrans(TRUE);
	SipCtSendReq(SIP_REQ_REGISTER, SIP_REG_UDP_HEAD, iTransNo);
	_iRegTimer = 0;
	_iNatTimer = 0;
}

void _RegInit()
{
	_iRegState = SIP_REG_IDLE;
	_iRegTimer = 0;
	SdramWriteData(PROTOCOL_SDRAM_POS+SIP_REG_WWW_AUTH, 0);
	SdramWriteData(PROTOCOL_SDRAM_POS+SIP_REG_PROXY_AUTH, 0);
	SdramWriteData(PROTOCOL_SDRAM_POS+SIP_REG_FROM_TAG, 0);
}

void _RegFailed()
{
	TaskLogonFailed(0);
	_RegInit();
	_iNatTimer = 0;
	_iAuthRetry = 0;
	_iRegTTL = Sys_iRegisterTTL;
}

void _GetRegTTL()
{
	PXCHAR_D pCur;
	if (SdramReadString(PROTOCOL_SDRAM_POS+SIP_RECV_EXPIRES))
	{
		_iRegTTL = p_atoi((PXCHAR)Sdram_pDataCache, 10);
		return;
	}

	if (SdramReadString(PROTOCOL_SDRAM_POS+SIP_RECV_CONTACT))
	{
		pCur = p_strchr_x((PXCHAR)Sdram_pDataCache, ';');
		if (pCur)
		{
			pCur ++;
			while (*pCur)
			{
				if (p_memcmp_nocase_c2x(pCur, (PCCHAR)"expires=", 8))
				{
					pCur += 8;
					_iRegTTL = p_atoi(pCur, 10);
					break;
				}
				pCur ++;
			}
		}
	}
/*
There was one system like UPTECH, that you send 60 , it will return 59
End:
	if (_iRegTTL < Sys_iRegisterTTL)
	{
		_iRegTTL = Sys_iRegisterTTL;
	}
*/
}

void SipRegStartup()
{
	_RegInit();
	_iNatTimer = 0;
	_iAuthRetry = 0;
	_iRegTTL = Sys_iRegisterTTL;
	SdramWriteData(PROTOCOL_SDRAM_POS+SIP_REG_CALL_ID, 0);
//	SdramWriteData(PROTOCOL_SDRAM_POS+SIP_REG_FROM_TAG, 0);

	p_line_start((PXCHAR)Sdram_pDataCache);
	p_line_add_c(_cSipColon);
	p_line_add_x(Sys_pServiceID);
	SdramWriteString(PROTOCOL_SDRAM_POS+SIP_REG_REQUEST_URI);
}

void SipRegHandleTxError()
{
	_RegFailed();
}

void SipStartReg()
{
	UdpOpen(Sys_pServiceIP, Sys_iServicePort, Sys_iRegisterPort, SIP_REG_UDP_HEAD);
	_iRegState = SIP_REG_ADD;
	_RegSend();
}

BOOLEAN _IsBehindNat()
{
	BOOLEAN bPublicLocal;
	BOOLEAN bPublicService;

	bPublicLocal = IsPublicIP(Local_pIPAddress);
	bPublicService = IsPublicIP(Sys_pServiceIP);

	if (bPublicLocal && bPublicService)
		return FALSE;
	if (!bPublicLocal && !bPublicService)
		return FALSE;
	return TRUE;
}

void SipRegHandleResp(USHORT iVal)
{
	DSHORT iResp;

	iResp = iVal;

	if (_iRegState == SIP_REG_IDLE)
		return;

	if (!SipCmpSdram(SIP_REG_FROM_TAG, SIP_RECV_FROM_TAG) || !SipCmpSdram(SIP_REG_CALL_ID, SIP_RECV_CALL_ID))
	{
//		DebugCString((PCCHAR)"From tag or call id mismatch");
		OutputString(IDS_SIP_TAG_OR_CALL_ID);
		return;
	}

	if (iResp < 200)	return;

	if (iResp == 401 || iResp == 407)
	{
		if (_iAuthRetry >= SIP_AUTH_MAX_RETRY)
		{
			OutputString(IDS_SIP_WWW_AUTH_FAIL);
			_iAuthRetry = 0;
			_RegFailed();
		}
		else
		{
			OutputString(IDS_SIP_WWW_AUTH);
			_iAuthRetry ++;
			_iRegTimer = 0;
			SipCopyAuth(SIP_REG_WWW_AUTH, SIP_REG_PROXY_AUTH);
			_RegSend();
		}
		return;
	}

	if (_iAuthRetry)
	{
		_iAuthRetry = 0;
	}

	if (iResp == 301 || iResp == 302)
	{
		Sys_iServicePort = SipGetIPPort(Sys_pServiceIP, SIP_RECV_CONTACT);
		if (Sys_iServicePort)
		{
			Task_bLogoned = FALSE;
			SipLogon();
		}
		else
		{
			_RegFailed();
		}
		return;
	}

	if (iResp == 423)
	{
		// Get Min-Expires and resend REGISTER
		_iRegTimer = 0;
		SdramReadString(PROTOCOL_SDRAM_POS+SIP_RECV_MIN_EXPIRES);
		_iRegTTL = p_atoi((PXCHAR)Sdram_pDataCache, 10);
		_RegSend();
		return;
	}

	if (iResp >= 300)
	{
		switch (_iRegState)
		{
		case SIP_REG_ADD:
			OutputString(IDS_SIP_REG_ADD_FAIL);
			_RegFailed();
			break;
		case SIP_REG_UPDATE:
			OutputString(IDS_SIP_REG_UPDATE_FAIL);
			_RegFailed();
			break;
		case SIP_REG_REMOVE:
			OutputString(IDS_SIP_REG_REMOVE_FAIL);
			break;
		}
	}
	else if (iResp >= 200)
	{
		_GetRegTTL();
		switch (_iRegState)
		{
		case SIP_REG_ADD:
			OutputString(IDS_SIP_REG_ADD_OK);
			TaskLogonCompleted();
			break;
		case SIP_REG_UPDATE:
			OutputString(IDS_SIP_REG_UPDATE_OK);
			break;
		case SIP_REG_REMOVE:
			OutputString(IDS_SIP_REG_REMOVE_OK);
			TaskLogoffCompleted();
			break;
		}
	}
	_RegInit();
}

void SipRegHandleTimer()
{
	if (!Sys_bUseService)	return;

	if (_iRegState == SIP_REG_IDLE)
	{
		_iRegTimer ++;
		if (Task_bLogoned)
		{
			if (_iRegTimer >= (USHORT)((_iRegTTL >> 2) * 3))
			{
				_iRegState = SIP_REG_UPDATE;
				_iRegTTL = Sys_iRegisterTTL;
				UdpOpen(Sys_pServiceIP, Sys_iServicePort, Sys_iRegisterPort, SIP_REG_UDP_HEAD);
				_RegSend();
			}
		}
		else
		{
			if (_iRegTimer >= SIP_REG_RETRY)
			{
				_iRegTimer = 0;
				_iRegTTL = Sys_iRegisterTTL;
				TaskLogon();
			}
		}
	}

	if (Task_bLogoned && Sys_iNatTTL && _IsBehindNat())
	{
		_iNatTimer ++;
		if (_iNatTimer >= Sys_iNatTTL)
		{
			p_memset_x(Sip_pDataOut, 0, 16);
			UdpSend(16, SIP_REG_UDP_HEAD);
			_iNatTimer = 0;
		}
	}

}

/**********************************************************************************
****** SIP API functions
***********************************************************************************/
 void SipLogon()
{
	if (Task_bLogoned)	return;

	SipRegStartup();

	if (IsValidIP(Sys_pServiceIP))
	{
		SipStartReg();
	}
	else
	{
		TaskLogonFailed(0);
	}
}

 void SipLogoff()
{
	if (!Task_bLogoned)		return;

	if (Sys_iServiceType != SIP_SERVICE_TYPE_NET2PHONE)
	{
		_iRegState = SIP_REG_REMOVE;
		_RegSend();
	}
	Task_bLogoned = FALSE;
}
