#include <GameTalk/GameTalkMessage.h>

// Only GameTalkMessage::Release necessitates the include of GameTalkInstance
#include <GameTalk/GameTalkInstance.h>

namespace EA
{
	namespace GameTalk
	{
		// This may want to belong to the GameTalkMessage class (since it is only used by it)
		class ChunkBlock
		{
		private:
			uint32_t	mType;
			uint32_t	mSize;

		public:
			uint32_t GetType() 
			{
				uint32_t resultType = mType;
				resultType = EndianSwap::SwapEndian(resultType, true);
				return resultType; 
			}

			uint32_t GetSize()
			{
				uint32_t resultSize = mSize;
				resultSize = EndianSwap::SwapEndian(resultSize, true);
				return resultSize;
			}

			// We presume that we're allowed to read the next eight bytes
			ChunkBlock* Next (void) { return(reinterpret_cast<ChunkBlock*>(reinterpret_cast<char*>(this) + GetSize())); };
			void* Data (void) { return(this+1); };
			size_t DataSize(void) { return GetSize() - sizeof(ChunkBlock); };

			static ChunkBlock* GetChunkBlock(char* buffer, uint32_t bufferSize)
			{
				if (bufferSize < 8) // Each ChunkBlock is at least eight bytes
					return NULL;

				ChunkBlock* resultBlock = (ChunkBlock*)buffer;
				return resultBlock;
			}
		};

		class GameTalkAllocator
		{
		public:
			GameTalkAllocator(uint8_t *tempBuffer, size_t tempBufferSize):
			  mTempBuffer(tempBuffer), mTempBufferSize(tempBufferSize),
				  mTempBufferPos(0)
			  {
			  }

			  void * Alloc(size_t size, const char* name)
			  {
				  if (size == 0)
					  return NULL;
				  if ( (mTempBufferPos + size)  <= mTempBufferSize )
				  {
					  // allocate space from our temporary buffer
					  void *ptr = mTempBuffer + mTempBufferPos;
					  mTempBufferPos += size;
					  return ptr;
				  }
				  else
				  {
					  // allocate space using “malloc”
					  return USER_GAMETALK_ALLOC(size, name);
				  }
			  }

			  void Free(void *ptr, const char* name)
			  {
				  if (ptr >= mTempBuffer && ptr < (mTempBuffer + mTempBufferSize) )
				  {
					  // if the pointer was from our temporary buffer, do nothing
					  ptr = NULL;
				  }
				  else
				  {
					  // free space
					  USER_GAMETALK_FREE(ptr, name);
					  ptr = NULL;
				  }
			  }

			  USE_GAMETALK_ALLOC("GameTalk::GameTalkAllocator")
		private:
			uint8_t *   mTempBuffer;
			size_t         mTempBufferSize;
			size_t         mTempBufferPos;
		};

		// --- KeyContent
		class KeyContent
		{
		public:
			KeyContent():mKey(NULL), mFormat(0), mContent(NULL), mSize(0), 
				mLittleEndian(0), mTypeCode((FormatTypes)0), 
				mTypeSize(0), mGameTalkAllocator(NULL){}

			inline KeyContent(const char* key, FormatTypes format, 
				const void* content, const size_t size, bool littleEndian, 
				FormatTypes typeCode, int typeSize, GameTalkAllocator* gameTalkAllocator);

			~KeyContent();

			void Initialize(char* key, FormatTypes format, void* content, 
				size_t size, bool littleEndian, 
				FormatTypes typeCode, int typeSize)
			{
				mKey = key;
				mFormat = format;
				mContent = content;
				mSize = size;
				mLittleEndian = littleEndian;
				mTypeCode = typeCode;
				mTypeSize = typeSize;
			}

			char * Key() { return mKey; }
			int Format() { return mFormat; }
			void * Content() { return mContent; }
			size_t Size() { return mSize; }
			bool IsLittleEndian() { return mLittleEndian; }
			EA::GameTalk::FormatTypes TypeCode() { return mTypeCode; }
			int TypeSize() { return mTypeSize; }

			USE_GAMETALK_ALLOC("GameTalk::KeyContent")
		private:
			KeyContent(KeyContent & other);
			KeyContent & operator=(KeyContent & other);

			char* mKey;
			int mFormat;
			void* mContent;
			size_t mSize;
			bool mLittleEndian;
			FormatTypes mTypeCode;
			int mTypeSize;
			GameTalkAllocator* mGameTalkAllocator;
		};

		KeyContent::KeyContent(const char * key, FormatTypes format, 
			const void* content, const size_t size, bool littleEndian, 
			FormatTypes typeCode, int typeSize, GameTalkAllocator* gameTalkAllocator):
		mKey(NULL),
			mFormat(format),
			mContent(NULL),
			mSize(size),
			mLittleEndian(littleEndian),
			mTypeCode(typeCode),
			mTypeSize(typeSize),
			mGameTalkAllocator(gameTalkAllocator)
		{
			if (!mGameTalkAllocator)
			{
				USER_GAMETALK_FAIL("KeyContent handed a null GameTalkAllocator - copies of user data will not be made - dangerous");
				mKey = (char*)key;
				mContent = (void*)content;
			}
			else // KeyContent assumes ownership of the data - so that a GameTalkMessage's contents will persist beyond the scope of the callback
			{
				// Include space for null delimiter
				size_t keySize = USER_GAMETALK_STRLEN(key) + 1;
				mKey = (char*)mGameTalkAllocator->Alloc(keySize, "GameTalk:KeyContent:mKey");
				USER_GAMETALK_STRNCPY_ADD_NULL(mKey, key, keySize);
				mContent = mGameTalkAllocator->Alloc(mSize, "GameTalk:KeyContent:mContent");
				USER_GAMETALK_MEMCPY(mContent, content, mSize);
			}
		}

		KeyContent::~KeyContent()
		{
			if (mGameTalkAllocator)
			{
				mGameTalkAllocator->Free(mKey, "GameTalk:KeyContent:mKey");
				mGameTalkAllocator->Free(mContent, "GameTalk:KeyContent:mContent");
			}
		}

		KeyContentBuffer::KeyContentBuffer(uint32_t maxSimultaneousMesgKeys, size_t strBufferSize)://, size_t bufferSize, char * buffer):
		mBuffer(NULL), mBufferSize(0), mMaxSimultaneousMesgKeys(maxSimultaneousMesgKeys), mKeyContentsUsed(0),
			mKeyContentPtrs(NULL), mKeyContents(NULL), mStringBuffer(NULL), mStringBufferUsed(0)
		{
			uint32_t ptrSize = sizeof(KeyContent**);
			size_t keyContentPtrMemorySize = ptrSize * maxSimultaneousMesgKeys;

			size_t keyContentSize = sizeof(KeyContent);
			size_t keyContentMemorySize = keyContentSize * maxSimultaneousMesgKeys;

			mBufferSize = keyContentPtrMemorySize + keyContentMemorySize + strBufferSize;
			char* buffer = (char *)USER_GAMETALK_ALLOC(mBufferSize, "GameTalk:buffer for KeyContentBuffer");
			mBuffer = buffer;


			size_t bufferIndex = 0;
			mKeyContentPtrs = (KeyContent**)buffer;
			for (size_t i = 0; i < mMaxSimultaneousMesgKeys; i++)
				mKeyContentPtrs[i] = NULL;

			uint32_t increment = ptrSize * mMaxSimultaneousMesgKeys;
			bufferIndex += increment;
			buffer += increment;

			mKeyContents = (KeyContent *)buffer;
			increment = sizeof(KeyContent) * mMaxSimultaneousMesgKeys;
			bufferIndex += increment;
			buffer += increment;

			USER_GAMETALK_ASSERT(mBufferSize > bufferIndex, "There is insufficient room in the buffer to create a KeyContentBuffer - this is VERY dangerous as unallocated memory is likely being referenced now.");
			for (uint32_t i = 0; i < mMaxSimultaneousMesgKeys;i++)
				mKeyContentPtrs[i] = &mKeyContents[i];

			// The string buffer gets whatever we have left
			mStringBuffer = buffer;
			mStringBufferSize = mBufferSize - bufferIndex;
		}

		// ------------------------------------------------------------------------------
		// KeyContentBuffer :: GetKeyContents
		// ------------------------------------------------------------------------------
		KeyContent ** KeyContentBuffer::GetKeyContents(uint32_t numKeyContents)
		{
			if (mKeyContentsUsed + numKeyContents > mMaxSimultaneousMesgKeys)
				return NULL;

			KeyContent ** keyContentResults = &(mKeyContentPtrs[mKeyContentsUsed]);
			mKeyContentsUsed += numKeyContents;
			return keyContentResults;
		}

		// ------------------------------------------------------------------------------
		// KeyContentBuffer :: ReleaseKeyContents
		// ------------------------------------------------------------------------------
		void KeyContentBuffer::ReleaseKeyContents(uint32_t numKeyContents)
		{
			mKeyContentsUsed -= numKeyContents;
			// We could optionally reset all the "freed" key-contents to 0's and nulls...
		}

