#include "EncodeDecodeTri.h"
#include <malloc.h>
#include <string.h>
#include <wchar.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

//local definitions without including headers
#define TRUE 1
#define FALSE 0

int decodeInt(int * pNum, const char * pData, int nLen, int nOffset)
{
	if (nOffset < 0)
		return -1;
	if (nOffset + 4 > nLen)
		return -1; // not enough bytes
	memcpy(pNum, &pData[nOffset], 4);
	nOffset += 4;
	return nOffset;
}

int decodeString(char ** ppszStr, const char * pData, int nLen, int nOffset)
{
	int nStrLen;
	
	if (nOffset < 0)
		return -1;
		
	nOffset = decodeInt(&nStrLen, pData, nLen, nOffset);
	if (nOffset < 0)
		return -1;

	if (nOffset + nStrLen > nLen)
		return -1; // not enough bytes

	if (nStrLen >= 0)
	{
		*ppszStr = (char*)malloc(nStrLen + 1);
		if (nStrLen > 0)
		{
			memcpy(*ppszStr, &pData[nOffset], nStrLen);		
			nOffset += nStrLen;
		}
		(*ppszStr)[nStrLen] = 0;
	}
	else
		*ppszStr = 0;

	return nOffset;
}

int decodeUString(wchar_t ** pwszStr, const char * pData, int nLen, int nOffset)
{
	int nStrLen;
	if (nOffset < 0)
		return -1;
	
	nOffset = decodeInt(&nStrLen, pData, nLen, nOffset);
	if (nOffset < 0)
		return -1;

	if (nOffset + nStrLen > nLen)
		return -1; // not enough bytes

	if (nStrLen >= 0)
	{
		if (sizeof(wchar_t) == 2)
		{
			*pwszStr = (wchar_t*)malloc(nStrLen + 2);
			if (nStrLen > 0)
			{
				memcpy(*pwszStr, &pData[nOffset], nStrLen);		
				nOffset += nStrLen;
			}
			(*pwszStr)[nStrLen/2] = 0;
		}
		else
		{
			nStrLen /= 2;
			*pwszStr = (wchar_t*)malloc((nStrLen + 1) * sizeof(wchar_t));
			if (nStrLen > 0)
			{
				int i;
				for(i = 0; i < nStrLen; i++)
				{
					int16_t nVal;
					memcpy(&nVal, &pData[nOffset], 2);		
					nOffset += 2;
					(*pwszStr)[i] = (wchar_t)nVal;
				}
			}
			(*pwszStr)[nStrLen] = 0;
		}
	}
	else
		*pwszStr = 0;

	return nOffset;
}

int decodeQualifiedName(QualifiedName * pName, const char * pData, int nLen, int nOffset)
{
	char * pszStr;
	
	if (nOffset < 0)
		return -1;
	memset(pName, 0, sizeof(QualifiedName));
	
	nOffset = decodeString(&pszStr, pData, nLen, nOffset);
	if (nOffset >= 0 && pszStr)
	{
		char * pszSub = strchr(pszStr, '.');		
		if (pszSub)
		{
			int nStrLen;
			int nPos = (int)(pszSub - pszStr);
			if (nPos > 0)
			{
				pName->moduleName = (char*)malloc(nPos + 1);
				memcpy(pName->moduleName, pszStr, nPos);
				pName->moduleName[nPos] = 0;
			}
		
			nStrLen = (int)strlen(pszStr) - nPos;
			pName->objectName = (char*)malloc(nStrLen);
			memcpy(pName->objectName, &pszStr[nPos + 1], nStrLen);

			free(pszStr);
		}
		else
			pName->objectName = pszStr;
	}
	
	return nOffset;
}

int decodeBinaryString(BinaryString * pBin, const char * pData, int nLen, int nOffset, int bDecodeBits)
{
	int nStrLen;
	if (nOffset < 0)
		return -1;
	
	memset(pBin, 0, sizeof(BinaryString));
	
	nOffset = decodeInt(&nStrLen, pData, nLen, nOffset);
	if (nOffset < 0)
		return -1;

	if (nOffset + nStrLen > nLen)
		return -1; // not enough bytes
	
	if (nStrLen > 0)
	{		
		pBin->bits = nStrLen * 8;
		pBin->data = (unsigned char*)malloc(nStrLen);
		memcpy(pBin->data, &pData[nOffset], nStrLen);
		nOffset += nStrLen;
	}
	else
	{
		pBin->bits = 0;
		pBin->data = 0;
	}
	if(bDecodeBits)
	{
		memcpy(&pBin->bits, &pData[nOffset], 4);
		nOffset += 4;
	}
	return nOffset;
}

