#include "version.h"
#include "PalmType.h"
#include "p_util.h"
//#include "p_h323.h"
#include "p_tcpip.h"
#include "debug.h"

#ifdef CALL_H323

#define ASN1_DECODE_BUF_LEN		32
static XCHAR _pDecodeBuf[ASN1_DECODE_BUF_LEN];
static XCHAR _iDecodeLen;

static PXCHAR _pMsgBuf;
static PXCHAR _pMsgBufBackup;
static DSHORT _iBitPos, _iBytePos;
static XSHORT _iBitPosBackup;
static XCHAR _iBitOffset;

static XSHORT _iExtensionStart;

void _ConvertByteAndBit()
{
//	_iBytePos = _iBitPos / 8;
//	_iBitOffset = _iBitPos % 8;
	_iBytePos = _iBitPos >> 3;
	_iBitOffset = _iBitPos - (_iBytePos << 3);
}

// padding for octet-aligned
void AsnEncodePadding()
{
	AsnDecodePadding();
}

void AsnDecodePadding()
{
	_ConvertByteAndBit();
	if (_iBitOffset)
	{
		_iBitOffset = 0;
		_iBytePos ++;
//		_iBitPos = _iBytePos * 8;
		_iBitPos = _iBytePos << 3;
	}
}

UCHAR AsnGetLastDecodeLen()
{
	return _iDecodeLen;
}

PXCHAR AsnGetCurrentPointer()
{
	return (PXCHAR)(_pMsgBuf+AsnGetCurrentPos());
}

USHORT AsnGetCurrentPos()
{
	_ConvertByteAndBit();
	return _iBytePos;
}

void AsnSetCurrentPos(USHORT iPos)
{
	_iBytePos = iPos;
	_iBitPos = _iBytePos << 3;
	_iBitOffset = 0;
}

void AsnEncodeStart(PXCHAR pMsgBuf)
{
	AsnDecodeStart(pMsgBuf);
}

USHORT AsnEncodeStop()
{
	AsnEncodePadding();
	return AsnGetCurrentPos();
}

void AsnDecodeStart(PXCHAR pMsgBuf)
{
	_pMsgBuf = pMsgBuf;
	_iBitPos = 0;
}

void AsnDecodeBackup()
{
	_pMsgBufBackup = _pMsgBuf;
	_iBitPosBackup = _iBitPos;
}

void AsnDecodeRestore()
{
	_pMsgBuf = _pMsgBufBackup;
	_iBitPos = _iBitPosBackup;
	_ConvertByteAndBit();
}

void AsnEncodeBOOL(BOOLEAN bVal)
{
	_ConvertByteAndBit();
	if (bVal)
	{
		_pMsgBuf[_iBytePos] |= 1 << (7 - _iBitOffset);
	}
	else
	{
		_pMsgBuf[_iBytePos] &= ~(1 << (7 - _iBitOffset));
	}
	_iBitPos ++;
}

BOOLEAN AsnDecodeBOOL()
{
	_ConvertByteAndBit();
	_iBitPos ++;
	return (BOOLEAN)((_pMsgBuf[_iBytePos] >> (7 - _iBitOffset)) & 1) ;
}

void AsnEncodeUnsignedChar(UCHAR iVal, UCHAR iMin, UCHAR iMax)
{
	DCHAR iNum, iBitNum;
	DSHORT iDataWord, iValWord, iMaskWord;

	iValWord = iVal - iMin;
	iNum = iMax - iMin;
	_ConvertByteAndBit();

	if (iNum == 255)
	{
		AsnEncodePadding();
		_pMsgBuf[_iBytePos] = (UCHAR)iValWord;
		_iBitPos += 8;
		return;
	}
	else if (iNum > 0x7f)	{	iBitNum = 8;	iMaskWord = 0xff;	}
	else if (iNum > 0x3f)	{	iBitNum = 7;	iMaskWord = 0x7f;	}
	else if (iNum > 0x1f)	{	iBitNum = 6;	iMaskWord = 0x3f;	}
	else if (iNum > 0x0f)	{	iBitNum = 5;	iMaskWord = 0x1f;	}
	else if (iNum > 0x7)	{	iBitNum = 4;	iMaskWord = 0x0f;	}
	else if (iNum > 0x3)	{	iBitNum = 3;	iMaskWord = 0x07;	}
	else if (iNum > 0x1)	{	iBitNum = 2;	iMaskWord = 0x03;	}
	else /*if (iNum == 1)*/	{	iBitNum = 1;	iMaskWord = 0x01;	}

	iDataWord = _pMsgBuf[_iBytePos];
	iDataWord <<= 8;
	iDataWord &= ~(iMaskWord << (16 - iBitNum - _iBitOffset));
	iDataWord |= iValWord << (16 - iBitNum - _iBitOffset);
	_iBitOffset += iBitNum;
	_iBitPos += iBitNum;
	if(_iBitOffset > 8)
	{
		_pMsgBuf[_iBytePos] = (UCHAR)(iDataWord >> 8);
		_iBytePos ++;
		_iBitOffset -= 8;
		iDataWord <<= 8;  
	}
	_pMsgBuf[_iBytePos] = (UCHAR)(iDataWord >> 8);
}