		// ------------------------------------------------------------------------------
		// KeyContentBuffer :: GetStringBuffer
		// ------------------------------------------------------------------------------
		bool KeyContentBuffer::GetStringBuffer(size_t bufferSizeRequested, char *& stringBuffer)
		{
			if (mStringBufferUsed + bufferSizeRequested > mStringBufferSize)
				return false;

			stringBuffer = &mStringBuffer[mStringBufferUsed];
			mStringBufferUsed += bufferSizeRequested;
			return true;
		}

		// ------------------------------------------------------------------------------
		// KeyContentBuffer :: ReleaseStringBuffer
		// ------------------------------------------------------------------------------
		void KeyContentBuffer::ReleaseStringBuffer(uint32_t bufferSize)
		{
			mStringBufferUsed -= bufferSize;
			// We could optionally reset all the "freed" (char *)'s to nulls...
		}


		// --- Variables

		// Start with a minimum of 25 and increase it based on the usage in the game.
		int GameTalkMessage::mMaxDataLength = 25;

		const char* GameTalkMessage::sGameTalkServerID = "GameTalkServer";
		const char* GameTalkMessage::sServerMessageID = "Server Message";
		const char* GameTalkMessage::sUpdateTargetName = "UpdateTargetName";
		const char* GameTalkMessage::sAddChannelFilter = "AddChannelFilter";
		const char* GameTalkMessage::sRemoveChannelFilter = "RemoveChannelFilter";
		const char* GameTalkMessage::sSynchronousMessageID = "GameTalk.Synchronous";
		const char* GameTalkMessage::sSynchronousMessageIDPlusSeparator = "GameTalk.Synchronous.";
		const char* GameTalkMessage::sSynchroResponseMessageID = "GameTalk.SynchroResponse";
		const char* GameTalkMessage::sSynchroResponseMessageIDPlusSeparator = "GameTalk.SynchroResponse.";
		const char* GameTalkMessage::sSynchronousGameTalkIssue = "SynchronousGameTalkIssue";
		const char* GameTalkMessage::sGameTalkServerPingID = "GameTalkServer.PING";

		// ------------------------------------------------------------------------------
		// GameTalkMessage :: GameTalkMessage
		//
		// Constructs the message and initializes the message ID and the protocol
		// being used. Also creates a new dynamic list to store key-content pairs in.
		// ------------------------------------------------------------------------------
		GameTalkMessage::GameTalkMessage(const char* channelId, GameTalkAllocator* gameTalkAllocator) :
				mChannelId((char*)channelId),
				mIncomingMessage(false),
				mData(NULL),
				mDataCount(0),
				mSynchronousTransaction(NULL),
				mRefCount(1),
				mOwner(NULL),
				mAllocated(true),
				mGameTalkAllocator(gameTalkAllocator),
				mGameTalkAllocatorAlloc(false),
				mSender(NULL),
				mHasError(false),
				mLeftOvers(NULL),
				mLeftOversSize(0),
				mAllocChunkBlock(false)
		{
			if (!mGameTalkAllocator)
			{
				mGameTalkAllocator = new GameTalkAllocator(NULL, 0);
				mGameTalkAllocatorAlloc = true;
			}

			mData = AllocateDataBuffer(mMaxDataLength);
		}

		// ------------------------------------------------------------------------------
		// GameTalkMessage :: GameTalkMessage
		//
		// Constructs the message and keeps a reference to the buffer that will not be
		// used by this GameTalkMessage but for the GameTalkMessage that is created as a
		// reply to this GameTalkMessage.
		// ------------------------------------------------------------------------------
		GameTalkMessage::GameTalkMessage(const char* channelId, char * tempBuffer, 
			size_t tempBufferSize, GameTalkAllocator* gameTalkAllocator) :
				mChannelId((char*)channelId),
				mIncomingMessage(false),
				mData(NULL),
				mDataCount(0),
				mSynchronousTransaction(NULL),
				mRefCount(1),
				mOwner(NULL),
				mAllocated(true),
				mGameTalkAllocator(gameTalkAllocator),
				mGameTalkAllocatorAlloc(false),
				mSender(NULL),
				mHasError(false),
				mLeftOvers(NULL),
				mLeftOversSize(0),
				mAllocChunkBlock(false)
		{
			if (!mGameTalkAllocator)
			{
				mGameTalkAllocator = new GameTalkAllocator( (uint8_t *)tempBuffer, (int)tempBufferSize);
				mGameTalkAllocatorAlloc = true;
			}

			mData = AllocateDataBuffer(mMaxDataLength);
		}

		// ------------------------------------------------------------------------------
		// GameTalkMessage :: GameTalkMessage
		//
		// This constructor is for incoming messages only.  Constructs the message and
		// initializes the message ID and the protocol being used.
		// ------------------------------------------------------------------------------
		GameTalkMessage::GameTalkMessage(const char* channelId,
			KeyContent ** keyContentArray,
			uint32_t numKeys, GameTalkAllocator* gameTalkAllocator):
				mChannelId((char*)channelId),
				mIncomingMessage(true),
				mData(keyContentArray),
				mDataCount(numKeys),
				mSynchronousTransaction(NULL),
				mRefCount(1),
				mOwner(NULL),
				mAllocated(true),
				mGameTalkAllocator(gameTalkAllocator),
				mGameTalkAllocatorAlloc(false),
				mSender(NULL),
				mHasError(false),
				mLeftOvers(NULL),
				mLeftOversSize(0),
				mAllocChunkBlock(false)
		{
			// We don't new our mGameTalkAllocator because this constructor is used for incoming GameTalkMessages which shouldn't ever need to allocate dynamic memory
		}

		// ------------------------------------------------------------------------------
		// GameTalkMessage :: ~GameTalkMessage
		//
		// Releases the dynamic list containing the key-content pairs.
		// ------------------------------------------------------------------------------
		GameTalkMessage::~GameTalkMessage()
		{
			if (!mIncomingMessage) // Incoming messages allocate no dynamic memory
			{
				for (int i = 0; i < mDataCount; i++)
				{
					// Free each of the new'ed KeyContents
					DeallocateKeyContent(mData[i]);
					mData[i] = NULL;
				}

				// Free the dynamic array of KeyContent pointers
				InternalFree(mData, "GameTalkMessage:mData");
				mData = NULL;
			}

			if (mAllocChunkBlock)
				InternalFree(mLeftOvers, "GameTalkMessage::ChunkBlocks");

			if ((mGameTalkAllocatorAlloc) && (mGameTalkAllocator)) // If GameTalkMessage allocated the GameTalkAllocator and it is non-null - delete it
			{
				delete mGameTalkAllocator;
				mGameTalkAllocator = NULL;
			}
		}

		// ------------------------------------------------------------------------------
		// GameTalkMessage :: Clone
		//
		// Clones an existing GameTalkMessage
		// Technically unnecessary to return the message - since the user will still have a pointer to what they passed in
		// ------------------------------------------------------------------------------
		GameTalkMessage* GameTalkMessage::Clone( char * buffer, size_t bufferSize,
			GameTalkMessage * clonedMessage)
		{
			// We do the following in case we ever want to generalize this to something that doesn't require a "this" pointer
			GameTalkMessage * messageToClone = this;
			size_t bufferIndex = 0;
			// Read each key value pair for this message.
			for(int currentKey = 0; currentKey < messageToClone->GetNumberOfKeys(); currentKey++)
			{
				FormatTypes format = (EA::GameTalk::FormatTypes)0;
				FormatTypes typeCode = (EA::GameTalk::FormatTypes)0;
				int typeSize = 1;
				messageToClone->GetKeyContentMetaData(currentKey, format, typeCode, typeSize);
				bool isLittleEndian = messageToClone->IsLittleEndian(currentKey);
				const char* keyToClone = messageToClone->GetKey(currentKey);
				// Key length - Include space for null delimiter
				size_t keyLength = USER_GAMETALK_STRLEN(keyToClone) + 1;

				// Content length
				size_t contentToCloneSize = messageToClone->GetSize(currentKey);
				if (format == StringFormat) // We have to increment for the null delimiter - for backward compatibility reasons the size of this content will not include the final char (the null delimiter) if it is string content
					++contentToCloneSize;

				if (contentToCloneSize + keyLength + bufferIndex > bufferSize)
				{
					USER_GAMETALK_FAIL("The char buffer provided to GameTalkMessage::Clone is too small to house the string data in the GameTalkMessage");
					break;
				}

				// Copy string data in key
				char * clonedKey = &(buffer[bufferIndex]);
				bufferIndex += keyLength;
				USER_GAMETALK_MEMCPY(clonedKey, keyToClone, keyLength);

				// Copy data in key content
				const void * contentToClone = messageToClone->GetContent(currentKey);
				char * clonedContent = &(buffer[bufferIndex]);
				// If this is a string - we could optionally tack a null delimiter on this and increment one extra byte
				bufferIndex += contentToCloneSize;
				USER_GAMETALK_MEMCPY(clonedContent, contentToClone, contentToCloneSize);
				// Add the key value pair to the message.
				clonedMessage->mData[currentKey]->Initialize(clonedKey, format, clonedContent, contentToCloneSize, isLittleEndian, typeCode, typeSize);
			}

			return clonedMessage;
		}