int decodeTimerDuration(TriTimerDuration * pDbl, const char * pData, int nLen, int nOffset)
{
	int nDblLen;
	if (nOffset < 0)
		return -1;
	
	memset(pDbl, 0, sizeof(TriTimerDuration));
	
	nOffset = decodeInt(&nDblLen, pData, nLen, nOffset);
	if (nOffset < 0)
		return -1;

	if (nOffset + nDblLen > nLen)
		return -1; // not enough bytes

	if (nDblLen != 0)
	{
		memcpy(pDbl, &pData[nOffset], nDblLen);
		nOffset += nDblLen;
	}
	else
		return -1;

	return nOffset;
}

int decodeCompId(TriComponentId * pComp, const char * pData, int nLen, int nOffset)
{
	if (nOffset < 0)
		return -1;

	// Component ID
	nOffset = decodeBinaryString(&pComp->compInst, pData, nLen, nOffset, FALSE);

	// Component name
	if (nOffset >= 0)
		nOffset = decodeString(&pComp->compName, pData, nLen, nOffset);

	// Component type
	if(nOffset >=0)
		nOffset = decodeQualifiedName(&pComp->compType, pData, nLen, nOffset);

	return nOffset;
}

int decodePortId(TriPortId * pPort, const char * pData, int nLen, int nOffset)
{
	if (nOffset < 0)
		return -1;
	memset(pPort, 0, sizeof(TriPortId));

	// port name
	nOffset = decodeString(&pPort->portName, pData, nLen, nOffset);	

	// component id
	if (nOffset >= 0)
	{
		if (nOffset + 1 < nLen)
		{
			if (pData[nOffset++]) // component id present
				nOffset = decodeCompId(&pPort->compInst, pData, nLen, nOffset);
		}
		else
			nOffset = -1;
	}

	// index
	if (nOffset >= 0)
	{
		int nIndex = -1;
		nOffset = decodeInt(&nIndex, pData, nLen, nOffset);
		pPort->portIndex = nIndex;
	}

	// type name
	if (nOffset >= 0)
		nOffset = decodeQualifiedName(&pPort->portType, pData, nLen, nOffset);

	return nOffset;
}

int decodePortList(TriPortIdList * pList, const char * pData, int nLen, int nOffset)
{
	int nPortCount = 0;
	if (nOffset < 0)
		return -1;
	memset(pList, 0, sizeof(TriPortIdList));
	
	nOffset = decodeInt(&nPortCount, pData, nLen, nOffset);
	pList->length = nPortCount;

	if (nOffset >= 0 && nPortCount > 0)
	{
		int i;
		pList->portIdList = (TriPortId**)malloc(sizeof(TriPortId*) * nPortCount);
		memset(pList->portIdList, 0, sizeof(TriPortId*) * nPortCount);
		for (i = 0; i < nPortCount && nOffset >= 0; i++)
		{
			pList->portIdList[i] = (TriPortId*)malloc(sizeof(TriPortId));
			nOffset = decodePortId(pList->portIdList[i], pData, nLen, nOffset);
			if (i > 0)
			{
				copyTriComponentId(&pList->portIdList[i]->compInst,
					&pList->portIdList[0]->compInst);
			}
		}
	}
	return nOffset;
}

int decodeAddrList(TriAddressList * pList, const char * pData, int nLen, int nOffset)
{
	int nAddrCount = 0;
	if (nOffset < 0)
		return -1;
	memset(pList, 0, sizeof(TriAddressList));
	
	nOffset = decodeInt(&nAddrCount, pData, nLen, nOffset);
	pList->length = nAddrCount;

	if (nOffset >= 0 && nAddrCount > 0)
	{
		int i;
		pList->addrList = (TriAddress**) malloc(sizeof(TriAddress*) * nAddrCount);
		memset(pList->addrList, 0, sizeof(TriAddress*) * nAddrCount);
		for (i = 0; i < nAddrCount && nOffset >= 0; i++)
		{
			pList->addrList[i] = (TriAddress*)malloc(sizeof(TriAddress));
			nOffset = decodeBinaryString(pList->addrList[i], pData, nLen, nOffset, TRUE);
		}
	}
	return nOffset;
}

