#include "EncDecTriTci.h"
#include <malloc.h>
#include <memory.h>

#ifdef _WIN32
	#ifdef _MSC_VER
		#if _MSC_VER >= 1600
			#include <stdint.h>
		#else
			typedef short int16_t;
			typedef int int32_t;
			typedef __int64 int64_t;
		#endif
	#else
		#include <stdint.h>
	#endif
#else
#include <stdint.h>
#endif



int decodeDouble(double * pDbl, const char * pData, int nLen, int nOffset)
{
	int nDblLen = sizeof(double);
	if (nOffset < 0)
		return -1;
	
	memset(pDbl, 0, nDblLen);
	
	if (nOffset + nDblLen > nLen)
		return -1; // not enough bytes

	memcpy(pDbl, &pData[nOffset], nDblLen);
	return nOffset + nDblLen;
}

int decodeTestCaseIdList(TciTestCaseIdListType *pList, const char * pData, int nLen, int nOffset)
{
	int nIdCount = 0;
	if (nOffset < 0)
		return -1;
	memset(pList, 0, sizeof(TciTestCaseIdListType));
	
	nOffset = decodeInt(&nIdCount, pData, nLen, nOffset);

	if (nOffset >= 0 && nIdCount > 0)
	{
		int i;
		pList->length = nIdCount;
		pList->idList = (QualifiedName *)malloc(sizeof(QualifiedName) * nIdCount);
		memset(pList->idList, 0, sizeof(QualifiedName) * nIdCount);
		for (i = 0; i < nIdCount && nOffset >= 0; i++)
		{
			nOffset = decodeQualifiedName(&pList->idList[i], pData, nLen, nOffset);
		}
	}
	return nOffset;
}

int decodeTciModuleIdList(TciModuleIdListType *pList, const char * pData, int nLen, int nOffset)
{
	return decodeTestCaseIdList(pList, pData, nLen, nOffset);
}

int decodeTciTypeClass(TciTypeClass *typeCls, const char * pData, int nLen, int nOffset)
{
	int tmp;
	nOffset = decodeInt(&tmp, pData, nLen, nOffset);
	if(tmp == 0)//Address,
		*typeCls = TCI_ADDRESS_TYPE;
	else if(tmp == 1)// Anytype,
		*typeCls = TCI_ANYTYPE_TYPE;
	else if(tmp == 2)// Bitstring,
		*typeCls = TCI_BITSTRING_TYPE;
	else if(tmp == 3)// BooleanType,
		*typeCls = TCI_BOOLEAN_TYPE;
	else if(tmp == 4)// CharType,
		*typeCls = TCI_CHAR_TYPE;
	else if(tmp == 5)// Charstring,
		*typeCls = TCI_CHARSTRING_TYPE;
	else if(tmp == 6)// Component,
		*typeCls = TCI_COMPONENT_TYPE;
	else if(tmp == 7)// Enumerated,
		*typeCls = TCI_ENUMERATED_TYPE;
	else if(tmp == 8)// Float,
		*typeCls = TCI_FLOAT_TYPE;
	else if(tmp == 9)// Hexstring,
		*typeCls = TCI_HEXSTRING_TYPE;
	else if(tmp == 10)// IntegerType,
		*typeCls = TCI_INTEGER_TYPE;
	else if(tmp == 11)// Objid,
		*typeCls = TCI_OBJID_TYPE;
	else if(tmp == 12)// Octetstring,
		*typeCls = TCI_OCTETSTRING_TYPE;
	else if(tmp == 13)// Record,
		*typeCls = TCI_RECORD_TYPE;
	else if(tmp == 14)// RecordOf,
		*typeCls = TCI_RECORD_OF_TYPE;
	else if(tmp == 15)// SetType,
		*typeCls = TCI_SET_TYPE;
	else if(tmp == 16)// SetOf,
		*typeCls = TCI_SET_OF_TYPE;
	else if(tmp == 17)// Union,
		*typeCls = TCI_UNION_TYPE;
	else if(tmp == 18)// UniversalChar,
		*typeCls = TCI_UNIVERSAL_CHAR_TYPE;
	else if(tmp == 19)// UniversalCharstring,
		*typeCls = TCI_UNIVERSAL_CHARSTRING_TYPE;
	else if(tmp == 20)// Verdict,
		*typeCls = TCI_VERDICT_TYPE;
	else //if(tmp == 21)// Array
		*typeCls = TCI_ARRAY_TYPE;
	//*typeCls = tmp;
	return nOffset;
}