		void GameTalkMessage::CopyTo( GameTalkMessage * destMessage)
		{
			// We do the following in case we ever want to generalize this to something that doesn't require a "this" pointer
			GameTalkMessage * srcMessage = this;

			// clear dest message of all keys
			destMessage->Clear();

			// Read each key value pair for this message.
			for(int currentKey = 0; currentKey < srcMessage->GetNumberOfKeys(); currentKey++)
			{
				FormatTypes format = (EA::GameTalk::FormatTypes)0;
				FormatTypes typeCode = (EA::GameTalk::FormatTypes)0;
				int typeSize = 1;
				srcMessage->GetKeyContentMetaData(currentKey, format, typeCode, typeSize);
				bool isLittleEndian = srcMessage->IsLittleEndian(currentKey);
				const char* keyToClone = srcMessage->GetKey(currentKey);

				// Content length
				size_t       contentToCloneSize = srcMessage->GetSize(currentKey);
				const void * contentToClone = srcMessage->GetContent(currentKey);

				// Add the key value pair to the message.
				destMessage->AddKeyContent(keyToClone, format, contentToClone, contentToCloneSize, isLittleEndian, typeCode, typeSize);
			}
		}

		bool GameTalkMessage::IsDataAligned(char*& buffer)
		{
			uintptr_t destAddr = (uintptr_t)buffer;
			uintptr_t destAddrRemainder = destAddr % USER_GAMETALK_ALIGNMENT;
			if(destAddrRemainder > 0)
				return false;
			else
				return true;
		}

		int GameTalkMessage::GetTypeSizeFromTypeCode(int typeCode)
		{
			// Should we default typeSize to 1 or 4?
			int typeSize = 1;
			switch(typeCode)
			{
			case 4:
				typeSize = sizeof(char);
				break;
			case 5:
			case 6:
				typeSize = sizeof(int8_t);
				break;
			case 7:
			case 8:
				typeSize = sizeof(int16_t);
				break;
			case 9:
			case 10:
				typeSize = sizeof(int32_t);
				break;
			case 11:
			case 12:
				typeSize = sizeof(int64_t);
				break;
			case 13:
				typeSize = sizeof(float);
				break;
			case 14:
				typeSize = sizeof(double);
				break;
			case 15: // Do we want to provide the fact that a .net decimal would be 16 bytes?
				typeSize = 16;
				break;
			}
			return typeSize;
		}

		const char* GameTalkMessage::CopyStringIntoBuffer(char*& buffer, char* endOfBuffer, char*& stringContentBuffer, size_t& stringContentBufferIndex, uint32_t stringContentBufferSize, uint32_t& size)
		{
			const char* stringWithoutNullDelimiter = ReadString(buffer, size);
			if (buffer > endOfBuffer) // We read past the end of the buffer to get this string - that's bad
			{
				USER_GAMETALK_FAIL("Bad encoding in GameTalkMessage");
				return NULL;
			}
			if (size + stringContentBufferIndex > stringContentBufferSize)
			{
				USER_GAMETALK_FAIL("StringFormat: The string buffer provided to GameTalkMessage::Create is too small - in most cases increasing the size of GAMETALK_RECEIVE_STRING_BUFFER_SIZE (or the string buffer size handed to the GameTalkInstance that is using this function) will fix this.");
				return NULL;
			}

			USER_GAMETALK_STRNCPY(stringContentBuffer, stringWithoutNullDelimiter, size);
			const char* copiedString = stringContentBuffer;
			stringContentBuffer[size] = '\0';
			stringContentBuffer += (size+1);
			stringContentBufferIndex += (size+1);

			return copiedString;
		}

		void* GameTalkMessage::GetLeftOvers(size_t& leftoverSize) const
		{
			leftoverSize = mLeftOversSize;
			return mLeftOvers;
		}

		void* GameTalkMessage::GetChunkData(uint32_t chunkType, size_t& chunkSize) const
		{
			chunkSize = 0;
			if (mLeftOvers == NULL) // This GameTalkMessage has no left-overs to process
				return NULL;

			char* buffer = static_cast<char*>(mLeftOvers);
			char* endOfBuffer = static_cast<char*>(buffer + mLeftOversSize);
			ChunkBlock* chunkBlock = ChunkBlock::GetChunkBlock(buffer, (uint32_t)mLeftOversSize);
			// The following is not a valid chunk - it points at the bytes immediately following the end of our buffer
			ChunkBlock* endChunk = reinterpret_cast<ChunkBlock*>(endOfBuffer);
			while ((chunkBlock != NULL) && (chunkBlock < endChunk))
			{
				intptr_t endOfChunkBlock = (intptr_t)((intptr_t)chunkBlock + chunkBlock->GetSize());
				if (endOfChunkBlock > (intptr_t)endChunk) // Our ChunkBlock has a size that extends past the end of buffer - error!
					return NULL;

				if (chunkBlock->GetType() == chunkType)
				{
					chunkSize = chunkBlock->DataSize();
					void* chunkBlockData = chunkBlock->Data();
					char* endOfChunk = static_cast<char*>(chunkBlockData) + chunkSize;
					if (endOfChunk > endOfBuffer)
					{
						USER_GAMETALK_FAIL("ChunkBlock data is corrupt - this ChunkBlock's data extends past the end of the buffer.");
						chunkBlock = NULL;
						chunkSize = 0;
					}

					break;
				}
				chunkBlock = chunkBlock->Next();
			}

			if ((chunkBlock) && (chunkBlock < endChunk) && (chunkSize > 0)) // If we have a valid ChunkBlock that isn't past the end of our buffer (i.e. the endChunk) and with a matching type (as indicated by having the size)
				return chunkBlock->Data();
			else
				return NULL;
		}

		void GameTalkMessage::AddChunkData(void* data, uint32_t chunkType, size_t chunkSize)
		{
			char* tempLeftOvers = NULL;
			// The eight is for the type uint and size uint to be stuffed into the chunkblocks
			size_t totalChunkSize = 8 + chunkSize;
			if (mLeftOvers)
			{
				tempLeftOvers = (char*)InternalAlloc(mLeftOversSize + totalChunkSize, "GameTalkMessage::ChunkBlocks");
				USER_GAMETALK_MEMCPY(tempLeftOvers, mLeftOvers, mLeftOversSize);
				if (mAllocChunkBlock) // We only free the existing mLeftOvers if we created it
					InternalFree(mLeftOvers, "GameTalkMessage::ChunkBlocks");

				mLeftOvers = tempLeftOvers;
				mLeftOversSize += totalChunkSize;
				tempLeftOvers += mLeftOversSize;
			}
			else
			{
				// The eight is for the type uint and size uint to be stuffed into the chunkblocks
				tempLeftOvers = (char*)InternalAlloc(totalChunkSize, "GameTalkMessage::ChunkBlocks");

				mLeftOvers = tempLeftOvers;
				mLeftOversSize = totalChunkSize;
			}
			EndianSwap::WriteLE32(tempLeftOvers, chunkType);
			EndianSwap::WriteLE32(tempLeftOvers, (uint32_t)(chunkSize + 8));
			USER_GAMETALK_MEMCPY(tempLeftOvers, static_cast<char*>(data), chunkSize);

			mAllocChunkBlock = true;
		}