UCHAR AsnDecodeUnsignedChar(UCHAR iMin, UCHAR iMax)
{
	DCHAR iNum, iBitNum;
	DSHORT iVal;

	iNum = iMax - iMin;
	_ConvertByteAndBit();

	if (iNum == 255)
	{
		AsnDecodePadding();
		_iBitPos += 8;
		return _pMsgBuf[_iBytePos] + iMin;
	}
	else if (iNum > 0x7f)	iBitNum = 8;
	else if (iNum > 0x3f)	iBitNum = 7;
	else if (iNum > 0x1f)	iBitNum = 6;
	else if (iNum > 0x0f)	iBitNum = 5;
	else if (iNum > 0x7)	iBitNum = 4;
	else if (iNum > 0x3)	iBitNum = 3;
	else if (iNum > 0x1)	iBitNum = 2;
	else if (iNum == 1)		iBitNum = 1;
	else	// 0
	{
		return iMin;
	}

	_iBitPos += iBitNum;
	iVal = _pMsgBuf[_iBytePos];
	iVal <<= 8;
	iVal += _pMsgBuf[_iBytePos + 1];
	iVal <<= _iBitOffset;
	iVal &= 0xff00;
	iNum = (UCHAR)(iVal >> (16 - iBitNum));
	iNum += iMin;
	return iNum;
}

void AsnEncodeUnsignedShort(USHORT iVal, USHORT iMin)
{
	DSHORT iDataWord;

	iDataWord = iVal - iMin;

	AsnEncodePadding();

	_iBitPos += 16;
	_pMsgBuf[_iBytePos] = (UCHAR)((iDataWord & 0xff00) >> 8);
	_iBytePos ++;
	_pMsgBuf[_iBytePos] = (UCHAR)(iDataWord & 0xff);
}

USHORT AsnDecodeUnsignedShort(USHORT iMin)
{
	DSHORT iVal;

	AsnDecodePadding();

	iVal = _pMsgBuf[_iBytePos];
	iVal <<= 8;
	iVal += _pMsgBuf[_iBytePos + 1];

	_iBitPos += 16;

	return iVal + iMin;
}

ULONG AsnDecodeInteger(ULONG lMin)
{
	DCHAR i, iOctetNum;
	DLONG lVal;

	iOctetNum = AsnDecodeUnsignedChar(1, 4);
	AsnDecodePadding();
	lVal = 0;
	for (i = 0; i < iOctetNum; i++)
	{
		lVal = (lVal << 8) + _pMsgBuf[_iBytePos+i];
		_iBitPos += 8;
	}
	lVal += lMin;
	return lVal;
}

void AsnEncodeObjectID(UCHAR iLen, PCCHAR pID)
{
	AsnEncodePadding();

	_pMsgBuf[_iBytePos] = iLen;
	if (iLen)
	{
		p_memcpy_c2x((PXCHAR)(_pMsgBuf+_iBytePos+1), pID, iLen);
	}
	_iBitPos += (iLen + 1)*8;
}

PXCHAR AsnDecodeObjectID()
{
	DCHAR i, iLen;
	PXCHAR_D pBuf;

	AsnDecodePadding();

	iLen = _pMsgBuf[_iBytePos];
	pBuf = (PXCHAR)(_pMsgBuf+_iBytePos);
	for (i = 0; i <= iLen; i ++)
	{
//		pBuf[i] = _pMsgBuf[_iBytePos+i];
		_iBitPos += 8;
	}

	return pBuf;
}