int decodeTciValue(Value *pVal, const char * pData, int nLen, int nOffset)
{
	if( nOffset + TCI_VALUE_LENGTH > nLen)
	{
		*pVal = 0;
		return -1;
	}
	*pVal = malloc(TCI_VALUE_LENGTH);
	memcpy(*pVal, &pData[nOffset], TCI_VALUE_LENGTH);
	return nOffset + TCI_VALUE_LENGTH;
}

int decodeTciType(Type *pType,const char *pData, int nLen, int nOffset)
{
	if(nOffset < 0 || nOffset + TCI_TYPE_LENGTH > nLen)
	{
		*pType = 0;
		return -1;
	}
	*pType = malloc(TCI_TYPE_LENGTH);
	memcpy(*pType, &pData[nOffset], TCI_TYPE_LENGTH);
	return nOffset + TCI_TYPE_LENGTH;
}

int decodeTciExtension(String ** paStr, const char * pData, int nLen, int nOffset)
{
	//null terminated string array...
	int nItems = 0, i;
	String *pArr;
	nOffset = decodeInt(&nItems, pData, nLen, nOffset);
	pArr = malloc(sizeof(String) * (nItems + 1));
	for(i = 0; i < nItems; i++)
	{
		String pStr;
		nOffset = decodeString(&pStr, pData, nLen, nOffset);
		pArr[i] = pStr;
	}
	pArr[nItems] = 0;
	*paStr = pArr;
	return nOffset;
}

int decodeLongLong(long long *ll, const char* pData, int nLen, int nOffset)
{
	if(nOffset < 0 || nOffset + 8 > nLen)
	{
		*ll = 0;
		return -1;
	}
	memcpy(ll, &pData[nOffset], 8);
	return nOffset + 8;
}

int decodeTciParameterTypeType(TciParameterTypeType *tt, const char* pData, int nLen, int nOffset)
{
	nOffset = decodeTciType(&tt->parameterType, pData, nLen, nOffset);
	if(nOffset < 0 || nOffset + 1 > nLen)
		return -1;
	tt->mode = (TciParameterPassingModeType)pData[nOffset++];
	return nOffset;
}

int decodeTciParameterTypeList(TciParameterTypeListType *parList, const char* pData, int nLen, int nOffset)
{
	char present = 0x00;
	if(nOffset < 0 || nOffset + 1 > nLen)
		return -1;
	present = pData[nOffset++];

	parList->length = 0;
	parList->parList = 0;

	if(present)
	{
		int nListLen = 0;
		long i;
		nOffset = decodeInt(&nListLen, pData, nLen, nOffset);
		parList->length = nListLen;
		if(parList->length)
		{
			parList->parList = (TciParameterTypeType*)malloc(sizeof(TciParameterTypeType) * parList->length);
			memset(parList->parList, 0, sizeof(TciParameterTypeType) * parList->length);
		}

		for(i = 0; i < parList->length; i++)
			nOffset = decodeTciParameterTypeType(&parList->parList[i], pData, nLen, nOffset);
	}
	return nOffset;
}

int decodeTciModuleParameterList(TciModuleParameterListType * parList, const char* pData, int nLen, int nOffset)
{
	int nListLen = 0;
	long i;
	if(nOffset < 0 || nOffset+1 > nLen)
		return -1;

	nOffset = decodeInt(&nListLen, pData, nLen, nOffset);
	parList->length = nListLen;
	if(parList->length)
	{
		parList->modParList = (TciModuleParameterType*) malloc(sizeof(TciModuleParameterType) * parList->length);
		memset(parList->modParList, 0, sizeof(TciModuleParameterType) * parList->length);
	}

	for(i = 0; i < parList->length; i++)
	{
		nOffset = decodeString(&(parList->modParList[i].parName), pData, nLen, nOffset);

		if(nOffset < 0 || nOffset +1 > nLen)
			return -1;
		
		if(pData[nOffset++])
		{
			nOffset = decodeTciValue(&(parList->modParList[i].defaultValue), pData, nLen, nOffset);
			nOffset += 4;//i have nowhere to this class Id...
		}
		else
			parList->modParList[i].defaultValue = 0;
	}
	return nOffset;
}