		// ------------------------------------------------------------------------------
		// GameTalkMessage :: Create
		//
		// Takes a char buffer and assembles a GameTalkMessage from it
		// ------------------------------------------------------------------------------
		GameTalkMessage* GameTalkMessage::Create(char* buffer,
			char* endOfBuffer,
			char * stringContentBuffer,
			uint32_t stringContentBufferSize,
			uint32_t numKeys,
			GameTalkMessage * message,
			bool allocKeyContentPairs)
		{
			if (endOfBuffer <= buffer) // nothing to process
				return NULL;

			size_t stringContentBufferIndex = 0;
			char * startOfBuffer = buffer;
			// Read each key value pair for this message.
			for(uint32_t currentKey = 0; currentKey < numKeys; currentKey++)
			{
				char* key = ReadString(buffer);
				// We must null the key-content meta-data so that the key is "naturally" null-delimited
				uint32_t keyContentMetaData = EndianSwap::ReadAndNullLE32(buffer);
				FormatTypes format = (FormatTypes)(keyContentMetaData & 3);
				// decimal 128 = binary 10000000
				Endian endian = (Endian)(keyContentMetaData & 128);
				// decimal 65280 = binary 1111111100000000 - I don't think the &'ing is necessary - but meh...
				FormatTypes typeCode = (FormatTypes)((keyContentMetaData & 65280) >> 8);
				// decimal 16711680 = binary 111111110000000000000000 - I don't think the &'ing is necessary - but meh...
				int typeSize = (keyContentMetaData & 16711680) >> 16;
				if (typeSize == 0)
				{
					if (typeCode != 0)
						typeSize = GetTypeSizeFromTypeCode(typeCode);
					else // If we weren't able to either directly get the typeSize or deduce the typeSize from the typeCode - should we default typeSize to 1 or 4?
						typeSize = 1;
				}
				void* content = NULL;
				uint32_t size = 0;
				if (format == StringFormat)
				{
					// Since the KeyContent constructor takes ownership of the string data received - this is a bit wasteful (to copy the data here and there)
					const char * value = CopyStringIntoBuffer(buffer, endOfBuffer, stringContentBuffer, (size_t&)stringContentBufferIndex, stringContentBufferSize, size);
					if (value == NULL) // something went wrong
						break;
					content = (void*)value;
				}
				else // If we don't recognize the format specified - assume binary - hence this condition being an else and not "else if (format == BinaryFormat)"
				{
					char* value = ReadBinary(buffer, size);
					if (!IsDataAligned(value))
					{
						uint32_t padding = AlignData(stringContentBuffer);
						stringContentBufferIndex += padding;
						if (size + stringContentBufferIndex > stringContentBufferSize)
						{
							USER_GAMETALK_FAIL("BinaryFormat: The string buffer provided to GameTalkMessage::Create is too small - in most cases increasing the size of GAMETALK_RECEIVE_STRING_BUFFER_SIZE will fix this (or the string buffer size handed to the GameTalkInstance using this function).");
							break;
						}
						USER_GAMETALK_MEMCPY(stringContentBuffer, value, size);
						value = stringContentBuffer;
						stringContentBuffer += size;
						stringContentBufferIndex += size;
					}
					content = (void*)value;
				}

				if (allocKeyContentPairs)
					message->AddKeyContent(key, format, content, size, (endian == EA::GameTalk::LittleEndian), typeCode, typeSize);
				else // Add the key value pair to the message.
					message->mData[currentKey]->Initialize(key, format, content, size, (endian == EA::GameTalk::LittleEndian), typeCode, typeSize);
			}

			if (buffer < startOfBuffer) // no idea how this would be possible - while parsing the buffer - we should be moving the pointer forward (not back tracking)
				return message;

			if (buffer > endOfBuffer) // we've processed data in memory that didn't belong to this buffer - that would be awful
				return message;

			// The leftovers include the sender (if it exists in the remaining data)
			message->mLeftOversSize = endOfBuffer - buffer;
			message->mLeftOvers = buffer;

			size_t chunkDataSize = 0;
			void* chunkData = message->GetChunkData(EA::GameTalk::GameTalkMessage::Sender, chunkDataSize);
			if (chunkData != NULL) 
			{
				if (chunkDataSize > 0) 
				{
					char* chunkDataStr = static_cast<char*>(chunkData);
					if (chunkDataStr[chunkDataSize-1] == 0)
						message->mSender = chunkDataStr;
					else
					{
						message->mSender = NULL;
						USER_GAMETALK_FAIL("The GameTalkMessage sender string is not null delimited - there is currently no work-around for this.");
						// we could have a contingency that will put the copy the sender data into our stringContentBuffer if the sender string is not null 
						// delimited - however, currently the only reason this would happen is if the data from the GameTalkServer has been corrupted - 
						// therefore, there isn't much point in trying to gracefully recover from this (as the data is likely just bad)
						//message->mSender = CopyStringIntoBuffer(buffer, endOfBuffer, stringContentBuffer, stringContentBufferIndex, stringContentBufferSize, size);
					}
				}
				else 
				{
					message->mSender = NULL;
					USER_GAMETALK_FAIL("We found matching ChunkData - but it has zero size.");
				}
			}

			return message;
		}

		uint32_t GameTalkMessage::AlignData(char*& buffer)
		{
			uint32_t padding = 0;
			uintptr_t destAddr = (uintptr_t)buffer;
			uintptr_t destAddrRemainder = destAddr % USER_GAMETALK_ALIGNMENT;
			if(destAddrRemainder > 0)
			{
				padding = (uint32_t)USER_GAMETALK_ALIGNMENT - (uint32_t)destAddrRemainder;
				buffer += padding;
			}
			return padding;
		}

		size_t GameTalkMessage::GetChannelAndKeyContentSize(const char *channelName)  const
		{
			size_t messageSize = 0;

			// Calculate message buffer length
			// "channel" length
			// "channel" string length
			// "key count" size
			// Repeat for number of keys
			// "key size" length
			// "key name" string length
			// "value size" length
			// "value name" string length
			messageSize += sizeof(int);
			messageSize += USER_GAMETALK_STRLEN(channelName);
			messageSize += sizeof(int);
			for (int keys = 0; keys < this->GetNumberOfKeys(); keys++)
			{
				// Key
				messageSize += sizeof(int);
				messageSize += USER_GAMETALK_STRLEN(this->GetKey(keys));

				// Format
				messageSize += sizeof(uint32_t);

				// Value
				messageSize += sizeof(int);
				messageSize += this->GetSize(keys);
			}
			messageSize += mLeftOversSize;

			return messageSize;
		}

		void GameTalkMessage::SerializeGameTalkMessageContents(char*& dest, const char* channelId) const
		{
			if (!channelId)
				channelId = this->GetChannelId();
			// Write the message ID (channel)
			size_t messageIdSize = USER_GAMETALK_STRLEN(channelId);
			EndianSwap::WriteLE32(dest, (uint32_t)messageIdSize);
			WriteBuffer(dest, channelId, messageIdSize);

			// Write keyCount
			uint32_t keyCount = this->GetNumberOfKeys();
			EndianSwap::WriteLE32(dest, keyCount);

			// Write the key-value pairs
			size_t keySize = 0;
			size_t valueSize = 0;
			for (uint32_t keyIndex = 0; keyIndex < keyCount; keyIndex++)
			{
				// key
				keySize = USER_GAMETALK_STRLEN(this->GetKey(keyIndex));
				EndianSwap::WriteLE32(dest, (uint32_t)keySize);
				WriteBuffer(dest, this->GetKey(keyIndex), keySize);

				// format
				FormatTypes format = (FormatTypes)0; // 0 is string, 1 is binary
				FormatTypes typeCode = (FormatTypes)0; // what is a good default (in case we don't get a legit typeCode from GetKeyContentMetaData)?
				int typeSize = 1; // what is a good default (in case we don't get a legit typeSize from GetKeyContentMetaData)?
				GetKeyContentMetaData(keyIndex, format, typeCode, typeSize);
				uint32_t keyContentMetaData = static_cast<uint32_t>(format);
#if (!USER_GAMETALK_IS_LITTLE_ENDIAN) // decimal 128 = binary 10000000
				keyContentMetaData |= 128;
#endif
				keyContentMetaData |= ((uint32_t)typeCode) << 8;
				keyContentMetaData |= typeSize << 16;
				EndianSwap::WriteLE32(dest, keyContentMetaData);

				// value
				valueSize = this->GetSize(keyIndex);
				EndianSwap::WriteLE32(dest, (uint32_t)valueSize);
				WriteBuffer(dest, (char*)this->GetContent(keyIndex), valueSize);
			}

			if (mLeftOvers)
				WriteBuffer(dest, static_cast<const char*>(mLeftOvers), mLeftOversSize);
		}

		// ------------------------------------------------------------------------------
		// GameTalkMessage :: CreateBuffer
		//
		// Hand a char buffer to this function and it will write all its containing data to the buffer
		// Currently only used to serialize a GameTalkMessage and send it over the wire
		// ------------------------------------------------------------------------------
		size_t GameTalkMessage::CreateBuffer(const char* target, char*& buffer, const char* channelId) const
		{
			size_t targetSize = USER_GAMETALK_STRLEN(target);
			size_t messageSize = GetChannelAndKeyContentSize(channelId ? channelId : this->GetChannelId());
			// Calculate the size of the buffer we need
			// Total Length
			// "target name" length
			// "target name" string length
			// "message" length
			// "message" buffer length
			size_t size = sizeof(int);
			size += sizeof(int);
			size += targetSize;
			size += sizeof(int);
			size += messageSize;

			// allocate memory
			buffer = (char*)InternalAlloc(size * sizeof(char), "GameTalkMessage:serialization buffer");
			char* dest = buffer;

			// Write the buffer size
			EndianSwap::WriteLE32(dest, (uint32_t)size);

			// Write the target
			EndianSwap::WriteLE32(dest, (uint32_t)targetSize);
			WriteBuffer(dest, target, targetSize);

			// Write the message, starting with the message size
			EndianSwap::WriteLE32(dest, (uint32_t)messageSize);

			SerializeGameTalkMessageContents(dest, channelId);
			return size;
		}