int decodeParameter(TriParameter * pParam, char ** pNameStorage, const char * pData, int nLen, int nOffset)
{
	if (nOffset < 0)
		return -1;
	memset(pParam, 0, sizeof(TriParameter));
	
	if (nOffset + 1 < nLen)
		pParam->mode = (TriParameterPassingMode)pData[nOffset++];
	else
		nOffset = -1;
	
	if (nOffset >= 0)
	{
		nOffset = decodeString(pNameStorage, pData, nLen, nOffset);
		//char * pszName;
		//nOffset = decodeString(&pszName, pData, nLen, nOffset);
		//free(pszName); // just temporary, C structure doesn't contain name

	}
	if (nOffset >= 0)
	{
		nOffset = decodeBinaryString(&pParam->par, pData, nLen, nOffset, TRUE);
	}
	return nOffset;
}

int decodeParamList(TriParameterList * pList, char *** pParameterNameList, const char * pData, int nLen, int nOffset)
{
	int nParamCount = 0;
	char ** pNameList = 0;
	*pParameterNameList = 0;
	if (nOffset < 0)
		return -1;
	memset(pList, 0, sizeof(TriParameterList));
	
	nOffset = decodeInt(&nParamCount, pData, nLen, nOffset);
	pList->length = nParamCount;
	
	if (nOffset >= 0 && nParamCount > 0)
	{
		int i;
		pNameList = (char**) malloc(sizeof(char*) * nParamCount);
		pList->parList = (TriParameter**) malloc(sizeof(TriParameter*) * nParamCount);
		memset(pList->parList, 0, sizeof(TriParameter*) * nParamCount);
		for (i = 0; i < nParamCount && nOffset >= 0; i++)
		{
			pList->parList[i] = (TriParameter*)malloc(sizeof(TriParameter));
			nOffset = decodeParameter(pList->parList[i], &pNameList[i], pData, nLen, nOffset);
		}
	}
	*pParameterNameList = pNameList;
	return nOffset;
}

int calcStrLen(const char * pszStr)
{
	int nLen = 4;
	if (pszStr)
		nLen += (int)strlen(pszStr);
	return nLen; 
}

int calcUStrLen(const wchar_t * wszStr)
{
	int nLen = 4;
	if (wszStr)
		nLen += (int)wcslen(wszStr) * 2;
	return nLen; 
}

int calcBinLen(const BinaryString * pData, int bCountBits)
{
	int nLen = 4 + pData->bits/8;
	if (bCountBits)
		nLen += 4;
	if (pData->bits % 8 > 0)
		nLen++;
	if (nLen < 0)
		nLen = 0;
	return nLen;
}

int calcQualifiedName(const QualifiedName * pName)
{
	int nLen =  4;
	if (pName->objectName)
	{
		nLen += (int)strlen(pName->objectName);
		if (pName->moduleName)
			nLen += 1 + (int)strlen(pName->moduleName);
	}
	return nLen;
}

int calcCompLen(const TriComponentId * pCompId)
{
	int nLen = calcBinLen(&pCompId->compInst, FALSE) + calcStrLen(pCompId->compName) +
		calcQualifiedName(&pCompId->compType);
	return nLen;
}

int calcTimerDuration(const TriTimerDuration * pDbl)
{
	int nLen = sizeof(int32_t) + sizeof(TriTimerDuration);//4 + 8
	return nLen;
}

int calcPortLen(const TriPortId * pPortId)
{	
	int nLen = calcStrLen(pPortId->portName) +
		1 /*component present*/ + 
		calcCompLen(&pPortId->compInst) +
		4 /*index*/ +
		calcQualifiedName(&pPortId->portType);
	return nLen;
}

int calcParamLen(const TriParameter * pParam, const char* pName)
{
	int nLen = 1 + calcStrLen(pName) + calcBinLen(&pParam->par, TRUE);
	return nLen;
}

int calcParamListLen(const TriParameterList * pParamList, const char *** pNameList)
{
	int nLen = 4, i;
	for (i = 0; i < pParamList->length; i++)
		nLen += calcParamLen(pParamList->parList[i], (*pNameList)[i]);
	return nLen;
}

int encodeString(const char * pszStr, char * pData, int nOffset)
{
	int nLen = -1;
	if (pszStr)
		nLen = (int)strlen(pszStr);
	memcpy(&pData[nOffset], &nLen, 4);	
	nOffset += 4;
	if(nLen > 0)
	{
		memcpy(&pData[nOffset], pszStr, nLen);
		nOffset += nLen;
	}
	return nOffset;
}