int decodeTciParamList(TciParameterListType *tciParList, const char * pData, int nLen, int nOffset)
{
	long i;
	int nDataLen;
	if(nOffset < 0 || nOffset+1 > nLen)
		return -1;

	nOffset = decodeInt(&nDataLen, pData, nLen, nOffset);
	tciParList->length = nDataLen;
	if(tciParList->length)
	{
		tciParList->parList = malloc(sizeof(TciParameterType) * tciParList->length);
		memset(tciParList->parList, 0, sizeof(TciParameterType) * tciParList->length);
	}
	
	for(i = 0; i < tciParList->length; i++)
	{
		nOffset = decodeString(&(tciParList->parList[i].parName), pData, nLen, nOffset);

		if(nOffset < 0 || nOffset + 2 > nLen)
			return -1;
		tciParList->parList[i].parPassMode = pData[nOffset++];

		if(pData[nOffset++])
		{
			nOffset = decodeTciValue(&(tciParList->parList[i].parValue), pData, nLen, nOffset);
			nOffset += 4;//i have nowhere to this class Id...
		}
		else
			tciParList->parList[i].parValue = 0;
	}
	return nOffset;
}
int decodeVerdictValue(VerdictValue *ver, const char * pData, int nLen, int nOffset)
{
	nOffset = decodeTciValue(ver, pData, nLen, nOffset);
	return 	nOffset + 4;//skip the type class
}

String decodeSimpleStringReply(AbstractTriAckMessage * msg)
{
	String sVal;
	if(!msg)
		return 0;
	decodeString(&sVal, msg->pData, msg->nLen, ACK_HEADER_V3_LENGTH);
	disposeAbstractMsg(msg);
	return sVal;
}

double decodeDoubleReply(AbstractTriAckMessage * msg)
{
	double dbl;
	if(!msg)
		return 0.0f;
	decodeDouble(&dbl, msg->pData, msg->nLen, ACK_HEADER_V3_LENGTH);
	disposeAbstractMsg(msg);
	return dbl;
}

Boolean decodeBoolReply(AbstractTriAckMessage * msg)
{
	Boolean bVal = 0x00;
	if(!msg)
		return 0x00;
	if(msg->nLen > ACK_HEADER_V3_LENGTH)
		bVal = msg->pData[ACK_HEADER_V3_LENGTH];
	disposeAbstractMsg(msg);
	return bVal;
}

Value decodeValueReply(AbstractTriAckMessage * msg)
{
	Value ret = 0;
	char isPresent = 0x00;

	if(!msg)
		return 0;
	isPresent = msg->pData[ACK_HEADER_V3_LENGTH];
	if(isPresent)
		decodeTciValue(&ret, msg->pData, msg->nLen, ACK_HEADER_V3_LENGTH + 1);
	else
	{
		ret = malloc(TCI_VALUE_LENGTH);
		memset(ret, 0, TCI_VALUE_LENGTH);
	}
	disposeAbstractMsg(msg);
	return ret;
}

Value decodeValueReplyWithBitPosition(AbstractTriAckMessage * msg, BinaryString *pBS)
{
	Value ret = 0;
	char isPresent = 0x00;

	if(!msg)
		return 0;
	isPresent = msg->pData[ACK_HEADER_V3_LENGTH];
	if(isPresent)
	{
		int nOffset = decodeTciValue(&ret, msg->pData, msg->nLen, ACK_HEADER_V3_LENGTH + 1);
		if(pBS && msg->nLen >= nOffset + 4)
		{
			int nBitOffset = 0;
			memcpy(&nBitOffset, &msg->pData[nOffset], sizeof(int32_t));
			setBinaryStringStartingPosition(pBS, nBitOffset);
		}
	}
	else
	{
		ret = malloc(TCI_VALUE_LENGTH);
		memset(ret, 0, TCI_VALUE_LENGTH);
	}
	disposeAbstractMsg(msg);
	return ret;
}