		// ------------------------------------------------------------------------------
		// GameTalkMessage :: GetNumberOfKeys
		// ------------------------------------------------------------------------------
		int GameTalkMessage::GetNumberOfKeys() const
		{
			return mDataCount;
		}

		// ------------------------------------------------------------------------------
		// GameTalkMessage :: CompareKey
		// ------------------------------------------------------------------------------
		bool GameTalkMessage::CompareKey(int index, const char* compareTo) const
		{
			return(USER_GAMETALK_STREQUAL(this->GetKey(index), compareTo));
		}

		// ------------------------------------------------------------------------------
		// GameTalkMessage :: GetKey
		// ------------------------------------------------------------------------------
		const char* GameTalkMessage::GetKey(int index) const
		{
			KeyContent* kc = mData[index];
			if (kc == NULL)
			{
				// We retest the condition (instead of just using false) in the assert to avoid compiler warning
				USER_GAMETALK_ASSERT(kc != NULL, "Key Content is Null.");
				return NULL;
			}
			else
				return kc->Key();
		}

		// ------------------------------------------------------------------------------
		// GameTalkMessage :: GetFormat
		// ------------------------------------------------------------------------------
		int GameTalkMessage::GetFormat(int index) const
		{
			KeyContent* kc = mData[index];
			if (kc == NULL)
			{
				// We retest the condition (instead of just using false) in the assert to avoid compiler warning
				USER_GAMETALK_ASSERT(kc != NULL, "Key Content is Null.");
				return 0;
			}
			else if (kc->TypeCode() != 0)
				return kc->TypeCode();
			else
				return kc->Format();
		}

		bool GameTalkMessage::IsLittleEndian(int index) const
		{
			KeyContent* kc = mData[index];
			if (kc == NULL)
			{
				// We retest the condition (instead of just using false) in the assert to avoid compiler warning
				USER_GAMETALK_ASSERT(kc != NULL, "Key Content is Null.");
				return NULL;
			}
			else
				return kc->IsLittleEndian();
		}

		// ------------------------------------------------------------------------------
		// GameTalkMessage :: GetContent
		// ------------------------------------------------------------------------------
		const void* GameTalkMessage::GetContent(int index) const
		{
			KeyContent* kc = mData[index];
			if (kc == NULL)
			{
				// We retest the condition (instead of just using false) in the assert to avoid compiler warning
				USER_GAMETALK_ASSERT(kc != NULL, "Key content is Null.");
				return NULL;
			}
			else
				return kc->Content();
		}

		// ------------------------------------------------------------------------------
		// GameTalkMessage :: GetContent
		// ------------------------------------------------------------------------------
		const void* GameTalkMessage::GetContentWithKey(const char* key) const
		{
			for (int i = 0; i < mDataCount; i++)
			{
				if (this->CompareKey(i, key))
					return GetContent(i);
			}

			return NULL;
		}

		int GameTalkMessage::GetIndexOfKey(const char* key) const
		{
			for (int i = 0; i < mDataCount; i++)
			{
				if (this->CompareKey(i, key))
					return i;
			}

			return -1;
		}

		void GameTalkMessage::ClearError() const
		{
			// clear error flag
			mHasError = false;
		}

		bool GameTalkMessage::HasError() const
		{
			return mHasError;
		}

		void GameTalkMessage::SetError() const
		{
			// set error flag
			mHasError = true;
		}

		bool GameTalkMessage::GetPtrTo64Bit(int index, uint64_t* userProvidedBuffer, size_t count, int startIndex) const
		{
			if (!userProvidedBuffer) // If user gives us a null pointer - unusable
			{
				SetError();
				return false;
			}

			if (index < 0 || index >= GetNumberOfKeys()) // If user requests an out of range index
			{
				SetError();
				return false;
			}

			size_t msgSize = GetSize(index);
			if (msgSize < 8) // Should we only check for less than eight or just anytime the size isn't wholly divisable by eight?
			{
				SetError();
				return false;
			}

			if (startIndex + count > (msgSize / sizeof(uint64_t))) // Check that the user isn't asking for more elements than are contained in the message
			{
				SetError();
				return false;
			}

			KeyContent* kc = mData[index];
			// Should we bother checking for a remainder - i.e. to provide an error mentioning extra/unnecessary data
			uint8_t* readBuffer = (uint8_t*)kc->Content();
			uint8_t* writeBuffer = (uint8_t*)userProvidedBuffer;
			if (EndianSwap::NeedSwap(kc->IsLittleEndian()))
			{
				size_t numIterations = startIndex + (count * sizeof(uint64_t));
				for(size_t i = startIndex; i+7 < numIterations;i+=8)
				{
					// Swap bytes - we should probably be using an intrinsic to do this
					size_t iter = 0;
					for (size_t j = i;j < i+8;j++)
					{
						writeBuffer[j] = readBuffer[i+7-iter];
						++iter;
					}
				}
			}
			else // We probably want to check (KeyContent::SerializerBufferSize < intArraySize) - this memcpy isn't useful unless we're using the user's provided buffer
				USER_GAMETALK_MEMCPY(userProvidedBuffer, &(readBuffer[startIndex]), count * sizeof(uint64_t));

			return true;
		}

		bool GameTalkMessage::GetPtrTo32Bit(int index, uint32_t* userProvidedBuffer, size_t count, int startIndex) const
		{
			if (!userProvidedBuffer) // If user gives us a null pointer - unusable
			{
				SetError();
				return false;
			}

			if (index < 0 || index >= GetNumberOfKeys()) // If user requests an out of range index
			{
				SetError();
				return false;
			}

			size_t msgSize = GetSize(index);
			if (msgSize < sizeof(uint32_t)) // Should we only check for less than four or just anytime the size isn't wholly divisable by four
			{
				SetError();
				return false;
			}

			// What if user asked for more data than is contained in the payload?
			// Should we:
			// 1. Take the min of the user's count and the number of 32 bit elements OR
			// 2. Return false like below
			if (startIndex + count > (msgSize / sizeof(uint32_t)))
			{
				SetError();
				return false;
			}

			KeyContent* kc = mData[index];
			// Should we bother checking for a remainder - i.e. to provide an error mentioning unnecessary data
			uint32_t* readBuffer = (uint32_t*)kc->Content();
			if (EndianSwap::NeedSwap(kc->IsLittleEndian()))
			{
				size_t numIterations = startIndex + count;
				for(size_t i = startIndex; i < numIterations;i++)
				{
					uint32_t value = EndianSwap::SwapEndian(readBuffer[i], kc->IsLittleEndian());
					userProvidedBuffer[i] = value;
				}
			}
			else // We probably want to check (KeyContent::SerializerBufferSize < intArraySize) - this memcpy isn't useful unless we're using the user's provided buffer
				USER_GAMETALK_MEMCPY(userProvidedBuffer, &(readBuffer[startIndex]), count * sizeof(uint32_t));

			return true;
		}

		bool GameTalkMessage::GetPtrTo16Bit(int index, uint16_t* userProvidedBuffer, size_t count, int startIndex) const
		{
			if (!userProvidedBuffer) // If user gives us a null pointer - unusable
			{
				SetError();
				return false;
			}

			if (index < 0 || index >= GetNumberOfKeys()) // If user requests an out of range index
			{
				SetError();
				return false;
			}

			size_t msgSize = GetSize(index);
			if (msgSize < 2) // Should we only check for less than two or just anytime the size isn't wholly divisable by two?
			{
				SetError();
				return false;
			}

			if (startIndex + count > (msgSize / sizeof(uint16_t))) // Check that the user isn't asking for more elements than are contained in the message
			{
				SetError();
				return false;
			}

			KeyContent* kc = mData[index];
			// Should we bother checking for a remainder - i.e. to provide an error mentioning extra/unnecessary data
			uint8_t* readBuffer = (uint8_t*)kc->Content();
			uint8_t* writeBuffer = (uint8_t*)userProvidedBuffer;
			if (EndianSwap::NeedSwap(kc->IsLittleEndian()))
			{
				size_t eightBitStartIndex = startIndex*2;
				size_t numIterations = eightBitStartIndex + (count * 2);
				for(size_t i = eightBitStartIndex; i+1 < numIterations;i+=2)
				{
					writeBuffer[i] = readBuffer[i+1];
					writeBuffer[i+1] = readBuffer[i];
				}
			}
			else // We probably want to check (KeyContent::SerializerBufferSize < intArraySize) - this memcpy isn't useful unless we're using the user's provided buffer
				USER_GAMETALK_MEMCPY(userProvidedBuffer, &(readBuffer[startIndex]), count * sizeof(uint16_t));

			return true;
		}