void AsnEncodeBmpString(UCHAR iMin, USHORT iMax, PXCHAR pStr)
{
	DSHORT i, iSize;

	iSize = p_strlen_x(pStr);
	if (iMax <= 256)
	{
		AsnEncodeUnsignedChar((UCHAR)iSize, iMin, (UCHAR)iMax);
	}
	else
	{
		AsnEncodeUnsignedShort(iSize, iMin);
	}
	AsnEncodePadding();

	for (i = 0; i < iSize; i ++)
	{
		_pMsgBuf[_iBytePos++] = 0;
		_pMsgBuf[_iBytePos++] = pStr[i];
		_iBitPos += 16;
	}
}

PXCHAR AsnDecodeBmpString(UCHAR iMin, USHORT iMax)
{
	DSHORT i, iSize;
	PXCHAR_D pBuf;

	_ConvertByteAndBit();
	if (iMax <= 256)
	{
		iSize = AsnDecodeUnsignedChar(iMin, (UCHAR)iMax);
	}
	else
	{
		iSize = AsnDecodeUnsignedShort(iMin);
	}
	iSize <<= 1;
//	OutputVal(iSize, IDS_CHECK_POINT_8);
	AsnDecodePadding();
	pBuf = (PXCHAR)(_pMsgBuf+_iBytePos);
	for (i = 0; i < iSize; i++)
	{
//		pBuf[i] = _pMsgBuf[_iBytePos+i];
		_iBitPos += 8;
		if (i%2)
		{
			DebugChar(_pMsgBuf[_iBytePos+i]);
		}
	}
	DebugNewLine();

	_iDecodeLen = (UCHAR)iSize;
	return pBuf;
}

void AsnEncodeIa5String(UCHAR iMin, UCHAR iMax, PXCHAR pStr)
{
	DCHAR i, j, iSize;
	BOOLEAN bOdd;

	iSize = p_strlen_x(pStr);

	AsnEncodeUnsignedChar(iSize, iMin, iMax);
	AsnEncodePadding();

	if (iSize%2)
	{
		bOdd = TRUE;
		iSize --;
	}
	else
	{
		bOdd = FALSE;
	}

	for (i = 0, j = 0; i < iSize; i += 2, j ++)
	{
		_pMsgBuf[_iBytePos+j] = (Digit_2_E164(pStr[i]) << 4) | Digit_2_E164(pStr[i+1]);
		_iBitPos += 8;
	}
	if (bOdd)
	{
		_pMsgBuf[_iBytePos+j] = (Digit_2_E164(pStr[i]) << 4);
		_iBitPos += 4;
//		iSize ++;
	}
}

PXCHAR AsnDecodeIa5String(UCHAR iMin, UCHAR iMax)
{
	DCHAR i, j, iSize, iE164;
	PXCHAR_D pBuf;
	BOOLEAN bOdd;

	pBuf = _pDecodeBuf;

	iSize = AsnDecodeUnsignedChar(iMin, iMax);
	AsnDecodePadding();
	if (iSize > ASN1_DECODE_BUF_LEN - 1)
	{
		for (i = 0; i < iSize; i ++)
		{
			_iBitPos += 4;
		}
		OutputString(IDS_ASN1_DECODE_EXCEPTION);
		iSize = 0;
		goto End;
	}

	if (iSize%2)
	{
		bOdd = TRUE;
		iSize --;
	}
	else
	{
		bOdd = FALSE;
	}
	for (i = 0, j = 0; i < iSize; i += 2, j ++)
	{
		iE164 = _pMsgBuf[_iBytePos+j] >> 4;
		pBuf[i] = E164_2_Digit(iE164);
		iE164 = _pMsgBuf[_iBytePos+j] & 0x0f;
		pBuf[i+1] = E164_2_Digit(iE164);
		_iBitPos += 8;
	}
	if (bOdd)
	{
		iE164 = _pMsgBuf[_iBytePos+j] >> 4;
		pBuf[i] = E164_2_Digit(iE164);
		_iBitPos += 4;
		iSize ++;
	}
End:
	pBuf[iSize] = 0;

	_iDecodeLen = iSize;
	return pBuf;
}

