#include "GameTalk/Utility.h"
#include "GameTalk/GameTalkBase.h"

EA::GameTalk::Utility::Parser::Parser(GameTalkReceiverFnc callback, GameTalkInstance * gameTalkInstance) : 
mCallback(callback), mGameTalkInstance(gameTalkInstance), mBufferMsgIndex(0), mBufferMsgSize(0)
{
}

bool EA::GameTalk::Utility::Parser::WaitingForData()
{
	if ((mBufferMsgIndex != 0) || (mBufferMsgSize != 0))
		return true;

	return false;
}

// The receivedBytes pointer that is passed into this function will be moved one byte forward
bool EA::GameTalk::Utility::Parser::GetMessageSize(char *receivedBytes, uint32_t numReceivedBytes, uint32_t *msgSizeRequirements) const
{
	const uint32_t amtNecessary = sizeof(unsigned int);
	if (numReceivedBytes >= amtNecessary)
	{
		// Do I need to worry that the REadLE32 is moving the char pointer?
		uint32_t msgSize = EndianSwap::ReadLE32(receivedBytes);
		*msgSizeRequirements = amtNecessary + msgSize;
		return true;
	}
	return false;
}

void EA::GameTalk::Utility::Parser::ReadFromBuffer()
{
	// Process everything in our temp buffer
	// Make sure we have enough bytes to process the message otherwise
	// just cache it for now until the rest arrives
	uint32_t msgSizeRequired = 0;
	bool valid = GetMessageSize(mBufferMessage + mBufferMsgIndex, mBufferMsgSize - mBufferMsgIndex, &msgSizeRequired);
	while ((msgSizeRequired <= (mBufferMsgSize - mBufferMsgIndex)) && (valid))
	{
		// We preincrement mBufferMsgIndex (before we invoke the callback) in case we recurse
		// back into this function as a result of the user invoking this method after we invoke their code
		mBufferMsgIndex += msgSizeRequired;
		if (mGameTalkInstance) // Process message since we have one
		{
			(mGameTalkInstance->*mCallback)((void*)(mBufferMessage + (mBufferMsgIndex - msgSizeRequired)), msgSizeRequired);
		}

		valid = GetMessageSize(mBufferMessage + mBufferMsgIndex, mBufferMsgSize - mBufferMsgIndex, &msgSizeRequired);
	}

	// Readjust our temp buffer
	USER_GAMETALK_MEMCPY(mBufferMessageTemp, mBufferMessage + mBufferMsgIndex, mBufferMsgSize - mBufferMsgIndex);
	USER_GAMETALK_MEMCPY(mBufferMessage, mBufferMessageTemp, mBufferMsgSize - mBufferMsgIndex);
	mBufferMsgSize -= mBufferMsgIndex;
	// Reset mBufferMsgIndex to start of buffer - since we've copied everything to start of buffer
	mBufferMsgIndex = 0;
}

/******************************************************************************/
/*! GameTalkProtocol :: DigestReceivedData

    \brief  Break up the stream of data received from the GameTalk server into
            complete messages and pass these onto the GameTalkManager.

    \param  ReceivedBytes    - Data received from GameTalk server (little endian)
    \param  NumReceivedBytes - Amount of data received
    \return                  - void.

    GameTalk requires *complete* messages (but can process several complete
    messages at once).

    This method reconciles the difference by peeking into received data,
    dishing out only complete messages to GameTalk, and buffering partial
    messages until it receives enough data.

    This ASSUMES that the largest possible message fits within the
    mPartialMessage buffer (and asserts when the assumption is invalid).

    NOTE:  This method is called by a callback which runs in the network's
           thread (a separate thread from the game).
*/
/******************************************************************************/
void EA::GameTalk::Utility::Parser::DigestReceivedData(const char *receivedBytes, 
													   uint32_t numReceivedBytes)
{
	// If we are full and we can't process the message then we are screwed...
	// Somebody is sending a huge message. We have to either increase the buffer to accept
	// the message or get the user to chop it up when sending it.
	bool msgExceedsAvailableMemory = (numReceivedBytes + mBufferMsgSize) > GAMETALK_MAX_BUFFER_SIZE;
	if (msgExceedsAvailableMemory)
	{
		// We end up retesting the same condition (in our assert) to avoid the "conditional expression is constant" warning
		USER_GAMETALK_ASSERT(!msgExceedsAvailableMemory, "Not enough room in the receiving buffer to stuff more data in there - try increasing GAMETALK_MAX_BUFFER_SIZE");
		return;
	}

	// Copy everything to our buffer
	USER_GAMETALK_MEMCPY(mBufferMessage + mBufferMsgSize, receivedBytes, numReceivedBytes);
	mBufferMsgSize += numReceivedBytes;

	ReadFromBuffer();
}