BinaryString decodeBinaryStringReply(AbstractTriAckMessage *msg)
{
	BinaryString ret;
	char isPresent;
	ret.aux = 0;
	ret.bits = 0;
	ret.data = 0;
	

	if(!msg)
		return ret;

	isPresent = msg->pData[ACK_HEADER_V3_LENGTH];
	if(isPresent)
	{
		int nOffset = 0, nAdditionalStructVersion;
		nOffset = decodeBinaryString(&ret, msg->pData, msg->nLen, ACK_HEADER_V3_LENGTH + 1, 1);
		nOffset = decodeInt(&nAdditionalStructVersion, msg->pData, msg->nLen, nOffset);
		if(nAdditionalStructVersion == 1)
		{
			int nBitPosition;
			void * data = malloc(sizeof(int32_t) + sizeof(AdditionalDataV1));
			memset(data, 0, sizeof(int32_t) + sizeof(AdditionalDataV1));
			memcpy(data, &nAdditionalStructVersion, sizeof(int32_t));
			nOffset = decodeInt(&nBitPosition, msg->pData, msg->nLen, nOffset);
			ret.aux = data;
			setBinaryStringStartingPosition(&ret, nBitPosition);
		}
	}
	disposeAbstractMsg(msg);
	return ret;
}

Type decodeTypeReply(AbstractTriAckMessage * msg)
{
	Type ret;
	if(!msg)
		return 0;
	decodeTciType(&ret, msg->pData, msg->nLen, ACK_HEADER_V3_LENGTH);
	disposeAbstractMsg(msg);
	return ret;
}

char ** decodeStringArrayReply(AbstractTriAckMessage * msg)
{
	char **pArr;
	if(!msg)
		return 0;
	decodeTciExtension(&pArr, msg->pData, msg->nLen, ACK_HEADER_V3_LENGTH);
	disposeAbstractMsg(msg);
	return pArr;
}

unsigned long int decodeNumericReply(AbstractTriAckMessage * msg)
{
	int uli = 0;
	if(!msg)
		return 0;
	decodeInt(&uli, msg->pData, msg->nLen, ACK_HEADER_V3_LENGTH);
	disposeAbstractMsg(msg);
	return uli;
}
TciUCStringValue decodeUCStringReply(AbstractTriAckMessage * msg)
{
	TciUCStringValue ucsVal;
	int nOffset, streamLen;
	ucsVal.length = 0;
	ucsVal.string = 0;
	if(!msg)
		return ucsVal;
	nOffset = decodeInt(&streamLen, msg->pData, msg->nLen, ACK_HEADER_V3_LENGTH);
	if(streamLen > 0)
	{
		
		ucsVal.length = streamLen / 2;
		ucsVal.string = (wchar_t*)malloc(ucsVal.length * sizeof(wchar_t));
		if (sizeof(wchar_t) == 2)
			memcpy(ucsVal.string, &msg->pData[nOffset], streamLen);
		else
		{
			unsigned int i;
			for (i = 0; i < ucsVal.length; i++)
			{
				int16_t nChar;
				memcpy(&nChar, &msg->pData[nOffset], 2);
				nOffset += 2;
				ucsVal.string[i] = (wchar_t)nChar;
			}
		}
	}
	disposeAbstractMsg(msg);
	return ucsVal;
}

