#include "build.h"
#include "netbuffer.h"

#define USE_NET_BUFFER 0

//[75]	not implemented yet but I would like to finish this and replace the existing buffer code with this,
//		this could be applied similarly to how Quake 3 worked with delta entities; for duke the replacement forone entity was planned to be a sprite and actor combined,
//		i.e., a netactor_t
//
//		Rather than sending over the entire netactor_t struct for a changed actor, this would send over only the fields within the struct that changed,
//		which could save quite a lot of network traffic.
#if (USE_NET_BUFFER==1)


//buffer read/write code inspired by Quake 3's source code, respectfully stolen and screwed up-er I mean adapted by me for eduke32
//---------------------------------------------------------------------------------------------------------------------------------

//enforce signed/unsigned and overflow behavior for WriteBits smaller than 32
//remember that all of the WriteX functions only take 32 bit integers as input...
#define PICKY_TYPECHECK 1

#define MAX_PACKET_STRING 1024

#define LOG_MESSAGE_BITS 1

#define	MAX_MSGLEN		16384		// max length of a message

//--------------------------------------------------------------------------------------------------------------------------------
// write data to net buffer
//--------------------------------------------------------------------------------------------------------------------------------

void NetBuffer_Init(NetBuffer_t *ioBuffer, int8_t *iByteArray, int32_t iArrayLength)
{
	// fill all the entries of the netbuffer struct (reminder: the byte array is not part of the struct)
	Bmemset(ioBuffer, 0, sizeof(*ioBuffer));
	ioBuffer->Data = iByteArray;
	ioBuffer->MaxSize = iArrayLength;
}


///<summary>
///guarantees that the destination string will be nul terminated
///</summary>
void static SafeStrncpyz(char *dest, const char *src, int destsize)
{
	Bassert(dest);
	Bassert(src);
	Bassert(destsize > 0);

	strncpy(dest, src, destsize - 1);
	dest[destsize - 1] = 0;
}

void NetBuffer_WriteBits(NetBuffer_t *ioBuffer, int32_t iData, int16_t iNumberOfBits)
{
	Bassert(ioBuffer->CurSize < ioBuffer->MaxSize);

	if (iNumberOfBits == 8)
	{
		ioBuffer->Data[ioBuffer->CurSize] = iData;
		ioBuffer->CurSize += 1;
		ioBuffer->WriteCurBit += 8;
	}

	else if (iNumberOfBits == 16)
	{
		int16_t tWord = iData;

		Bmemcpy(&ioBuffer->Data[ioBuffer->CurSize], &tWord, 2);

		ioBuffer->CurSize += 2;
		ioBuffer->WriteCurBit += 16;
	}

	else if (iNumberOfBits == 32)
	{
		Bmemcpy(&ioBuffer->Data[ioBuffer->CurSize], ioBuffer, 4);
		ioBuffer->CurSize += 4;
		ioBuffer->WriteCurBit += 32;
	}
}

void NetBuffer_WriteUnsignedByte(NetBuffer_t *ioBuffer, int32_t iByte)
{
#ifdef PICKY_TYPECHECK
	Bassert( !(iByte < 0 || iByte > 255) );
#endif
	NetBuffer_WriteBits(ioBuffer, iByte, 8);
}

void NetBuffer_WriteSignedByte(NetBuffer_t *ioBuffer, int32_t iByte)
{
#ifdef PICKY_TYPECHECK
	Bassert( !(iByte < -128 || iByte > 127) );
#endif
	NetBuffer_WriteBits(ioBuffer, iByte, 8);
}


void NetBuffer_WriteInt16(NetBuffer_t *ioBuffer, int32_t iWord)
{
#ifdef PICKY_TYPECHECK
	const int16_t cLowVal = (int16_t) 0x00008000;
	const int16_t cHighVal = (int16_t) 0x00007fff;

	Bassert( !(iWord < cLowVal || iWord > cHighVal) );
#endif
	NetBuffer_WriteBits(ioBuffer, iWord, 16);
}

void NetBuffer_WriteUInt16(NetBuffer_t *ioBuffer, int32_t iWord)
{
#ifdef PICKY_TYPECHECK
	const uint16_t cLowVal = (uint16_t) 0x00000000;
	const uint16_t cHighVal = (uint16_t) 0x0000ffff;
	Bassert( !(iWord < cLowVal || iWord > cHighVal) );
#endif
	NetBuffer_WriteBits(ioBuffer, iWord, 16);
}

void NetBuffer_WriteDword(NetBuffer_t *ioBuffer, int32_t iDWord)
{
	// don't care about signed/unsigned since the bit width matches
	NetBuffer_WriteBits(ioBuffer, iDWord, 32);
}