		bool GameTalkMessage::GetPtrTo8Bit(int index, uint8_t* userProvidedBuffer, size_t count, int startIndex) const
		{
			if (!userProvidedBuffer) // If user gives us a null pointer - unusable
			{
				SetError();
				return false;
			}

			if (index < 0 || index >= GetNumberOfKeys()) // If user requests an out of range index
			{
				SetError();
				return false;
			}

			size_t msgSize = GetSize(index);
			if (msgSize < 1) // Should we only check for less than one or just anytime the size isn't wholly divisable by one
			{
				SetError();
				return false;
			}

			if (startIndex + count > msgSize) // Check that the user isn't asking for more elements than are contained in the message
			{
				SetError();
				return false;
			}

			KeyContent* kc = mData[index];
			uint8_t* readBuffer = (uint8_t*)kc->Content();
			USER_GAMETALK_MEMCPY(userProvidedBuffer, &(readBuffer[startIndex]), count);
			return true;
		}

		bool GameTalkMessage::Get(int keyIndex, bool &value) const
		{
			int8_t v;
			if (Get(keyIndex, v))
			{
				value = (v!=0);
				return true;
			}
			else
			{
				return false;
			}
		}


		bool GameTalkMessage::Get(int keyIndex, float &value) const
		{
			return GetArray(keyIndex, &value, 0, 1);
		}

		bool GameTalkMessage::Get(int keyIndex, double &value) const
		{
			return GetArray(keyIndex, &value, 0, 1);
		}

		bool GameTalkMessage::Get(int keyIndex, int8_t &value) const
		{
			return GetArray(keyIndex, &value, 0, 1);
		}

		bool GameTalkMessage::Get(int keyIndex, uint8_t &value) const
		{
			return GetArray(keyIndex, &value, 0, 1);
		}

		bool GameTalkMessage::Get(int keyIndex, int16_t &value) const
		{
			return GetArray(keyIndex, &value, 0, 1);
		}

		bool GameTalkMessage::Get(int keyIndex, uint16_t &value) const
		{
			return GetArray(keyIndex, &value, 0, 1);
		}

		bool GameTalkMessage::Get(int keyIndex, int32_t &value) const
		{
			return GetArray(keyIndex, &value, 0, 1);
		}

		bool GameTalkMessage::Get(int keyIndex, uint32_t &value) const
		{
			return GetArray(keyIndex, &value, 0, 1);
		}

		bool GameTalkMessage::Get(int keyIndex, int64_t &value) const
		{
			return GetArray(keyIndex, &value, 0, 1);
		}

		bool GameTalkMessage::Get(int keyIndex, uint64_t &value) const
		{
			return GetArray(keyIndex, &value, 0, 1);
		}


		bool GameTalkMessage::Get(int keyIndex, const char *&string) const
		{
			if (GetFormat(keyIndex) != EA::GameTalk::StringFormat)
			{
				SetError();
				return false;
			}

			string = (const char*)this->GetContent(keyIndex);
			return true;
		}


		const char *GameTalkMessage::GetString(int keyIndex) const
		{
			if (GetFormat(keyIndex) != EA::GameTalk::StringFormat)
			{
				SetError();
				return NULL;
			}

			return (const char*)this->GetContent(keyIndex);
		}

		const char *GameTalkMessage::GetString(const char *key) const
		{
			int index = GetIndexOfKey(key);
			if (index < 0)
				return NULL;
			return GetString(index);
		}



		bool GameTalkMessage::Get(int index, IMessage* message, char* userBuffer, size_t userBufferSize) const
		{
			if (!message)
			{
				SetError();
				return false;
			}

			if(GetFormat(index) != EA::GameTalk::GameTalkMessageFormat)
			{
				SetError();
				return false;
			}

			char* serializedGtm = (char*)GetContent(index);
			if (!serializedGtm)
			{
				SetError();
				return false;
			}

			size_t serializedGtmSize = GetSize(index);
			if (serializedGtmSize == 0)
			{
				SetError();
				return false;
			}

			char * channelId = NULL;
			uint32_t nestedNumKeys = 0;
			if (!GameTalkMessage::ParseGameTalkMessageHeader(serializedGtm, nestedNumKeys, channelId))
			{
				SetError();
				return false;
			}

			GameTalkMessage* gtm = (GameTalkMessage*)message;
			gtm->mChannelId = channelId;
			GameTalkMessage::Create(serializedGtm, (serializedGtm + serializedGtmSize), userBuffer, (uint32_t)userBufferSize, nestedNumKeys, gtm, true);
			return true;
		}

		bool GameTalkMessage::GetArray(int index, float* outputArray, int startIndex, int count) const
		{
			// Have yet to add type-check on type requested vs. type contained in GameTalkMessage
			// compare this->GetFormat(index) with EA::GameTalk::Float
			return GetPtrTo32Bit(index, (uint32_t*)outputArray, count, startIndex);
		}

		bool GameTalkMessage::GetArray(int index, double* outputArray, int startIndex, int count) const
		{
			// Have yet to add type-check on type requested vs. type contained in GameTalkMessage
			// compare this->GetFormat(index) with EA::GameTalk::Double
			return GetPtrTo64Bit(index, (uint64_t*)outputArray, count, startIndex);
		}

		bool GameTalkMessage::GetArray(int index, int8_t* outputArray, int startIndex, int count) const
		{
			// Have yet to add type-check on type requested vs. type contained in GameTalkMessage
			// compare this->GetFormat(index) with EA::GameTalk::Int8
			return GetPtrTo8Bit(index, (uint8_t*)outputArray, count, startIndex);
		}

		// startIndex is the offset to read from the data in the payload of the message and count is the number of elements to read
		bool GameTalkMessage::GetArray(int index, uint8_t* outputArray, int startIndex, int count) const
		{
			// Have yet to add type-check on type requested vs. type contained in GameTalkMessage
			// compare this->GetFormat(index) with EA::GameTalk::UInt8
			return GetPtrTo8Bit(index, (uint8_t*)outputArray, count, startIndex);
		}

		// startIndex is the offset to read from the data in the payload of the message and count is the number of elements to read
		bool GameTalkMessage::GetArray(int index, int16_t* outputArray, int startIndex, int count) const
		{
			// Have yet to add type-check on type requested vs. type contained in GameTalkMessage
			// compare this->GetFormat(index) with EA::GameTalk::Int16
			return GetPtrTo16Bit(index, (uint16_t*)outputArray, count, startIndex);
		}

		// startIndex is the offset to read from the data in the payload of the message and count is the number of elements to read
		bool GameTalkMessage::GetArray(int index, uint16_t* outputArray, int startIndex, int count) const
		{
			// Have yet to add type-check on type requested vs. type contained in GameTalkMessage
			// compare this->GetFormat(index) with EA::GameTalk::UInt16
			return GetPtrTo16Bit(index, (uint16_t*)outputArray, count, startIndex);
		}

		bool GameTalkMessage::GetArray(int index, int32_t* outputArray, int startIndex, int count) const
		{
			// Have yet to add type-check on type requested vs. type contained in GameTalkMessage
			// compare this->GetFormat(index) with EA::GameTalk::Int32
			return GetPtrTo32Bit(index, (uint32_t*)outputArray, count, startIndex);
		}

		bool GameTalkMessage::GetArray(int index, uint32_t* outputArray, int startIndex, int count) const
		{
			// Have yet to add type-check on type requested vs. type contained in GameTalkMessage
			// compare this->GetFormat(index) with EA::GameTalk::UInt32
			return GetPtrTo32Bit(index, (uint32_t*)outputArray, count, startIndex);
		}

		bool GameTalkMessage::GetArray(int index, int64_t* outputArray, int startIndex, int count) const
		{
			// Have yet to add type-check on type requested vs. type contained in GameTalkMessage
			// compare this->GetFormat(index) with EA::GameTalk::Int64
			return GetPtrTo64Bit(index, (uint64_t*)outputArray, count, startIndex);
		}

		bool GameTalkMessage::GetArray(int index, uint64_t* outputArray, int startIndex, int count) const
		{
			// Have yet to add type-check on type requested vs. type contained in GameTalkMessage
			// compare this->GetFormat(index) with EA::GameTalk::UInt64
			return GetPtrTo64Bit(index, (uint64_t*)outputArray, count, startIndex);
		}

		bool GameTalkMessage::GetKeyContentMetaData(int index, FormatTypes& format, FormatTypes& typeCode, int& typeSize) const
		{
			if (index >= GetNumberOfKeys()) // Should we set all output parameters to -1, 0 or some useful default in this case?
				return false;

			KeyContent* kc = mData[index];
			if (kc == NULL)
				return false;

			format = static_cast<FormatTypes>(kc->Format());
			typeCode = kc->TypeCode();
			typeSize = kc->TypeSize();

			return true;
		}

		// If we change the return type to int - we could return a -1 to indicate the user has asked for content that doesn't exist
		size_t GameTalkMessage::GetArrayLength(int index) const
		{
			if (index >= GetNumberOfKeys())
				return 0;

			int typeSize = GetTypeSize(index);
			if (typeSize == 0) // Prevent divide by zero
				typeSize = 1;

			return GetSize(index) / typeSize;
		}

		int GameTalkMessage::GetTypeSize(int index) const
		{
			if (index >= GetNumberOfKeys())
				return -1;

			KeyContent* kc = mData[index];
			return kc->TypeSize();
		}