void AsnEncodeOctetString(UCHAR iMin, UCHAR iMax, PXCHAR pOctet)
{
	_ConvertByteAndBit();
	if (iMin)
	{
	}
	else
	{
		if (iMax)
		{
			if (iMax == 2)
			{	// OCTET STRING (SIZE(2)) No-octet-aligned
			}
			else
			{	// OCTET STRING (SIZE(16)) Octet-aligned
				AsnEncodePadding();
				p_memcpy_x2x((PXCHAR)(_pMsgBuf+_iBytePos), pOctet, iMax);
				_iBitPos += 8*iMax;
			}
		}
		else
		{	// OCTET STRING
		}
	}
}

PXCHAR AsnDecodeOctetString(UCHAR iMin, UCHAR iMax)
{
	DSHORT i, iSize;
	PXCHAR_D pBuf;
	DSHORT iVal3;
	XLONG lVal, lVal2;

	_ConvertByteAndBit();
	if (iMin)
	{	// OCTET STRING (SIZE(1..20)) ????????maybe to be modified
		iSize = AsnDecodeUnsignedChar(iMin, iMax);
		AsnDecodePadding();
		pBuf = (PXCHAR)(_pMsgBuf+_iBytePos);
		for (i = 0; i < iSize; i++)
		{
//			pBuf[i] = _pMsgBuf[_iBytePos+i];
			_iBitPos += 8;
		}
	}
	else
	{
		if (iMax)
		{
			if (iMax == 2)
			{	// OCTET STRING (SIZE(2)) No-octet-aligned
				if (_iBitOffset)
				{
					lVal = _pMsgBuf[_iBytePos];
					lVal <<= 24;
					lVal2 = _pMsgBuf[_iBytePos+1];
					lVal2 <<= 16;
					lVal += lVal2;
					iVal3 = _pMsgBuf[_iBytePos+2];
					iVal3 <<= 8;
					lVal += iVal3;
					lVal <<= _iBitOffset;
					pBuf = _pDecodeBuf;
					pBuf[0] = (UCHAR)((lVal & 0xff000000) >> 24);
					pBuf[1] = (UCHAR)((lVal & 0x00ff0000) >> 16);
				}
				else
				{
					pBuf = (PXCHAR)(_pMsgBuf+_iBytePos);
//					pBuf[0] = _pMsgBuf[_iBytePos];
//					pBuf[1] = _pMsgBuf[_iBytePos+1];
				}
				_iBitPos += 16;
			}
			else
			{	// OCTET STRING (SIZE(16)) Octet-aligned
				AsnDecodePadding();
				pBuf = (PXCHAR)(_pMsgBuf+_iBytePos);
				for (i = 0; i < iMax; i++)
				{
//					pBuf[i] = _pMsgBuf[_iBytePos+i];
					_iBitPos += 8;
				}
			}
		}
		else
		{	// OCTET STRING
			AsnDecodePadding();
			iSize = AsnDecodeShortInteger();
			_ConvertByteAndBit();
			pBuf = (PXCHAR)(_pMsgBuf+_iBytePos);
			for (i = 0; i < iSize; i++)
			{
//				pBuf[i] = _pMsgBuf[_iBytePos+i];
				_iBitPos += 8;
			}
		}
	}

	_iDecodeLen = (UCHAR)iSize;
	return pBuf;
}

void AsnEncodeExtensionsNumber(UCHAR iNum)
{
	AsnEncodeUnsignedChar(iNum, 1, 128);
}

UCHAR AsnDecodeExtensions(PXCHAR pExtensions, BOOLEAN bSkipAll)
{
	DCHAR i, iLen;
	PXCHAR_D pCur;

	pCur = pExtensions;

	p_memset_x(pCur, 0, MAX_ASN1_EXTENSION_NUM); 
	iLen = AsnDecodeUnsignedChar(1, 128);
	for (i = 0; i < ((iLen <= MAX_ASN1_EXTENSION_NUM) ? iLen : MAX_ASN1_EXTENSION_NUM); i ++)
	{
		pCur[i] = AsnDecodeBOOL() ? TRUE : FALSE;
	}
	for (; i < iLen; i ++)
	{
		OutputVal(i, IDS_ASN1_TOO_MANY_EXTENSIONS);
		AsnDecodeBOOL();
	}

	if (bSkipAll)
	{
		for (i = 0; i < iLen; i ++)
		{
			AsnSkipExtension(pCur[i]);
		}
	}

	return iLen;
}