///<summary>
///where length is the size in bytes
///</summary>
void NetBuffer_WriteData(NetBuffer_t  *ioBuffer, const void *iData, int16_t iLength)
{
	int i;
	for (i = 0; i < iLength; i++)
	{
		NetBuffer_WriteUnsignedByte(ioBuffer, ((int8_t *)iData)[i]);
	}
}

void NetBuffer_WriteString(NetBuffer_t *ioBuffer, const char *iString)
{
	if (!iString)
	{
		// if iString is empty, write a NUL character
		NetBuffer_WriteData(ioBuffer, "", 1);
	}

	else
	{
		int		tLength; // length of input string
		char	tString[MAX_PACKET_STRING]; // temp storage

		tLength = strlen(iString);

		if (tLength >= MAX_PACKET_STRING)
		{
			Bassert(tLength < MAX_PACKET_STRING);
			NetBuffer_WriteData(ioBuffer, "", 1);
			return;
		}
		SafeStrncpyz(tString, iString, sizeof(tString));

		NetBuffer_WriteData(ioBuffer, tString, tLength + 1);
	}
}

//============================================================

//
// reading functions
//

int32_t NetBuffer_ReadBits(NetBuffer_t *ioBuffer, int iNumberOfBits)
{
	int32_t		qValue;

	qValue = 0;

	if (iNumberOfBits == 8)
	{
		qValue = ioBuffer->Data[ioBuffer->ReadCurByte];
		ioBuffer->ReadCurByte += 1;
		ioBuffer->ReadCurBit += 8;
	}
	else if (iNumberOfBits == 16)
	{
		int16_t tWord;

		Bmemcpy(&tWord, &ioBuffer->Data[ioBuffer->ReadCurByte], 2);
		qValue = tWord;
		ioBuffer->ReadCurByte += 2;
		ioBuffer->ReadCurBit += 16;
	}
	else if (iNumberOfBits == 32)
	{
		Bmemcpy(&qValue, &ioBuffer->Data[ioBuffer->ReadCurByte], 4);
		ioBuffer->ReadCurByte += 4;
		ioBuffer->ReadCurBit += 32;
	}

	else
		Bassert(0);

	return qValue;
}


// returns -1 if no more characters are available
int32_t NetBuffer_ReadByte(NetBuffer_t *ioBuffer)
{
	int32_t	qByte;

	qByte = (int8_t)NetBuffer_ReadBits(ioBuffer, 8);

	if (ioBuffer->ReadCurByte > ioBuffer->CurSize)
	{
		qByte = -1;
	}

	return qByte;
}

int32_t NetBuffer_ReadUnsignedByte(NetBuffer_t *ioBuffer)
{
	int32_t	qByte;

	qByte= (uint8_t)NetBuffer_ReadBits(ioBuffer, 8);

	if (ioBuffer->ReadCurByte > ioBuffer->CurSize)
	{
		qByte = -1;
	}

	return qByte;
}

int32_t NetBuffer_ReadInt16(NetBuffer_t *ioBuffer)
{
	int32_t	qWord;

	qWord = (int16_t)NetBuffer_ReadBits(ioBuffer, 16);
	if (ioBuffer->ReadCurByte > ioBuffer->CurSize)
	{
		qWord = -1;
	}

	return qWord;
}

int32_t NetBuffer_ReadUInt16(NetBuffer_t *ioBuffer)
{
	int32_t	qWord;

	qWord = (uint16_t)NetBuffer_ReadBits(ioBuffer, 16);
	if (ioBuffer->ReadCurByte > ioBuffer->CurSize)
	{
		qWord = -1;
	}

	return qWord;
}

int32_t NetBuffer_ReadDWord(NetBuffer_t *ioBuffer)
{
	int32_t	qDWord;

	qDWord = NetBuffer_ReadBits(ioBuffer, 32);
	if (ioBuffer->ReadCurByte > ioBuffer->CurSize)
	{
		qDWord = -1;
	}

	return qDWord;
}

void NetBuffer_ReadData(NetBuffer_t *ioBuffer, void *qData, int iDataLength) 
{
	int32_t i;
	for (i = 0; i<iDataLength; i++) 
	{
		((int8_t *)qData)[i] = NetBuffer_ReadByte(ioBuffer);
	}
}


char *NetBuffer_ReadString(NetBuffer_t *ioBuffer)
{
	// note that this is not returning a hanging pointer because
	// it's static
	static char	qString[MAX_PACKET_STRING];
	int		tLength, tChar;

	tLength = 0;
	do
	{
		tChar = NetBuffer_ReadUnsignedByte(ioBuffer);		// use ReadUnsignedByte so -1 is out of bounds
		if (tChar == -1 || tChar == 0)
		{
			break;
		}

		if (tChar > 127)
		{
			tChar = '.';
		}

		qString[tLength] = tChar;
		tLength++;
	} while (tLength < sizeof(qString) - 1);

	qString[tLength] = 0;

	return qString;
}


#endif