int encodeBinData(const BinaryString *pBin, char * pData, int nOffset, int bEncodeBits)
{
	int nLen = pBin->bits/8;
	if (pBin->bits % 8 > 0)
		nLen++;
	if (nLen <= 0 || !pBin->data)
		nLen = -1;
	memcpy(&pData[nOffset], &nLen, 4);
	nOffset += 4;
	if(nLen > 0)
	{
		memcpy(&pData[nOffset], pBin->data, nLen);
		nOffset += nLen;
	}
	if(bEncodeBits)
	{
		memcpy(&pData[nOffset], &pBin->bits, 4);
		nOffset += 4;
	}
	return nOffset;
}

int encodeTimerDuration(const TriTimerDuration *pDbl, char * pData, int nOffset)
{
	int nLen = sizeof(TriTimerDuration);
	memcpy(&pData[nOffset], &nLen, 4);
	nOffset += 4;
	if(nLen > 0)
	{
		memcpy(&pData[nOffset], pDbl, nLen);
		nOffset += nLen;
	}
	return nOffset;
}

int encodeQualifiedName(const QualifiedName * pName, char * pData, int nOffset)
{
	int nLen =  0;
	if (pName->objectName)
	{
		nLen = (int)strlen(pName->objectName);
		if (pName->moduleName)
			nLen += 1 + (int)strlen(pName->moduleName);
	}
	
	memcpy(&pData[nOffset], &nLen, 4);
	nOffset += 4;

	if (pName->objectName)
	{
		if (pName->moduleName)
		{
			nLen = (int)strlen(pName->moduleName);
			memcpy(&pData[nOffset], pName->moduleName, nLen);
			nOffset += nLen;
			pData[nOffset++] = '.';
		}
		nLen = (int)strlen(pName->objectName);
		memcpy(&pData[nOffset], pName->objectName, nLen);
		nOffset += nLen;		
	}
	return nOffset;
}

int encodeCompId(const TriComponentId * pCompId, char * pData, int nOffset)
{
	nOffset = encodeBinData(&pCompId->compInst, pData, nOffset, FALSE);
	nOffset = encodeString(pCompId->compName, pData, nOffset);
	nOffset = encodeQualifiedName(&pCompId->compType, pData, nOffset);
	return nOffset;
}

int encodePortId(const TriPortId * pPortId, char * pData, int nOffset)
{
	nOffset = encodeString(pPortId->portName, pData, nOffset);
	pData[nOffset++] = (char)0xFF;
	nOffset = encodeCompId(&pPortId->compInst, pData, nOffset);
	memcpy(&pData[nOffset], &pPortId->portIndex, 4);
	nOffset += 4;
	nOffset = encodeQualifiedName(&pPortId->portType, pData, nOffset);
	return nOffset;
}

int encodeUString(const wchar_t * wszStr, char * pData, int nOffset)
{
	int32_t nLen = -1; // length in bytes
	if (wszStr)
		nLen = (int)wcslen(wszStr) * 2;
	memcpy(&pData[nOffset], &nLen, 4);
	nOffset += 4;
	if(nLen > 0)
	{
		if (sizeof(wchar_t) == 2)
		{
			memcpy(&pData[nOffset], wszStr, nLen);
			nOffset += nLen;
		}
		else // if wchar_t has different size than two bytes
		{					
			int i;
			nLen /= 2; // len in chars
			for (i = 0; i < nLen; i++)
			{
				int16_t nVal = (int16_t)wszStr[i];
				memcpy(&pData[nOffset], &nVal, 2);
				nOffset += 2;
			}
		}
	}
	return nOffset;
}

int encodeParam(const TriParameter * pParam, const char * pName, char * pData, int nOffset)
{
	pData[nOffset++] = pParam->mode;
	nOffset = encodeString(pName, pData, nOffset);
	nOffset = encodeBinData(&pParam->par, pData, nOffset, TRUE);
	return nOffset;
}

int encodeParamList(const TriParameterList * pParamList, const char *** pNameList, char * pData, int nOffset)
{
	int i;
	memcpy(&pData[nOffset], &pParamList->length, 4);
	nOffset += 4;
	for (i = 0; i < pParamList->length; i++)
		nOffset = encodeParam(pParamList->parList[i], (*pNameList)[i], pData, nOffset);
	return nOffset;
}