void AsnSkipExtension(UCHAR iExtension)
{
	DSHORT iLen;

	if (iExtension)	
	{
		iLen = AsnDecodeExtensionLength();
		AsnSetCurrentPos((USHORT)(AsnGetCurrentPos() + iLen));
	}
}

void AsnSkipChoiceExtension()
{
	DSHORT iLen;

	iLen = AsnDecodeExtensionLength();
	AsnSetCurrentPos((USHORT)(AsnGetCurrentPos() + iLen));
}

void AsnEncodeShortInteger(USHORT iLen)
{
	if (iLen < 0x80)
	{
		AsnEncodeUnsignedChar((UCHAR)iLen, 0, 255);
	}
}

USHORT AsnDecodeShortInteger()
{
	DSHORT iLen;

	iLen = AsnDecodeUnsignedChar(0, 255);
	if (iLen < 0x80)	return iLen;

	iLen -= 0x80;
	iLen <<= 8;
	iLen += AsnDecodeUnsignedChar(0, 255);
	return iLen;
}

void AsnEncodeExtensionStart()
{
	AsnEncodeExtensionLength(0);
	_ConvertByteAndBit();
	_iExtensionStart = _iBytePos;
}

void AsnEncodeExtensionStop()
{
	AsnEncodePadding();
	_pMsgBuf[_iExtensionStart-1] = _iBytePos - _iExtensionStart;	
}

void AsnEncodeExtensionLength(USHORT iLen)
{
	AsnEncodeShortInteger(iLen);
}

USHORT AsnDecodeExtensionLength()
{
	return AsnDecodeShortInteger();
}

void AsnEncodeSequenceOfNumber(UCHAR iNum)
{
	AsnEncodeUnsignedChar(iNum, 0, 255);
}

UCHAR AsnDecodeSequenceOfNumber()
{
	return AsnDecodeUnsignedChar(0, 255);
}

void AsnEncodeChoice(UCHAR iIndex, UCHAR iFixed, BOOLEAN bHasExtension)
{
	BOOLEAN bExtension;

	bExtension = (iIndex > iFixed) ? TRUE : FALSE;
	if (bHasExtension)
	{
		AsnEncodeBOOL(bExtension);
	}

	if (bExtension)
	{
		AsnEncodeUnsignedChar((UCHAR)(iIndex-iFixed-1), 0, 127);
	}
	else
	{
		AsnEncodeUnsignedChar(iIndex, 0, iFixed);
	}
}

UCHAR AsnDecodeChoice(UCHAR iFixed, BOOLEAN bHasExtension)
{
	DCHAR iIndex;
	BOOLEAN bExtension;

	if (bHasExtension)
	{
		bExtension = AsnDecodeBOOL();
	}
	else
	{
		bExtension = FALSE;
	}

	if (bExtension)
	{
		iIndex = AsnDecodeUnsignedChar(0, 127);
		iIndex += iFixed + 1;
	}
	else
	{
		iIndex = AsnDecodeUnsignedChar(0, iFixed);
	}
	return iIndex;
}

UCHAR AsnDecodeSetSize(UCHAR iMin, UCHAR iMax)
{
	return AsnDecodeUnsignedChar(iMin, iMax);
}

BOOLEAN IsDisplayChar(UCHAR iChar)
{
	if (iChar < 0x20)	return FALSE;
	if (iChar > 0x7e)	return FALSE;
	return TRUE;
}


#define E164_MAX	14
static CCHAR _cE164Map[] = "#*,0123456789.@";

BOOLEAN IsE164Char(UCHAR iChar)
{
	DCHAR i;
	for (i = 0; i <= E164_MAX; i ++)
	{
		if (iChar == _cE164Map[i])
		{
			return TRUE;
		}
	}
	return FALSE;
}


UCHAR Digit_2_E164(UCHAR iDigit)
{
	DCHAR i;
	for (i = 0; i <= E164_MAX; i ++)
	{
		if (iDigit == _cE164Map[i])
		{
			return i;
		}
	}
	return 0x0d;
}


UCHAR E164_2_Digit(UCHAR iE164Val)
{
	DCHAR iE164;

	iE164 = iE164Val;
	if (iE164 > E164_MAX)	return 0;

	return _cE164Map[iE164];
}

#endif