		// ------------------------------------------------------------------------------
		// GameTalkMessage :: GetSize
		// ------------------------------------------------------------------------------
		size_t GameTalkMessage::GetSize(int index) const
		{
			KeyContent* kc = mData[index];
			if (kc == NULL)
			{
				// We retest the condition (instead of just using false) in the assert to avoid compiler warning
				USER_GAMETALK_ASSERT(kc != NULL, "Key content is Null.");
				return 0;
			}
			else
				return kc->Size();
		}

		// ------------------------------------------------------------------------------
		// GameTalkMessage :: GetSize
		// Note: It is possible to have multiple keys with the same name contained within
		// a GameTalkMessage.  This function will return the first one that matches.
		// ------------------------------------------------------------------------------
		size_t GameTalkMessage::GetSize(const char* key) const
		{
			for (int i = 0; i < mDataCount; i++)
			{
				if (this->CompareKey(i, key))
				{
					return GetSize(i); // function already defined
				}
			}

			USER_GAMETALK_FAIL("This GameTalkMessage doesn't contain a matching key.");
			return 0;
		}

		// ------------------------------------------------------------------------------
		// GameTalkMessage :: ResizeKeyContentBuffer
		//
		// This method is used to increase the size of the KeyContent array by one (if necessary).
		// ------------------------------------------------------------------------------
		void GameTalkMessage::ResizeKeyContentBuffer()
		{
			if (mDataCount < mMaxDataLength)
				return;

			// Increase buffer dynamically by allocating a new one and copying the data over
			// There is a risk of creating fragmentation here...
			size_t originalSize = mMaxDataLength;

			// Double the buffer
			mMaxDataLength *= 2;
			KeyContent** newData = AllocateDataBuffer(mMaxDataLength);

			// Copy current key content
			for (size_t i = 0; i < originalSize; i++)
				newData[i] = mData[i];

			// Free current array and set new one
			InternalFree(mData, "GameTalkMessage:mData");
			mData = newData;
		}

		// ------------------------------------------------------------------------------
		// GameTalkMessage :: AddKeyContent
		//
		// This method is used by the user, so the key and the content need to be
		// released by the user.
		// ------------------------------------------------------------------------------
		void GameTalkMessage::AddKeyContent(const char* key, const FormatTypes format, const void* content, const size_t size)
		{
			ResizeKeyContentBuffer();
			// Should we default 0 for TypeCode (or should we use format for both format and type?) and 1 for TypeSize?
			KeyContent* keyContent = InstantiateKeyContent(key, format, content, size, USER_GAMETALK_IS_LITTLE_ENDIAN, (FormatTypes)0, 1);
			mData[mDataCount] = keyContent;
			mDataCount++;
		}

		// ------------------------------------------------------------------------------
		// GameTalkMessage :: AddKeyContent
		//
		// This method is used by the user, so the key and the content need to be
		// released by the user.  Note that the isLittleEndian is passed in - this is because
		// the KeyContent may be contained in a GameTalkMessage received from the socket (like a nested GameTalkMessage)
		// ------------------------------------------------------------------------------
		void GameTalkMessage::AddKeyContent(const char* key, int format, const void* content, const size_t size, bool isLittleEndian, int typeCode, size_t typeSize)
		{
			ResizeKeyContentBuffer();
			KeyContent* keyContent = InstantiateKeyContent(key, (FormatTypes)format, content, size, isLittleEndian, (FormatTypes)typeCode, (int)typeSize);
			mData[mDataCount] = keyContent;
			mDataCount++;
		}

		// ------------------------------------------------------------------------------
		// GameTalkMessage :: AddBinaryKeyContent
		//
		// This method is used by the user, so the key and the content need to be
		// released by the user.
		// ------------------------------------------------------------------------------
		void GameTalkMessage::AddBinaryKeyContent(const char* key, int typeCode, const void* content, const size_t size, size_t typeSize)
		{
			AddKeyContent(key, EA::GameTalk::BinaryFormat, content, size, USER_GAMETALK_IS_LITTLE_ENDIAN, typeCode, typeSize);
		}

		void GameTalkMessage::Add(const char* key, bool value)
		{
			int8_t boolValue = (int8_t)(value ? 1 : 0);
			Add(key, boolValue);
		}

		void GameTalkMessage::Add(const char* key, float value)
		{
			AddArray(key, &value, 1);
		}

		void GameTalkMessage::Add(const char* key, double value)
		{
			AddArray(key, &value, 1);
		}

		void GameTalkMessage::Add(const char* key, int8_t value)
		{
			AddArray(key, &value, 1);
		}

		void GameTalkMessage::Add(const char* key, uint8_t value)
		{
			AddArray(key, &value, 1);
		}

		void GameTalkMessage::Add(const char* key, int16_t value)
		{
			AddArray(key, &value, 1);
		}

		void GameTalkMessage::Add(const char* key, uint16_t value)
		{
			AddArray(key, &value, 1);
		}

		void GameTalkMessage::Add(const char* key, int32_t value)
		{
			AddArray(key, &value, 1);
		}

		void GameTalkMessage::Add(const char* key, uint32_t value)
		{
			AddArray(key, &value, 1);
		}

		void GameTalkMessage::Add(const char* key, int64_t value)
		{
			AddArray(key, &value, 1);
		}

		void GameTalkMessage::Add(const char* key, uint64_t value)
		{
			AddArray(key, &value, 1);
		}

		void GameTalkMessage::Add(const char* key, const char *string)
		{
			// send string along with null terminator
			AddKeyContent(key, EA::GameTalk::StringFormat, string, USER_GAMETALK_STRLEN(string) + 1);
		}

		void GameTalkMessage::Add(const char* key, const IMessage* content)
		{
			if (!content)
				return;

			const GameTalkMessage* gtm = (const GameTalkMessage*)content;
			size_t gtmSize = gtm->GetChannelAndKeyContentSize(gtm->GetChannelId());
			char* serializedGtm = (char*)InternalAlloc(gtmSize, "GameTalkMessage:serializedGtm");
			char* startOfSerializedGtm = serializedGtm;
			gtm->SerializeGameTalkMessageContents((char*&)serializedGtm);
			AddBinaryKeyContent(key, EA::GameTalk::GameTalkMessageFormat, startOfSerializedGtm, gtmSize, gtmSize);
			// AddKeyContent will dynamically allocate memory and copy the contents of the serializedGtm into it - so safe to free memory now
			InternalFree(startOfSerializedGtm, "GameTalkMessage:serializedGtm");
		}

		void GameTalkMessage::AddArray(const char* key, const float* content, const int count)
		{
			AddBinaryKeyContent(key, EA::GameTalk::Float, (uint32_t*)content, count * sizeof(float), sizeof(float));
		}

		void GameTalkMessage::AddArray(const char* key, const double* content, const int count)
		{
			AddBinaryKeyContent(key, EA::GameTalk::Double, (uint64_t*)content, count * sizeof(double), sizeof(double));
		}

		void GameTalkMessage::AddArray(const char* key, const int8_t* content, const int count)
		{
			AddBinaryKeyContent(key, EA::GameTalk::Int8, (uint8_t*)content, count * sizeof(int8_t), sizeof(int8_t));
		}

		void GameTalkMessage::AddArray(const char* key, const uint8_t* content, const int count)
		{
			AddBinaryKeyContent(key, EA::GameTalk::UInt8, (uint8_t*)content, count * sizeof(uint8_t), sizeof(uint8_t));
		}

		void GameTalkMessage::AddArray(const char* key, const int16_t* content, const int count)
		{
			AddBinaryKeyContent(key, EA::GameTalk::Int16, (uint16_t*)content, count * sizeof(int16_t), sizeof(int16_t));
		}

		void GameTalkMessage::AddArray(const char* key, const uint16_t* content, const int count)
		{
			AddBinaryKeyContent(key, EA::GameTalk::UInt16, (uint16_t*)content, count * sizeof(uint16_t), sizeof(uint16_t));
		}

		void GameTalkMessage::AddArray(const char* key, const int32_t* content, const int count)
		{
			AddBinaryKeyContent(key, EA::GameTalk::Int32, (uint32_t*)content, count * sizeof(int32_t), sizeof(int32_t));
		}

		void GameTalkMessage::AddArray(const char* key, const uint32_t* content, const int count)
		{
			AddBinaryKeyContent(key, EA::GameTalk::UInt32, (uint32_t*)content, count * sizeof(uint32_t), sizeof(uint32_t));
		}

		void GameTalkMessage::AddArray(const char* key, const int64_t* content, const int count)
		{
			AddBinaryKeyContent(key, EA::GameTalk::Int64, (uint64_t*)content, count * sizeof(int64_t), sizeof(int64_t));
		}

		void GameTalkMessage::AddArray(const char* key, const uint64_t* content, const int count)
		{
			AddBinaryKeyContent(key, EA::GameTalk::UInt64, (uint64_t*)content, count * sizeof(uint64_t), sizeof(uint64_t));
		}