TciUCStringValue decodeUStringBufferReply(AbstractTriAckMessage * msg)
{
	TciUCStringValue ucsVal;
	int nOffset, strLen;
	ucsVal.length = 0;
	ucsVal.string = 0;
	if(!msg)
		return ucsVal;
	nOffset = decodeInt(&strLen, msg->pData, msg->nLen, ACK_HEADER_V3_LENGTH);
	if(strLen > 0)
	{
		unsigned int i = 0;
		unsigned int nOffset = 0;
		int buffLen = strLen * sizeof(wchar_t);
		ucsVal.string = (wchar_t *)malloc(buffLen);
		ucsVal.length = strLen;
		if (sizeof(wchar_t) == 4)
		{
			memcpy(ucsVal.string, &msg->pData[nOffset], buffLen);
			nOffset += buffLen;
		}
		else
		{
			for(i = 0; i < ucsVal.length; i++)
			{
				int32_t nChar;
				memcpy(&nChar, &msg->pData[nOffset], 4);
				nOffset += 4;
				ucsVal.string[i] = (wchar_t)nChar;
			}
		}
	}
	disposeAbstractMsg(msg);
	return ucsVal;
}

TciObjidValue decodeObjectidReply(AbstractTriAckMessage * msg)
{
	TciObjidValue retVal;
	int nOffset, nDataLen;
	unsigned long i;
	char present = 0x00;
	retVal.elements = 0;
	retVal.length = 0;
	if(!msg)
		return retVal;

	memset(&retVal, 0, sizeof(TciObjidValue));
	present = msg->pData[ACK_HEADER_V3_LENGTH];
	nOffset = ACK_HEADER_V3_LENGTH + 1;
	if(!present)
	{
		disposeAbstractMsg(msg);
		return retVal;
	}

	nOffset = decodeInt(&nDataLen, msg->pData, msg->nLen, nOffset);
	retVal.length = nDataLen;
	if(retVal.length)
	{
		retVal.elements = malloc(retVal.length * sizeof(TciObjidElemValue));
		memset(retVal.elements, 0, retVal.length * sizeof(TciObjidElemValue));
	}

	for(i = 0; i < retVal.length; i++)
	{
		//unsigned short tmp;
		TciObjidElemValue * elem = &retVal.elements[i];
		nOffset = decodeString(&elem->elem_as_ascii, msg->pData, msg->nLen, nOffset);
		elem->aux = 0;
		memcpy(&elem->elem_as_number, &msg->pData[nOffset], 2);
		nOffset += 2;
		
	}
	disposeAbstractMsg(msg);
	return retVal;
}

long long decodeLongIntReply(AbstractTriAckMessage * msg)
{
	long long ll = 0;
	if(!msg)
		return 0;
	decodeLongLong(&ll, msg->pData, msg->nLen, ACK_HEADER_V3_LENGTH);
	disposeAbstractMsg(msg);
	return ll;
}


int calcTciTypeLen(const Type type)
{
	return TCI_TYPE_LENGTH;
}

Boolean tciIsNull(Value inst);//forward declaration

int calcTciValueLen(const Value val)
{
	return TCI_VALUE_LENGTH;
}

int calcDoubleLen(const double * dbl)
{
	return 8;
}

int calcObjidLen(TciObjidValue *value)
{
	int nLen = 0;
	if(value->length)
	{
		unsigned long i;
		nLen += 4;
		for(i = 0; i < value->length; i++)
		{
			nLen += calcStrLen(value->elements[i].elem_as_ascii);
			nLen += 2;
		}
	}
	return nLen;
}

int calcUCStringLen(TciUCStringValue *ucsVal)
{
	if(ucsVal)
		return 4 + ucsVal->length * 2;
	return 4;
}

int calcUCStringBufferLen(TciUCStringValue *ucsVal)
{
	if(ucsVal)
		return 4 + ucsVal->length * 4;
	return 4;
}

int calcTciParamTypeLen(TciParameterType *parType)
{
	int nLen = calcStrLen(parType->parName);//QualifiedName???
	nLen++;//parameter passing mode byte
	//optional value reference
	nLen++;//value present or missing
	if(!tciIsNull(parType->parValue))
	{
		nLen += TCI_VALUE_LENGTH;//uid
		nLen += 4;//cls_id
	}
	return nLen;
}

int calcTciParamListLen(TciParameterListType *parList)
{
	int nLen = 1;
	if(parList->length)
	{
		long i;
		nLen += 4;
		for(i = 0; i < parList->length; i++)
			nLen += calcTciParamTypeLen(&parList->parList[i]);
	}
	return nLen;
}

int encodeBoolean(Boolean val, char * pData, int nOffset)
{
	pData[nOffset] = val;
	return nOffset+1;
}
int encodeDouble(const double * dbl, char * pData, int nOffset)
{
	int nLen = calcDoubleLen(dbl);
	memcpy(&pData[nOffset], dbl, nLen);
	return nOffset + nLen;
}

int encodeTciType(const Type inst, char * pData, int nOffset)
{
	memcpy(&pData[nOffset], inst, TCI_TYPE_LENGTH);
	return nOffset + TCI_TYPE_LENGTH;
}

int encodeTciValue(const Value inst, char * pData, int nOffset)
{//should only be called if "tciIsNull(inst) == FALSE"
	if(inst)
		memcpy(&pData[nOffset], inst, TCI_VALUE_LENGTH);
	else
		memset(&pData[nOffset], 0, TCI_VALUE_LENGTH);
	nOffset += TCI_VALUE_LENGTH;
	return nOffset;
}

int encodeObjectidValue(TciObjidValue * value, char * pData, int nOffset)
{
	unsigned long i;

	memcpy(&pData[nOffset], &value->length, 4);
	nOffset += 4;
	for(i = 0; i < value->length; i++)
	{
		TciObjidElemValue *elem = &value->elements[i];
		nOffset = encodeString(elem->elem_as_ascii, pData, nOffset);
		memcpy(&pData[nOffset], &elem->elem_as_number, 2);
		nOffset += 2;
	}
	return nOffset;
}

int encodeUCString(TciUCStringValue *val, char * pData, int nLen, int nOffset)
{
	unsigned int /*i,*/ streamLen = (val) ? val->length * 2 : 0; // len in bytes
	memcpy(&pData[nOffset], &streamLen, 4);
	nOffset += 4;
	//(version >= TCI 4.2.2)
	if (sizeof(wchar_t) == 2)
	{
		memcpy(&pData[nOffset], val->string, streamLen);
		nOffset += streamLen;
	}
	else
	{
		unsigned int i;
		streamLen /= 2; // len in chars
		for (i = 0; i < streamLen; i++)
		{
			int16_t nChar = (int16_t)val->string[i];
			memcpy(&pData[nOffset], &nChar, 2);
			nOffset += 2;
		}
	}
	return nOffset;
}

int encodeUCStringBuffer(TciUCStringValue *val, char * pData, int nLen, int nOffset)
{
	unsigned int strLen = (val) ? val->length : -1;
	memcpy(&pData[nOffset], &strLen, 4);
	nOffset += 4;
	if (sizeof(wchar_t) == 4)
	{
		memcpy(&pData[nOffset], &val->string, val->length * 4);
		nOffset = val->length * 4;
	}
	else
	{
		unsigned int i;
		int32_t nChar;
		for(i = 0; i < val->length; i++)
		{
			nChar = (int32_t)val->string[i];
			memcpy(&pData[nOffset], &nChar, 4);
			nOffset += 4;
		}
	}
	return nOffset;
}

int encodeTciParameterType(TciParameterType *parType, char *pData,int nOffset)
{
	char isPresent = tciIsNull(parType->parValue) ? 0x00 : 0xFF;
	nOffset = encodeString(parType->parName, pData, nOffset);
	pData[nOffset++] = (char)parType->parPassMode;
	pData[nOffset++] = isPresent;
	if(isPresent)
	{
		int clsId = 0;
		nOffset = encodeTciValue(parType->parValue, pData, nOffset);
		memcpy(&pData[nOffset], &clsId, 4);
		nOffset += 4;
	}
	return nOffset;
}

int encodeTciParamList(TciParameterListType * parList, char * pData, int nOffset)
{
	pData[nOffset++] = (parList->length) ? 0xFF : 0x00;
	if(parList->length)
	{
		long i;
		memcpy(&pData[nOffset], &parList->length, 4);
		nOffset+=4;
		for(i=0;i<parList->length;i++)
			nOffset = encodeTciParameterType(&parList->parList[i], pData, nOffset);
	}
	return nOffset;
}