		// ------------------------------------------------------------------------------
		// GameTalkMessage :: AddKeyContentAsString
		//
		// Convenience member function that is a wrapper for AddKeyContent
		// ------------------------------------------------------------------------------
		void GameTalkMessage::AddKeyContentAsString(const char* key, const char* stringContent)
		{
			Add(key, stringContent);
		}

		int GameTalkMessage::AddRef()
		{
			return ++mRefCount;
		}

		int GameTalkMessage::Release()
		{
			int count = --mRefCount;
			if (count <= 0)
			{
				// delete ourselves
				if (mOwner != NULL)
				{
					// if we have an owner then let our owner delete ourselves
					mOwner->DeleteMessage(this);
				}
				else
				{
					// if no owner then just delete this
					delete this;
				}
			}
			return count;
		}

		int GameTalkMessage::GetRefCount()
		{
			return mRefCount;
		}

		IConnection* GameTalkMessage::GetConnection() const
		{
			return mOwner;
		}

		bool GameTalkMessage::IsArray(int keyIndex) const
		{
			return (GetArrayLength(keyIndex) > 1);
		}

		void GameTalkMessage::Clear()
		{
			for (int i = 0; i < mDataCount; i++)
			{
				// Free each of the new'ed KeyContents - the delete should use the custom delete in KeyContent
				DeallocateKeyContent(mData[i]);
				mData[i] = NULL;
			}
		}

		const char* GameTalkMessage::GetSender() const
		{
			return mSender;
		}

		// ------------------------------------------------------------------------------
		// GameTalkMessage :: AllocateDataBuffer
		//
		// Allocate a buffer for the messages
		// ------------------------------------------------------------------------------
		KeyContent** GameTalkMessage::AllocateDataBuffer(size_t numKeyContents)
		{
			int* getPtrSize = NULL;
			USER_GAMETALK_USE_VAR_TO_AVOID_WARNING(getPtrSize);
			int ptrSize = sizeof(getPtrSize);
			// This allocation always gets assigned to GameTalkMessage::mData (eventually) - hence the allocation name
			KeyContent** buffer = (KeyContent**)InternalAlloc(ptrSize * numKeyContents, "GameTalkMessage:mData");
			for (size_t i = 0; i < numKeyContents; i++)
				buffer[i] = NULL;

			return buffer;
		}

		// ------------------------------------------------------------------------------
		// GameTalkMessage :: WriteBuffer
		// ------------------------------------------------------------------------------
		void GameTalkMessage::WriteBuffer(char*& outBuffer, const char* inBuffer, size_t inSize)
		{
			USER_GAMETALK_MEMCPY(outBuffer, inBuffer, inSize);
			char* curBuffer = (char*)outBuffer;
			curBuffer += inSize;
			outBuffer = curBuffer;
		}

		// ------------------------------------------------------------------------------
		// GameTalkMessage :: ReadString
		//
		// ReadString - this
		// 1. gets the size from the start of the char array
		// 2. moves the string pointer passed into ReadString to the next block of data
		// 3. returns a pointer to the start of that string
		// ------------------------------------------------------------------------------
		char* GameTalkMessage::ReadString(char*& nextString)
		{
			// Read the size, the first 4 bytes
			uint32_t size = EndianSwap::ReadLE32(nextString);
			// Read the string
			char* result = nextString;
			// Increment next string
			nextString += size;

			return result;
		}

		// ------------------------------------------------------------------------------
		// GameTalkMessage :: ReadString
		//
		// ReadString - this
		// 1. gets the size from the start of the char array
		// 2. moves the string pointer passed into ReadString to the next block of data
		// 3. returns a pointer to the start of that string and by reference parameter returns how large the string is
		// ------------------------------------------------------------------------------
		char* GameTalkMessage::ReadString(char*& nextString, uint32_t& size)
		{
			// Read the size, the first 4 bytes
			size = EndianSwap::ReadLE32(nextString);
			// Read the string
			char* result = nextString;
			// Increment next string
			nextString += size;

			return result;
		}

		// ------------------------------------------------------------------------------
		// GameTalkMessage :: ReadBinary
		// ------------------------------------------------------------------------------
		char* GameTalkMessage::ReadBinary(char*& nextBuffer, uint32_t& size)
		{
			// Read the size, the first 4 bytes
			size = EndianSwap::ReadAndNullLE32(nextBuffer);
			char* result = nextBuffer;
			// Increment next string
			nextBuffer += size;

			return result;
		}

		void* GameTalkMessage::InternalAlloc(size_t size, const char* name) const
		{
			// If we don't want all internal allocations to go through the mGameTalkAllocator
			// we could only initialize the mGameTalkAllocator when the user explicitly 
			// provides a buffer to GameTalkMessage - thus add in a check here for 
			// if(mGameTalkAllocator) and if it is null then use EA::GameTalk::Alloc(size, name)
			if (!mGameTalkAllocator)
			{
				USER_GAMETALK_FAIL("mGameTalkAllocator has not been instantiated - cannot allocate anything for this GameTalkMessage in this way.");
				return NULL;
			}

			return mGameTalkAllocator->Alloc(size, name);
		}

		void GameTalkMessage::InternalFree(void *ptr, const char* name) const
		{
			// If we don't want all internal deallocations to go through the mGameTalkAllocator
			// we could only initialize the mGameTalkAllocator when the user explicitly 
			// provides a buffer to GameTalkMessage - thus add in a check here for 
			// if(mGameTalkAllocator) and if it is null then use EA::GameTalk::Free(ptr, name)
			if (!mGameTalkAllocator)
			{
				USER_GAMETALK_FAIL("mGameTalkAllocator has not been instantiated - cannot free anything for this GameTalkMessage in this way.");
				return;
			}

			mGameTalkAllocator->Free(ptr, name);
		}

		KeyContent* GameTalkMessage::InstantiateKeyContent(const char * key, FormatTypes format, 
			const void* content, const size_t size, bool littleEndian, 
			FormatTypes typeCode, int typeSize)
		{
			// If we don't want all internal allocations to go through the mGameTalkAllocator
			// we could only initialize the mGameTalkAllocator when the user explicitly 
			// provides a buffer to GameTalkMessage - thus add in a check here for 
			// if(mGameTalkAllocator) and if it is null then just use new (not placement new)
			size_t keyContentSize = sizeof(KeyContent);
			void* buffer = mGameTalkAllocator->Alloc(keyContentSize, "GameTalk:KeyContent");
			return new (buffer) KeyContent(key, format, content, size, littleEndian, (FormatTypes)typeCode, typeSize, mGameTalkAllocator);
		}

		void GameTalkMessage::DeallocateKeyContent(KeyContent* keyContent)
		{
			// If we don't want all internal allocations to go through the mGameTalkAllocator
			// we could only initialize the mGameTalkAllocator when the user explicitly 
			// provides a buffer to GameTalkMessage - thus add in a check here for 
			// if(mGameTalkAllocator) and if it is null then use regular delete
			keyContent->~KeyContent();
			InternalFree(keyContent, "GameTalk:KeyContent");
		}


		namespace SyncReply
		{
			bool ContainsError(GameTalkMessage* gtm)
			{
				if ((gtm) && (gtm->GetNumberOfKeys() > 0) && (USER_GAMETALK_STREQUAL(gtm->GetKey(0), GameTalkMessage::sSynchronousGameTalkIssue)))
					return true;

				return false;
			}
		}

		SyncReplyGameTalkMessage::SyncReplyGameTalkMessage(GameTalkMessage* gameTalkMessage, GameTalkInstance* gameTalkInstance, const char * channelToReplyOn, const char* returnTargetName)
			:GameTalkMessage(channelToReplyOn),
			mGameTalkInstance(gameTalkInstance), mReplied(false)
		{
			mSender = returnTargetName;
			uint32_t synchroId = 0;
			if (SyncReplyGameTalkMessage::GetSynchroId(gameTalkMessage, synchroId))
				this->AddChunkData(&synchroId, GameTalkMessage::SynchroId, sizeof(uint32_t));
		}

		bool SyncReplyGameTalkMessage::Reply()
		{
			mReplied = mGameTalkInstance->SendMessage(GetSender(), this);
			return mReplied;
		}

		bool SyncReplyGameTalkMessage::GetSynchroId(GameTalkMessage* gtm, uint32_t& syncId)
		{
			size_t synchroDataSize = 0;
			void* synchroData = gtm->GetChunkData(GameTalkMessage::SynchroId, synchroDataSize);
			if (synchroData == NULL)
			{
				USER_GAMETALK_FAIL("Why is our synchronized message missing its synchro id?");
				return false;
			}
			if (synchroDataSize != sizeof(uint32_t))
			{
				USER_GAMETALK_ASSERT(synchroDataSize == sizeof(uint32_t), "Why isn't the synchro id the same size as size_t data");
				return false;
			}

			uint32_t* synchroDataIntPtr = (uint32_t*)synchroData;
			syncId = (*synchroDataIntPtr);
			return true;
		}
	}
}