#include <GameTalk/GameTalkBase.h>
#include <GameTalk/GameTalkInstance.h>


namespace EA
{
	namespace GameTalk
	{
		// --- Variables
		const char * GameTalkInstance::mVersion = "4.5.4.0";

		GameTalkManager* GameTalkManager::sInstance = NULL;

		GameTalkInstance * GameTalkManager::sGameTalkInstance = NULL;


		class SynchronousTransaction
		{
		public:
			SynchronousTransaction(GameTalkInstance *instance, uint32_t syncId, GameTalkMessage * sentMessage, GameTalkMessage * replyMessage )                
				:
			mInstance(instance),
				mSyncId(syncId),
				mSentMessage(sentMessage),
				mReplyMessage(replyMessage),
				mReceivedReply(false)
			{}

			GameTalkInstance * mInstance;
			uint32_t          mSyncId;
			GameTalkMessage * mSentMessage;
			GameTalkMessage * mReplyMessage;
			bool              mReceivedReply;

			USE_GAMETALK_ALLOC("GameTalk::SynchronousTransaction")
		};


		// --- GameTalkHandler
		class GameTalkHandler
		{
		public:
			GameTalkHandler(const char* channelId, GameTalkHandlerFnc* gameTalkHandlerFnc):
			  mChannelId(channelId),
				  mGameTalkHandlerFnc(gameTalkHandlerFnc),
				  mUserGameTalkHandlerFnc(NULL),
				  mSyncGameTalkHandlerFnc(NULL),
				  mMessageHandlerFnc(NULL),
				  mUserData(NULL)
			  {}

			  GameTalkHandler(const char* channelId, UserGameTalkHandlerFnc* userGameTalkHandlerFnc, void * userData):
			  mChannelId(channelId),
				  mGameTalkHandlerFnc(NULL),
				  mUserGameTalkHandlerFnc(userGameTalkHandlerFnc),
				  mSyncGameTalkHandlerFnc(NULL),
				  mMessageHandlerFnc(NULL),
				  mUserData(userData)
			  {}

			  GameTalkHandler(const char* channelId, SyncGameTalkHandlerFnc* syncGameTalkHandlerFnc, void * userData):
			  mChannelId(channelId),
				  mGameTalkHandlerFnc(NULL),
				  mUserGameTalkHandlerFnc(NULL),
				  mSyncGameTalkHandlerFnc(syncGameTalkHandlerFnc),
				  mMessageHandlerFnc(NULL),
				  mUserData(userData)
			  {}

			  GameTalkHandler(const char* channelId, MessageHandler messageHandlerFunc, void * userData):
			  mChannelId(channelId),
				  mGameTalkHandlerFnc(NULL),
				  mUserGameTalkHandlerFnc(NULL),
				  mSyncGameTalkHandlerFnc(NULL),
				  mMessageHandlerFnc(messageHandlerFunc),
				  mUserData(userData)
			  {}

			  ~GameTalkHandler() {};

			  const char* GetChannelId() const { return mChannelId; }
			  void * GetUserData() const { return mUserData; }
			  GameTalkHandlerFnc* GetHandlerFnc() const { return mGameTalkHandlerFnc; }
			  UserGameTalkHandlerFnc* GetUserHandlerFnc() const { return mUserGameTalkHandlerFnc; }
			  SyncGameTalkHandlerFnc* GetSynchroHandlerFnc() const { return mSyncGameTalkHandlerFnc; }
			  MessageHandler          GetMessageHandlerFnc() const { return mMessageHandlerFnc; }

			  void Invoke(GameTalkInstance *connection, GameTalkMessage* message)
			  {
				  if (mGameTalkHandlerFnc)
					  (*mGameTalkHandlerFnc)(message);
				  else if (mUserGameTalkHandlerFnc)
					  (*mUserGameTalkHandlerFnc)(message, mUserData);
				  else if (mMessageHandlerFnc)
				  {
					  mMessageHandlerFnc(
						  connection, 
						  message->GetSender(),
						  message->GetChannelId(),
						  message,
						  NULL,
						  mUserData
						  );
				  }
			  }

			  void Invoke(GameTalkInstance *connection, GameTalkMessage* message, SyncReplyGameTalkMessage* synReplyMessage, const char *channel)
			  {
				  if (mSyncGameTalkHandlerFnc)
					  (*mSyncGameTalkHandlerFnc)(message, synReplyMessage, mUserData);
				  else if (mMessageHandlerFnc)
				  {
					  mMessageHandlerFnc(
						  connection, 
						  message->GetSender(),
						  channel,
						  message,
						  synReplyMessage,
						  mUserData
						  );

					  // send reply
					  synReplyMessage->Reply();
				  }
			  }

			  USE_GAMETALK_ALLOC("GameTalk::GameTalkHandler")

		private:
			GameTalkHandler();

			const char* mChannelId;
			GameTalkHandlerFnc* mGameTalkHandlerFnc;

			UserGameTalkHandlerFnc* mUserGameTalkHandlerFnc;
			SyncGameTalkHandlerFnc* mSyncGameTalkHandlerFnc;
			MessageHandler          mMessageHandlerFnc;
			void * mUserData;
		};

		// ------------------------------------------------------------------------------
		// GameTalkInstance :: GameTalkInstance
		// ------------------------------------------------------------------------------
		GameTalkInstance::GameTalkInstance(IGameTalkProtocol* protocol, uint32_t maxHandlersCount, const char * targetName,
			bool freeGameTalkProtocol, bool reconnect, uint32_t receiveStrBufferSize)
			:
		mGameTalkProtocol(protocol),
			mRegisteredMessageHandlers(NULL),
			mRegisteredMessageHandlersCount(0),
			mMaxHandlersCount(maxHandlersCount),
			mTargetName(NULL),
			mFreeGameTalkProtocol(freeGameTalkProtocol),
			mKeyContentBuffer(NULL),
			mSyncMsgUidCounter(0),
			mReconnect(reconnect),
			mReceivedMsg(false),
			mReceiveStrBufferSize(receiveStrBufferSize),
			mSendInitInfoToServer(false)
		{
			USER_GAMETALK_ASSERT(protocol != NULL, "Protocol is invalid.");
			mGameTalkProtocol->RegisterReceiverCallback(&EA::GameTalk::GameTalkInstance::ReceiverCallback, this);

			// Use dynamic allocation for target name
			AssignTargetName(targetName);

			int* getPtrSize = NULL;
			USER_GAMETALK_USE_VAR_TO_AVOID_WARNING(getPtrSize);
			size_t ptrSize = sizeof(getPtrSize);
			mRegisteredMessageHandlers = (GameTalkHandler**)GameTalk::Alloc(ptrSize * maxHandlersCount, "GameTalkInstance:mRegisteredMessageHandlers");
			for (uint32_t i = 0; i < mMaxHandlersCount; i++)
				mRegisteredMessageHandlers[i] = NULL;

			// Initialize our mSynchroMsgTable
			for (uint32_t synchroMsgIndex = 0; synchroMsgIndex < USER_GAMETALK_MAX_PENDING_SYNCHRO_TRANSACTIONS; ++synchroMsgIndex)
				mSynchroMsgTable[synchroMsgIndex] = NULL;

			// In passive mode we don't want to send the initialize packet because it is the GameTalkServer that will tell us our name
			if (mGameTalkProtocol->ShouldSendInitializePacket()) 
			{
				if (IsConnected())
					SendInitializePacket();
				else // Since in non-blocking (sockets) mode we may not instantly become connected - just set a flag to remind ourselves to eventually send the init info to the server
					mSendInitInfoToServer = true;
			}

			InitializeKCBuffer();
			// Even if we're in passive mode (not yet connected) - this channel will be registered (with the GTS) upon connection
			RegisterMessageHandler(HandleSynchronousMessages, GameTalkMessage::sSynchronousMessageID, this);
			RegisterMessageHandler(SynchronousMessageResponseHandler, GameTalkMessage::sSynchroResponseMessageID, this);
		}

		// ------------------------------------------------------------------------------
		// GameTalkInstance :: ~GameTalkInstance
		// ------------------------------------------------------------------------------
		GameTalkInstance::~GameTalkInstance()
		{
			UnregisterMessageHandler(HandleSynchronousMessages);
			UnregisterMessageHandler(SynchronousMessageResponseHandler);
			USER_GAMETALK_ASSERT(mRegisteredMessageHandlersCount == 0, "Message handlers should be unregistered before releasing the Message Manager!");

			GameTalk::Free(mRegisteredMessageHandlers, "GameTalkInstance:mRegisteredMessageHandlers");
			mRegisteredMessageHandlers = NULL;

			USER_GAMETALK_FREE(mTargetName, "GameTalkInstance");
			mTargetName = NULL;

			if (mFreeGameTalkProtocol)
			{
				delete mGameTalkProtocol;
				mGameTalkProtocol = NULL;
			}

			ReleaseKCBuffer();
		}

		IMessage* GameTalkInstance::CreateMessage()
		{
			GameTalkMessage* gtm = new GameTalkMessage(NULL);
			gtm->mOwner = this;
			gtm->mAllocated = true;
			return gtm;
		}

		IMessage* GameTalkInstance::CreateMessage(uint8_t *tempBuffer, size_t tempBufferSize)
		{
			size_t gtmSize = sizeof(GameTalkMessage);
			if (tempBufferSize < gtmSize)
				return CreateMessage(); // just use normal create function

			// use the "placement new" to pull all allocated bytes directly from the user provided buffer (tempBuffer)
			GameTalkMessage* gtm = new (tempBuffer) GameTalkMessage(NULL, (char *)tempBuffer + gtmSize, tempBufferSize - gtmSize);
			gtm->mOwner = this;
			gtm->mAllocated = false;
			return gtm;
		}

		void GameTalkInstance::DeleteMessage(GameTalkMessage *message)
		{
			if (message->mAllocated)
			{
				delete message;
			}
			else
			{
				// placement new was used, just call destructor
				message->~GameTalkMessage();
			}
		}


		// ------------------------------------------------------------------------------
		// GameTalkInstance :: IsConnected
		// ------------------------------------------------------------------------------
		bool GameTalkInstance::IsConnected() const
		{
			return mGameTalkProtocol->IsConnected();
		}

		// ------------------------------------------------------------------------------
		// GameTalkInstance :: SendMessage
		// ------------------------------------------------------------------------------
		bool GameTalkInstance::SendMessage(const char* target, GameTalkMessage* message)
		{
			bool sendSuccessful = false;
			// The message sent through the network protocol
			char* buffer = NULL;
			size_t msgSize = message->CreateBuffer(target, buffer);
			// The user can keep a direct reference to the member mGameTalkProtocol
			// Therefore, they may have deleted it - we may want to build something to 
			// allow us to check if our pointer is valid (before we try to Send (example register or unregistering a server-side channel)
			sendSuccessful = mGameTalkProtocol->Send(buffer, msgSize);
			message->InternalFree(buffer, "GameTalkInstance:GameTalkMessage serialization buffer");
			return sendSuccessful;
		}

		bool GameTalkInstance::Send(const char* target, const char *channel, const IMessage * message)
		{
			if (!message)
				return false;

			GameTalkMessage* gtm = (GameTalkMessage*)message;
			// The user is giving us a channel - therefore, we'll use that instead of whatever might be in the GameTalkMessage
			char* buffer;
			size_t msgSize = gtm->CreateBuffer(target, buffer, channel);
			bool sendSuccessful = mGameTalkProtocol->Send(buffer, msgSize);
			gtm->InternalFree(buffer, "GameTalkInstance:GameTalkMessage buffer");
			return sendSuccessful;
		}

		bool GameTalkInstance::SendRegisteredChannelsToServer()
		{
			bool allSendsSucceeded = true;
			for (uint32_t curIndex = 0; curIndex < mMaxHandlersCount; curIndex++)
			{
				GameTalkHandler * curMessageHandler = mRegisteredMessageHandlers[curIndex];
				if (curMessageHandler != NULL)
				{
					if (!SendServerChannel(curMessageHandler->GetChannelId(), true))
						allSendsSucceeded = false;
				}
			}
			return allSendsSucceeded;
		}

		// ------------------------------------------------------------------------------
		// GameTalkInstance :: ConfigHandler
		// ------------------------------------------------------------------------------
		void GameTalkInstance::ConfigHandler(GameTalkMessage* message)
		{
			if (message->GetNumberOfKeys() == 0)
				return;

			if (USER_GAMETALK_STREQUAL(message->GetKey(0), GameTalkMessage::sUpdateTargetName))
			{
				const char * targetName = static_cast<const char*>(message->GetContent(0));
				AssignTargetName(targetName);

				// Send our GameTalk version - essential version is sent before channels to allow GameTalkServer chance to remove "all channels" registration
				bool keywordMessageSent = SendKeywordMessage("gametalk.config.version", mVersion);
				USER_GAMETALK_ASSERT(keywordMessageSent, "Wasn't able to send GameTalk version to GameTalkServer");
				USER_GAMETALK_USE_VAR_TO_AVOID_WARNING(keywordMessageSent);

				// When a GameTalk client sits on a socket and waits for the GameTalkServer to connect to it.  At the moment the
				// GameTalkServer connects to it - it needs to register its server side channels - hence the following:
				bool channelsRegistered = SendRegisteredChannelsToServer();
			    USER_GAMETALK_ASSERT(channelsRegistered , "Wasn't able to register all server-side channels with GameTalkServer - this can result in the GameTalkServer not forwarding messages on channels you wanted to register on.");
			    USER_GAMETALK_USE_VAR_TO_AVOID_WARNING(channelsRegistered);
			}
		}

		// ------------------------------------------------------------------------------
		// GameTalkInstance :: ReceiveMessage
		// ------------------------------------------------------------------------------
		void GameTalkInstance::ReceiveMessage(GameTalkMessage* message)
		{
			GameTalkHandler* curMessageHandler = NULL;
			// In case the user is using the "blocking poll" we set this flag to indicate our GameTalkMessage callback has been invoked
			mReceivedMsg = true;
			// By not just registering this code on a channel the user isn't able to deregister this
			if (IsMessageMatching(message->GetChannelId(), GameTalkMessage::sServerMessageID))
				ConfigHandler(message);

			for (uint32_t curIndex = 0; curIndex < mMaxHandlersCount; curIndex++)
			{
				curMessageHandler = mRegisteredMessageHandlers[curIndex];
				if (curMessageHandler != NULL)
				{
					if (IsMessageMatching(message->GetChannelId(), curMessageHandler->GetChannelId()))
					{
						curMessageHandler->Invoke(this, message);
					}
				}
			}
		}

		// ------------------------------------------------------------------------------
		// GameTalkInstance :: Poll
		//
		// Poll the network for messages
		// ------------------------------------------------------------------------------
		int GameTalkInstance::Poll()
		{
			int bytesReceived = mGameTalkProtocol->Poll();
			if ((mReconnect) && (bytesReceived < 0)) // We've had a connection error if bytesReceived is negative
			{
				mGameTalkProtocol->Connect();
				if (mGameTalkProtocol->ShouldSendInitializePacket()) // Since in non-blocking (sockets) mode we may not instantly become connected - just set a flag to remind ourselves to eventually send the init info to the server
					mSendInitInfoToServer = true;
			}

			if (mSendInitInfoToServer)
			{
				// We only want to send the name, version, + platform to server if we're not in passive mode
				// since the server is responsible for telling us our name - at which point we'll send it our version (and our registered channels)
				if (IsConnected()) // We could also check (mGameTalkProtocol->ShouldSendInitializePacket()) to ensure we should be sending this - but the mSendInitInfoToServer shouldn't have been set (to true) if we shouldn't
				{
					// We want to test the below functions (to ensure they succeeded) before setting mSendInitInfoToServer (to false)
					if (SendInitializePacket() && SendRegisteredChannelsToServer()) // Since we've successfully sent our initilize package (our name, platform, etc) AND our registered channels - we no longer need to send that info - so update our member variable
						mSendInitInfoToServer = false;
				}
			}

			if ((mReconnect) && (bytesReceived < 0) && (!mSendInitInfoToServer)) // We've had a connection error if bytesReceived is negative and because mSendInitInfoToServer is false - we either have sent the initialization information or don't need to
			{
				bool reconnectedSuccessfully = IsConnected();
				if (reconnectedSuccessfully)
					bytesReceived = mGameTalkProtocol->Poll();
			}

			return bytesReceived;
		}

		int GameTalkInstance::Poll(bool blocking)
		{
			mReceivedMsg = false;
			int numBytes = 0;
			this->mGameTalkProtocol->SetSocketBlocking(blocking);
			for (int i = 0;(i < USER_GAMETALK_MAX_BLOCKING_POLLS) && (!mReceivedMsg) && (numBytes >= 0);i++)
				numBytes += Poll();

			mReceivedMsg = false;
			if (blocking)
				this->mGameTalkProtocol->SetSocketBlocking(false);
			return numBytes;
		}

		// ------------------------------------------------------------------------------
		// GameTalkInstance :: SendServerChannel
		//
		// Sends an add or remove channel message to the GameTalkServer.
		// ------------------------------------------------------------------------------
		bool GameTalkInstance::SendServerChannel(const char * filter, bool registerChannel)
		{
			const char * messageKey;
			if (registerChannel)
				messageKey = GameTalkMessage::sAddChannelFilter;
			else
				messageKey = GameTalkMessage::sRemoveChannelFilter;

			EA::GameTalk::GameTalkMessage gtm("Client Message");
			gtm.AddKeyContent(messageKey, EA::GameTalk::StringFormat, filter, USER_GAMETALK_STRLEN(filter));
			return SendMessage("GameTalkServer", &gtm);
		}

		// ------------------------------------------------------------------------------
		// GameTalkInstance :: RegisterMessageHandler
		//
		// Registers a MessageHandler to receive messages.
		// ------------------------------------------------------------------------------
		bool GameTalkInstance::RegisterMessageHandler(GameTalkHandlerFnc* handlerFunc, const char* filter)
		{
			USER_GAMETALK_ASSERT(mRegisteredMessageHandlersCount < mMaxHandlersCount, "All available handlers in use. Increase default max handlers.");

			bool registered = false;
			// Find the first emtpy slot
			for (uint32_t handlerIndex = 0; handlerIndex < mMaxHandlersCount; handlerIndex++)
			{
				if (mRegisteredMessageHandlers[handlerIndex] == NULL)
				{
					mRegisteredMessageHandlers[handlerIndex] = new GameTalkHandler(filter, handlerFunc);
					mRegisteredMessageHandlersCount++;

					registered = SendServerChannel(filter, true);
					break;
				}
			}

			return registered;
		}

		// ------------------------------------------------------------------------------
		// GameTalkInstance :: RegisterMessageHandler
		//
		// Registers a MessageHandler to receive messages - allows user to specify user data to pass through to callback.
		// ------------------------------------------------------------------------------
		bool GameTalkInstance::RegisterMessageHandler(UserGameTalkHandlerFnc* handlerFunc, const char* filter, void * userData)
		{
			USER_GAMETALK_ASSERT(mRegisteredMessageHandlersCount < mMaxHandlersCount, "All available handlers in use. Increase default max handlers.");

			bool registered = false;
			// Find the first emtpy slot
			for (uint32_t handlerIndex = 0; handlerIndex < mMaxHandlersCount; handlerIndex++)
			{
				if (mRegisteredMessageHandlers[handlerIndex] == NULL)
				{
					mRegisteredMessageHandlers[handlerIndex] = new GameTalkHandler(filter, handlerFunc, userData);
					mRegisteredMessageHandlersCount++;

					registered = SendServerChannel(filter, true);
					break;
				}
			}

			return registered;
		}

		// ------------------------------------------------------------------------------
		// GameTalkInstance :: UnregisterMessageHandler

		// Removes a MessageHandler from receiving messages.
		// ------------------------------------------------------------------------------
		void GameTalkInstance::UnregisterMessageHandler(GameTalkHandlerFnc* handlerFunc)
		{
			// go through all the slots
			for (uint32_t handlerIndex = 0; handlerIndex < mMaxHandlersCount; handlerIndex++)
			{
				if (mRegisteredMessageHandlers[handlerIndex] != NULL)
				{
					GameTalkHandler* handler = mRegisteredMessageHandlers[handlerIndex];
					GameTalkHandlerFnc* func = handler->GetHandlerFnc();

					if (func == handlerFunc)
					{
						// Tell GameTalkServer that this client won't be using this channel anymore
						bool sentMessage = SendServerChannel(handler->GetChannelId(), false);
						if (!sentMessage)
						{
							// something wrong with our connection - what more we can do?
						}

						delete handler;
						mRegisteredMessageHandlers[handlerIndex] = NULL;
						mRegisteredMessageHandlersCount--;
						break;
					}
				}
			}
		}

		// ------------------------------------------------------------------------------
		// GameTalkInstance :: UnregisterMessageHandler

		// Removes a MessageHandler from receiving messages.
		// ------------------------------------------------------------------------------
		void GameTalkInstance::UnregisterMessageHandler(UserGameTalkHandlerFnc* handlerFunc, const char * channelId)
		{
			// go through all the slots
			for (uint32_t handlerIndex = 0; handlerIndex < mMaxHandlersCount; handlerIndex++)
			{
				if (mRegisteredMessageHandlers[handlerIndex] != NULL)
				{
					GameTalkHandler* handler = mRegisteredMessageHandlers[handlerIndex];
					UserGameTalkHandlerFnc* func = handler->GetUserHandlerFnc();
					if (func == handlerFunc)
					{
						if ((!channelId) || (USER_GAMETALK_STREQUAL(handler->GetChannelId(), channelId)))
						{
							// Tell GameTalkServer that this client won't be using this channel anymore
							bool sentMessage = SendServerChannel(handler->GetChannelId(), false);
							if (!sentMessage)
							{
								// something wrong with our connection - what more we can do?
								// perhaps put this in a list of channels that need to be unregistered on the server (once we regain our connection)?
								// It isn't critical we unregister our channels from the server - since we filter 
								// incoming messages in our client code regardless
							}

							delete handler;
							mRegisteredMessageHandlers[handlerIndex] = NULL;
							mRegisteredMessageHandlersCount--;
							break;
						}
					}
				}
			}
		}

		bool GameTalkInstance::RegisterSynchroMessageHandler(SyncGameTalkHandlerFnc* syncGameTalkHandler, const char* filter, void * userData)
		{
			bool registered = false;
			// Find the first emtpy slot
			for (uint32_t handlerIndex = 0; handlerIndex < mMaxHandlersCount; handlerIndex++)
			{
				if (mRegisteredMessageHandlers[handlerIndex] == NULL)
				{
					mRegisteredMessageHandlers[handlerIndex] = new GameTalkHandler(filter, syncGameTalkHandler, userData);
					mRegisteredMessageHandlersCount++;
					registered = true;
					break;
				}
			}

			return registered;
		}

		void GameTalkInstance::UnregisterSynchroMessageHandler(SyncGameTalkHandlerFnc* syncHandlerFunc, const char * channelId)
		{
			// go through all the slots
			for (uint32_t handlerIndex = 0; handlerIndex < mMaxHandlersCount; handlerIndex++)
			{
				if (mRegisteredMessageHandlers[handlerIndex] != NULL)
				{
					GameTalkHandler* handler = mRegisteredMessageHandlers[handlerIndex];
					SyncGameTalkHandlerFnc* func = handler->GetSynchroHandlerFnc();
					if (func == syncHandlerFunc)
					{
						if ((!channelId) || (USER_GAMETALK_STREQUAL(handler->GetChannelId(), channelId)))
						{
							delete handler;
							mRegisteredMessageHandlers[handlerIndex] = NULL;
							mRegisteredMessageHandlersCount--;
							break;
						}
					}
				}
			}
		}

		bool GameTalkInstance::InvokeMatchingSynchroMsgHandler(GameTalkMessage* gtm, SyncReplyGameTalkMessage* syncReplyMessage, const char* userChannel)
		{
			bool invoked = false;
			// go through all the slots
			for (uint32_t handlerIndex = 0; handlerIndex < mMaxHandlersCount; handlerIndex++)
			{
				if (mRegisteredMessageHandlers[handlerIndex] != NULL)
				{
					GameTalkHandler* handler = mRegisteredMessageHandlers[handlerIndex];
					if (IsMessageMatching(userChannel, handler->GetChannelId())) // if the handler is registered on either the empty string or the matching channel - invoke it
					{
						handler->Invoke(this, gtm, syncReplyMessage, userChannel);
						invoked = true;
						// I think we want to invoke all matching handlers - instead of just "break;" after first matching
					}
				}
			}
			return invoked;
		}

		/*virtual*/
		bool GameTalkInstance::RegisterMessageHandler(MessageHandler handlerFunc, const char* filter, void * userData)
		{
			USER_GAMETALK_ASSERT(mRegisteredMessageHandlersCount < mMaxHandlersCount, "All available handlers in use. Increase default max handlers.");

			bool registered = false;
			// Find the first emtpy slot
			for (uint32_t handlerIndex = 0; handlerIndex < mMaxHandlersCount; handlerIndex++)
			{
				if (mRegisteredMessageHandlers[handlerIndex] == NULL)
				{
					mRegisteredMessageHandlers[handlerIndex] = new GameTalkHandler(filter, handlerFunc, userData);
					mRegisteredMessageHandlersCount++;

					registered = SendServerChannel(filter, true);
					break;
				}
			}

			return registered;
		}

		/// Unregister a message handler
		/*virtual*/ 
		void    GameTalkInstance::UnregisterMessageHandler(MessageHandler handlerFunc, const char * channelId)
		{
			// go through all the slots
			for (uint32_t handlerIndex = 0; handlerIndex < mMaxHandlersCount; handlerIndex++)
			{
				if (mRegisteredMessageHandlers[handlerIndex] != NULL)
				{
					GameTalkHandler* handler = mRegisteredMessageHandlers[handlerIndex];
					MessageHandler func = handler->GetMessageHandlerFnc();
					if (func == handlerFunc)
					{
						if ((!channelId) || (USER_GAMETALK_STREQUAL(handler->GetChannelId(), channelId)))
						{
							// Tell GameTalkServer that this client won't be using this channel anymore
							bool sentMessage = SendServerChannel(handler->GetChannelId(), false);
							if (!sentMessage)
							{
								// something wrong with our connection - what more we can do?
								// perhaps put this in a list of channels that need to be unregistered on the server (once we regain our connection)?
								// It isn't critical we unregister our channels from the server - since we filter 
								// incoming messages in our client code regardless
							}

							delete handler;
							mRegisteredMessageHandlers[handlerIndex] = NULL;
							mRegisteredMessageHandlersCount--;
							break;
						}
					}
				}
			}
		}



		// ------------------------------------------------------------------------------
		// GameTalkInstance :: SendKeywordMessage

		// Generic function for sending any keyword message desired.
		// ------------------------------------------------------------------------------
		bool GameTalkInstance::SendKeywordMessage(const char * const keywordKeyStr, const char * keywordValueStr)
		{
			const uint32_t stackKeywordBufferSize = 512;
			char buffer[stackKeywordBufferSize];
			char * outBuffer = buffer;
			const uint32_t keywordKeyStrLength = (uint32_t)USER_GAMETALK_STRLEN(keywordKeyStr);
			const uint32_t keywordValueStrLength = (uint32_t)USER_GAMETALK_STRLEN(keywordValueStr);
			const uint32_t bufferSize = (uint32_t)4 + 4 + keywordKeyStrLength + 4 + keywordValueStrLength;
			USER_GAMETALK_ASSERT(stackKeywordBufferSize > bufferSize, "The stack keyword buffer size is too small to hold the keyword message to be sent");
			if (stackKeywordBufferSize < bufferSize)
				return false;

			// Set the total length
			EndianSwap::WriteLE32(outBuffer, bufferSize);
			// Set the key length
			EndianSwap::WriteLE32(outBuffer, keywordKeyStrLength);
			// Set the key string
			USER_GAMETALK_STRNCPY(outBuffer, keywordKeyStr, keywordKeyStrLength);

			// Move the outBuffer pointer just past the keyword key string
			outBuffer += keywordKeyStrLength;
			// Add the value string length
			EndianSwap::WriteLE32(outBuffer, keywordValueStrLength);
			// Add the value string
			USER_GAMETALK_STRNCPY(outBuffer, keywordValueStr, keywordValueStrLength);
			// Send the message
			return mGameTalkProtocol->Send(buffer, bufferSize);
		}

		// ------------------------------------------------------------------------------
		// GameTalkInstance :: SendInitializePacket
		// ------------------------------------------------------------------------------
		bool GameTalkInstance::SendInitializePacket()
		{
			// Send the initialize message (includes target name, platform and version number)
#if defined(USER_GAMETALK_TARGET_NAME) && defined(USER_GAMETALK_PLATFORM_NAME)
			const char* const targetString = USER_GAMETALK_TARGET_NAME;
			const char* const platformString = USER_GAMETALK_PLATFORM_NAME;
#elif EA_PLATFORM_PLAYSTATION2
			const char* const targetString = "Game.PS2";
			const char* const platformString = "ps2";
#elif EA_PLATFORM_PLAYSTATION3 || EA_PLATFORM_PS3
			const char* const targetString = "Game.PS3";
			const char* const platformString = "ps3";
#elif EA_PLATFORM_XBOX
			const char* const targetString = "Game.Xbox";
			const char* const platformString = "xbox";
#elif EA_PLATFORM_XENON
			const char* const targetString = "Game.Xenon";
			const char* const platformString = "xenon";
#elif EA_PLATFORM_WIN32
			const char* targetString = "Game.PC";
			const char* platformString = "pc";
#elif EA_PLATFORM_WIN64
			const char* targetString = "Game.PC64";
			const char* platformString = "pc64";
#elif EA_PLATFORM_GAMECUBE
			const char* const targetString = "Game.GameCube";
			const char* const platformString = "gamecube";
#elif EA_PLATFORM_REVOLUTION
			const char* const targetString = "Game.Revolution";
			const char* const platformString = "revolution";
#elif EA_PLATFORM_IPHONE
            const char* const targetString = "Game.iPhone";
            const char* const platformString = "iphone";
#elif EA_PLATFORM_XBOXONE
            const char* const targetString = "Game.Capilano";
            const char* const platformString = "capilano";
#elif EA_PLATFORM_PS4
	const char* const targetString = "Game.Kettle";
	const char* const platformString = "kettle";
#else
			const char* const targetString = "Game.Undefined";
			const char* const platformString = "undefined";
#endif
			if (mTargetName == NULL) // If the user hasn't given us a mTargetName - take the default from targetString
				AssignTargetName(targetString);

			bool sentVersionString = false;
			bool sentTargetName = false;
			bool sentPlatformString = false;
			for (int i = 0; (i < USER_GAMETALK_MAX_BLOCKING_POLLS) && (!sentTargetName);i++)
			{
				sentPlatformString = SendKeywordMessage("gametalk.config.platform", platformString);
				if (sentPlatformString)
				{
					sentVersionString = SendKeywordMessage("gametalk.config.version", mVersion);
					if (sentVersionString)
					{
						// The target name must be sent last, since it triggers a context specific work flow in the GameTalkServer
						sentTargetName = SendKeywordMessage("initialize", mTargetName);
					}
				}

				if (!IsConnected()) // Don't want this blocking if we're not actual connected (or in the process or becoming connected)
					break;
			}
			return (sentPlatformString && sentVersionString && sentTargetName);
		}

		bool GameTalkMessage::ParseGameTalkMessageHeader(char*& buffer, uint32_t& numKeys, char *& channelId)
		{
			// Read the message ID
			channelId = GameTalkMessage::ReadString(buffer);

			// Read the number of keys - we have to use ReadAndNullLE32 to ensure the channel is null delimited
			numKeys = EndianSwap::ReadAndNullLE32(buffer);

			return true;
		}

		// ------------------------------------------------------------------------------
		// GameTalkInstance :: ParseCreateInvokeCallbackAndDelete
		// ------------------------------------------------------------------------------
		void GameTalkInstance::ParseCreateInvokeCallbackAndDelete(char* buffer, char* endOfBuffer)
		{
			// Read the size of the message
			uint32_t msgSize = EndianSwap::ReadLE32(buffer);
			if (msgSize == 0)
				return;

			char * channelId = NULL;
			uint32_t numKeys = 0;
			if (!GameTalkMessage::ParseGameTalkMessageHeader(buffer, numKeys, channelId))
				return;

			KeyContent ** keyContentPtrArray = mKeyContentBuffer->GetKeyContents(numKeys);
			if (keyContentPtrArray == NULL) // We don't have room to hand out some more KeyContents :(
			{
				USER_GAMETALK_FAIL("No room to hand out more KeyContents - try increasing the size of GAMETALK_MAX_SIMULTANEOUS_MESSAGE_KEYS");
				return;
			}

			char * stringContentOfGTM = NULL;
			// We'll make our string buffer the same size as the size of the entire GameTalkMessage - wasteful, but safe
			uint32_t stringBufferSize = msgSize;
			// The msgSize is much larger than we will need - but at least smaller
			// than GAMETALK_RECEIVE_STRING_BUFFER_SIZE (or whatever string was handed to the GameTalkInstance::mReceiveStrBufferSize)
			// The reason we don't simply ask for all the remaining string content space
			// (by using the KeyContentBuffer::GetRemainingStringBufferSpace()) in
			// the KeyContentBuffer is in case the user recurses (i.e. invokes Poll within their callback)
			bool gotBuffer = mKeyContentBuffer->GetStringBuffer(stringBufferSize, stringContentOfGTM);
			if (!gotBuffer) // We don't have room to hand out a string buffer of the requested size
			{
				USER_GAMETALK_FAIL("No room to hand out a string buffer of the required size - try increasing the size of GAMETALK_RECEIVE_STRING_BUFFER_SIZE or the string buffer size handed to this specific GameTalkInstance");
				return;
			}

			// Use the incoming message GameTalkMessage constructor
			GameTalkMessage message (channelId, keyContentPtrArray, numKeys);
			GameTalkMessage::Create(buffer, endOfBuffer, stringContentOfGTM, stringBufferSize, numKeys, &message, false);
			ReceiveMessage(&message);
			mKeyContentBuffer->ReleaseKeyContents(numKeys);
			mKeyContentBuffer->ReleaseStringBuffer(stringBufferSize);
		}

		// ------------------------------------------------------------------------------
		// GameTalkInstance :: ReceiverCallback
		// ------------------------------------------------------------------------------
		void GameTalkInstance::ReceiverCallback(void* buffer, unsigned int sizeInBytes)
		{
			uint32_t bytesRead = 0;
			uint32_t totalBytesRead = 0;
			char* endOfBuffer = ((char*)buffer) + sizeInBytes;
			while (totalBytesRead < sizeInBytes)
			{
				// Get the buffer
				char* messageBuffer = ((char*)buffer) + totalBytesRead;

				// Read the message size
				uint32_t msgSize = 0;
				USER_GAMETALK_MEMCPY(&msgSize, messageBuffer, 4);
				msgSize = USER_GAMETALK_SWAP_LITTLE_ENDIAN32(msgSize);
				if ((int)(sizeInBytes - totalBytesRead) < (int)msgSize)
				{
					USER_GAMETALK_FAIL("Our buffer is smaller than the GameTalkMessage - this should've been checked before GameTalk's callback was invoked");
					return;
				}

				bytesRead = 4 + msgSize;

				ParseCreateInvokeCallbackAndDelete(messageBuffer, endOfBuffer);

				totalBytesRead += bytesRead;
			}
		}

		// ------------------------------------------------------------------------------
		// GameTalkInstance :: IsMessageMatching
		// ------------------------------------------------------------------------------
		bool GameTalkInstance::IsMessageMatching(const char* messageId, const char* messageFilter)
		{
			if (messageFilter == NULL)
			{
				messageFilter = "";
			}
			if (USER_GAMETALK_STREQUAL(messageFilter, ""))
			{
				return true;
			}
			else
			{
				int msgIdLen = static_cast<int>(USER_GAMETALK_STRLEN(messageId));
				int msgFilterLen = static_cast<int>(USER_GAMETALK_STRLEN(messageFilter));
				if (msgFilterLen > msgIdLen)
				{
					// The length of the channel name in our filter is longer than the length of the channel name of the message
					// - we can't possibly get a partial match from this
					return false;
				}
				for (int count = 0; count < msgFilterLen; count++)
				{
					if (messageId[count] != messageFilter[count])
					{
						return false;
					}
				}
				if (msgFilterLen != msgIdLen)
				{
					if (messageId[msgFilterLen] != '.')
					{
						return false;
					}
				}
				return true;
			}
		}

		// ------------------------------------------------------------------------------
		// GameTalkInstance :: TargetName
		// ------------------------------------------------------------------------------
		const char * GameTalkInstance::TargetName ()
		{
			return mTargetName;
		}

		uint32_t GameTalkInstance::GenerateSynchronousMessageChannel(char * synchronousMsgChannelBuffer, const char * userChannelId)
		{
			char * channelIdPtr = synchronousMsgChannelBuffer;
			size_t synchronousGtmIdSize = USER_GAMETALK_STRLEN(GameTalkMessage::sSynchronousMessageIDPlusSeparator);
			USER_GAMETALK_STRNCPY(channelIdPtr, GameTalkMessage::sSynchronousMessageIDPlusSeparator, synchronousGtmIdSize);
			channelIdPtr += synchronousGtmIdSize;
			size_t userChannelIdSize = USER_GAMETALK_STRLEN(userChannelId);
			USER_GAMETALK_STRNCPY(channelIdPtr, userChannelId, userChannelIdSize);
			channelIdPtr += userChannelIdSize;

			USER_GAMETALK_STRNCPY(channelIdPtr, ".", 1);
			++channelIdPtr;

			char numStr [10];
			uint32_t syncMsgUid = ++mSyncMsgUidCounter;
			USER_GAMETALK_UNSIGNED_VALUE_TO_TEXT(numStr, syncMsgUid);
			size_t numStrLength = USER_GAMETALK_STRLEN(numStr);
			USER_GAMETALK_STRNCPY(channelIdPtr, numStr, numStrLength);
			channelIdPtr += numStrLength;
			for (size_t i = numStrLength;i < 10;++i)
			{
				USER_GAMETALK_STRNCPY(channelIdPtr, "x", 1);
				++channelIdPtr;
			}

			*channelIdPtr = '\0';
			channelIdPtr++;

			return syncMsgUid;
		}


		GameTalkMessage * GameTalkInstance::SendSynchronousMessage(const char * target,
			GameTalkMessage * gameTalkMessage, ITimer* timer)
		{
			// create gametalkmessage to hold sync-reply
			GameTalkMessage *reply = new GameTalkMessage(NULL);

			// send message
			if (SendSynchronous(target, gameTalkMessage->GetChannelId(), gameTalkMessage, reply, timer))
			{
				// success
				return reply;
			}
			else
			{
				// failed
				delete reply;
				return NULL;
			}
		}


		// ------------------------------------------------------------------------------
		// GameTalkInstance :: SendSynchronousMessage
		// ------------------------------------------------------------------------------
		bool  GameTalkInstance::SendSynchronous(const char * target, const char *channel, const IMessage * message, IMessage *reply, ITimer* timer)
		{
			GameTalkMessage *gameTalkMessage = (GameTalkMessage *)message;
			GameTalkMessage *gameTalkReply   = (GameTalkMessage *)reply;

			char synchronousMsgChannel [1024];
			// Append sSynchronousMessageIDPlusSeparator as prefix to channel and a UID as suffix
			uint32_t synchroMsgUid = GenerateSynchronousMessageChannel(synchronousMsgChannel, channel);

			// Append the "@." at the front of the target string to ensure this synchronous message is only sent to the specified target (i.e. disable the usual target matching semantics the GameTalkServer performs)
			char specificTarget [256];
			USER_GAMETALK_STRNCPY(specificTarget, "@.", 2);
			USER_GAMETALK_STRNCPY(specificTarget+2, target, USER_GAMETALK_STRLEN(target));
			// Null delimit the target
			specificTarget[2 + USER_GAMETALK_STRLEN(target)] = '\0';

			// <Find a spot in our mSynchroMsgTable>
			// For thread-safety correctness - we'll need a lock from this point (checking we have room) until we put a SynchronousTransaction into our mSynchroMsgTable
			int synchoMsgIndexToUse = -1;
			for (uint32_t synchroMsgIndex = 0; synchroMsgIndex < USER_GAMETALK_MAX_PENDING_SYNCHRO_TRANSACTIONS; ++synchroMsgIndex)
			{
				if (mSynchroMsgTable[synchroMsgIndex] == NULL)
				{
					synchoMsgIndexToUse = synchroMsgIndex;
					break;
				}
			}
			if (synchoMsgIndexToUse == -1) // Failed to find a spot for our SynchronousTransaction
				return false;
			// </Find a spot in our mSynchroMsgTable>

			// create sync transaction
			SynchronousTransaction synchroData(this, synchroMsgUid, gameTalkMessage, gameTalkReply);
			gameTalkMessage->mSynchronousTransaction = &synchroData;

			// <Register entry in our mSynchroMsgTable - in anticipation of response>
			mSynchroMsgTable[synchoMsgIndexToUse] = &synchroData;
			// </Register entry in our mSynchroMsgTable - in anticipation of response>

			gameTalkMessage->AddChunkData(&synchroMsgUid, GameTalkMessage::SynchroId, sizeof(synchroMsgUid));
			// send sync-message
			this->Send(specificTarget, synchronousMsgChannel, gameTalkMessage);

			// wait for sync-reply to be received
			while ((!timer->HasExpired()) && (!synchroData.mReceivedReply))
				this->Poll();

			// Remove this entry from our table - regardless of whether we received a response (may have timed out)
			mSynchroMsgTable[synchoMsgIndexToUse] = NULL;

			// clear sync transaction reference in message
			gameTalkMessage->mSynchronousTransaction = NULL;

			return synchroData.mReceivedReply;
		}

		// ------------------------------------------------------------------------------
		// GameTalkInstance :: SendShutdown
		// ------------------------------------------------------------------------------
		bool GameTalkInstance::SendShutdown()
		{
#define GAMETALK_SHUTDOWN_MSG_LENGTH 16
			char shutDownMsgStr [GAMETALK_SHUTDOWN_MSG_LENGTH];
			const char * shutdownStr = "shutdown";

			char * bufferIndex = shutDownMsgStr;
			// Set the total length
			EndianSwap::WriteLE32(bufferIndex, GAMETALK_SHUTDOWN_MSG_LENGTH);
			// Set the shutdown string length
			EndianSwap::WriteLE32(bufferIndex, (uint32_t)USER_GAMETALK_STRLEN(shutdownStr));
			USER_GAMETALK_STRNCPY(bufferIndex, shutdownStr, USER_GAMETALK_STRLEN(shutdownStr));

			// Send the message
			return mGameTalkProtocol->Send(shutDownMsgStr, GAMETALK_SHUTDOWN_MSG_LENGTH);
		}

		// ------------------------------------------------------------------------------
		// GameTalkInstance :: AssignTargetName
		// ------------------------------------------------------------------------------
		void GameTalkInstance::AssignTargetName(const char * targetName)
		{
			if(targetName == NULL)
				return;

			if (mTargetName != NULL)
				USER_GAMETALK_FREE(mTargetName, "GameTalkInstance:mTargetName");

			size_t lengthOfTargetName = USER_GAMETALK_STRLEN(targetName);
			mTargetName = (char*)USER_GAMETALK_ALLOC(lengthOfTargetName + 1, "GameTalkInstance:mTargetName");
			USER_GAMETALK_STRNCPY_ADD_NULL(mTargetName, targetName, lengthOfTargetName + 1);
		}

		// ------------------------------------------------------------------------------
		// GameTalkInstance :: UpdateTargetName
		// ------------------------------------------------------------------------------
		bool GameTalkInstance::UpdateTargetName(const char * targetName)
		{
			if(targetName == NULL)
				return false;

			AssignTargetName(targetName);
			return this->SendInitializePacket();
		}

		// ------------------------------------------------------------------------------
		// GameTalkInstance :: SendIsAlivePing
		// ------------------------------------------------------------------------------
		bool GameTalkInstance::SendIsAlivePing()
		{
			GameTalkMessage gameTalkMessage(GameTalkMessage::sGameTalkServerPingID);
			return this->SendMessage(GameTalkMessage::sGameTalkServerID, &gameTalkMessage);
		}

		// ------------------------------------------------------------------------------
		// GameTalkInstance :: InitializeKCBuffer
		// ------------------------------------------------------------------------------
		void GameTalkInstance::InitializeKCBuffer()
		{
			mKeyContentBuffer = new KeyContentBuffer(GAMETALK_MAX_SIMULTANEOUS_MESSAGE_KEYS, mReceiveStrBufferSize);
		}

		// ------------------------------------------------------------------------------
		// GameTalkInstance :: ReleaseKCBuffer
		// ------------------------------------------------------------------------------
		void GameTalkInstance::ReleaseKCBuffer()
		{
			USER_GAMETALK_FREE(mKeyContentBuffer->mBuffer, "GameTalkInstance:mKeyContentBuffer->mBuffer");
			delete mKeyContentBuffer;
		}

		// ------------------------------------------------------------------------------
		// GameTalkInstance :: SynchronousMessageResponseHandler
		//
		// Upon receipt of a response to a submitted Synchronous GameTalkMessage - we need to
		// copy the data into the memory that the user has provided to us - so the message will
		// live past the life of the callback (there has to be a better way of saying that).
		// Therefore, the user is responsible for freeing/dealloc'ing the memory associated with this GameTalkMessage
		// Since this is static member function we need a reference to the GameTalkInstance that sent this synchronous message.
		// ------------------------------------------------------------------------------
		void GameTalkInstance::SynchronousMessageResponseHandler (EA::GameTalk::GameTalkMessage * message, void *& userData)
		{
			GameTalkInstance* gameTalkInstance = (GameTalkInstance*)userData;
			USER_GAMETALK_ASSERT(gameTalkInstance != NULL, "The GameTalk callback has a null GameTalkInstance - look at the GameTalkInstance constructor to see why");

			uint32_t synchroDataInt = 0;
			if (!SyncReplyGameTalkMessage::GetSynchroId(message, synchroDataInt))
				return;

			// Find our SynchronousTransaction (match based on the Sync Id)
			SynchronousTransaction* foundSynchroTransaction = NULL;
			for (uint32_t synchroMsgIndex = 0; synchroMsgIndex < USER_GAMETALK_MAX_PENDING_SYNCHRO_TRANSACTIONS; ++synchroMsgIndex)
			{
				if (gameTalkInstance->mSynchroMsgTable[synchroMsgIndex] != NULL)
				{
					SynchronousTransaction* synchroTransaction = gameTalkInstance->mSynchroMsgTable[synchroMsgIndex];
					if (synchroTransaction->mSyncId == synchroDataInt)
					{
						foundSynchroTransaction = synchroTransaction;
						break;
					}
				}
			}

			if (foundSynchroTransaction == NULL)
			{
				// We received a response to a synchronous message that we didn't send (apparently)
				// It is possible this is simply a late response to a synchronous message we stopped listening for... so we may not want to assert
				return;
			}

			// It'd be great if we didn't need to clone the contents of the GameTalkMessage (and instead just assign like this "synchroGtd->mMessage = message") - but we're lower in the call-stack and this GameTalkMessage will get freed before we return
			SynchronousTransaction* synchroGtd = foundSynchroTransaction;
			if (synchroGtd->mReplyMessage != NULL)
			{
				// copy the sync-reply to reply message
				message->CopyTo(synchroGtd->mReplyMessage);
			}

			// indicate that we received reply
			synchroGtd->mReceivedReply = true;
		}

		// ------------------------------------------------------------------------------
		// GameTalkInstance :: SynchronousMessageResponseHandler
		//
		// This handler is registered to ensure a response is sent to a Synchronous GameTalkMessage
		// regardless of whether the user is listening or not.
		// Since this is static member function we need a reference to the GameTalkInstance that sent this synchronous message.
		// ------------------------------------------------------------------------------
		void GameTalkInstance::HandleSynchronousMessages(GameTalkMessage * gameTalkMessage, void *& userData)
		{
			GameTalkInstance * gameTalkInstance = (GameTalkInstance*)userData;
			if (gameTalkInstance == NULL)
			{
				USER_GAMETALK_FAIL("The GameTalkInstance::HandleSynchronousMessages callback has a null GameTalkInstance - something screwy must've happened when this callback was registered with GameTalkInstance.");
				return;
			}

			int synchroMessageLength = (int)USER_GAMETALK_STRLEN(GameTalkMessage::sSynchronousMessageIDPlusSeparator);
			int messageChannelLength = (int)USER_GAMETALK_STRLEN(gameTalkMessage->GetChannelId());
			if (messageChannelLength < synchroMessageLength)
				return;

			// Check that sSynchronousMessageID is the channel prefix to the GameTalkMessage channel
			char messageChannelPrefix [256];
			USER_GAMETALK_STRNCPY(messageChannelPrefix, gameTalkMessage->GetChannelId(), synchroMessageLength);
			messageChannelPrefix[synchroMessageLength] = '\0';
			if (!USER_GAMETALK_STREQUAL(messageChannelPrefix, GameTalkMessage::sSynchronousMessageIDPlusSeparator))
			{
				USER_GAMETALK_FAIL("Somehow the GameTalkInstance::HandleSynchronousMessages callback has been invoked with a message that doesn't have a matching synchronous prefix - this should be impossible.");
				return;
			}

			const char * messageChannelAfterPrefix = &(gameTalkMessage->GetChannelId()[synchroMessageLength]);
			// We minus 11 off the end - because there should be a ".xxxxxxxxxx" UID appended onto every synchronous GameTalkMessage channel
			int lengthOfUserDefinedChannel = (int)(messageChannelLength - synchroMessageLength - 11);
			if (lengthOfUserDefinedChannel < 0)
			{
				USER_GAMETALK_FAIL("A synchronous GameTalkMessage channel doesn't contain all parts that it should contain");
				return;
			}

			char responseChannel[256];
			size_t synchroResponseLength = USER_GAMETALK_STRLEN(GameTalkMessage::sSynchroResponseMessageIDPlusSeparator);
			USER_GAMETALK_STRNCPY(responseChannel, GameTalkMessage::sSynchroResponseMessageIDPlusSeparator, synchroResponseLength);
			USER_GAMETALK_STRNCPY(&(responseChannel[synchroResponseLength]), messageChannelAfterPrefix, lengthOfUserDefinedChannel + 11);
			// Null delimit response channel
			responseChannel[synchroResponseLength + lengthOfUserDefinedChannel + 11] = '\0';

			char returnTargetName [256];
			USER_GAMETALK_STRNCPY(returnTargetName, "@.", 2);
			USER_GAMETALK_STRNCPY(&(returnTargetName[2]), gameTalkMessage->GetSender(), USER_GAMETALK_STRLEN(gameTalkMessage->GetSender()));
			returnTargetName[2 + USER_GAMETALK_STRLEN(gameTalkMessage->GetSender())] = '\0';
			// The GTM that will be replied to
			SyncReplyGameTalkMessage synchroReplyGtm(gameTalkMessage, gameTalkInstance, responseChannel, returnTargetName);

			char channelForUser [256];
			USER_GAMETALK_STRNCPY(channelForUser, messageChannelAfterPrefix, lengthOfUserDefinedChannel);
			channelForUser[lengthOfUserDefinedChannel] = '\0';
			// Does the user need access to the channel they actually sent the message on (i.e. channelForUser)?
			bool invokedMatchingSynchroMsgHandler = gameTalkInstance->InvokeMatchingSynchroMsgHandler(gameTalkMessage, &synchroReplyGtm, channelForUser);
			if (!synchroReplyGtm.Replied())
			{
				if(invokedMatchingSynchroMsgHandler)
					synchroReplyGtm.AddKeyContentAsString(GameTalkMessage::sSynchronousGameTalkIssue, "User did not reply in their callback");
				else
					synchroReplyGtm.AddKeyContentAsString(GameTalkMessage::sSynchronousGameTalkIssue, "User did not register a synchronous GameTalkMessage handler");

				synchroReplyGtm.Reply();
			}
		}

		// ------------------------------------------------------------------------------
		// GameTalkManager :: GameTalkManager
		// ------------------------------------------------------------------------------
		GameTalkManager::GameTalkManager(IGameTalkProtocol* protocol, uint32_t maxHandlersCount, const char * targetName, bool reconnect, uint32_t receiveStrBufferSize):
		mFreeGameTalkProtocol(false)
		{
			GameTalkInstance* gameTalkInstance = new GameTalkInstance(protocol, maxHandlersCount, targetName, false, reconnect, receiveStrBufferSize);
			USER_GAMETALK_TLS_SET_VALUE(gameTalkInstance);
		}

		// ------------------------------------------------------------------------------
		// GameTalkManager :: ~GameTalkManager
		// ------------------------------------------------------------------------------
		GameTalkManager::~GameTalkManager()
		{
			GameTalkInstance* gameTalkInstance = (GameTalkInstance*)USER_GAMETALK_TLS_GET_VALUE();
			USER_GAMETALK_ASSERT(gameTalkInstance != NULL, "GameTalkInstance has not yet been created.");
			if (gameTalkInstance == NULL)
				return;

			gameTalkInstance->mFreeGameTalkProtocol = mFreeGameTalkProtocol;

			// We may want to make the deletion of the GameTalkInstance optional - since users can swap them in and out using the AdoptInstance function
			delete gameTalkInstance;
			USER_GAMETALK_TLS_SET_VALUE(NULL);
		}

		// ------------------------------------------------------------------------------
		// GameTalkManager :: CreateInstance
		// ------------------------------------------------------------------------------
		void GameTalkManager::CreateInstance(IGameTalkProtocol* protocol, uint32_t maxHandlersCount, const char * targetName, bool reconnect, uint32_t receiveStrBufferSize)
		{
			USER_GAMETALK_ASSERT(sInstance == NULL, "Instance has already been created.");

			if (sInstance == NULL)
				sInstance = new GameTalkManager(protocol, maxHandlersCount, targetName, reconnect, receiveStrBufferSize);
		}

		// ------------------------------------------------------------------------------
		// GameTalkManager :: GetInstance
		// ------------------------------------------------------------------------------
		GameTalkManager * GameTalkManager::GetInstance()
		{
			return sInstance;
		}

		// [FIFA] iw: expose ability to set GameTalkManager instance so another dll can set to the GameTalkManager 
		// already created in another dll. Workaround for GameTalk not supporting dll.
		// ------------------------------------------------------------------------------
		// GameTalkManager :: SetInstance
		// ------------------------------------------------------------------------------
		void GameTalkManager::SetInstance(GameTalkManager* pGameTalkManager)
		{
			USER_GAMETALK_ASSERT(sInstance == NULL, "Instance has already been created.");
			if (sInstance == NULL)
				sInstance = pGameTalkManager;
		}

		// ------------------------------------------------------------------------------
		// GameTalkManager :: ReleaseInstance
		//
		// Asserts that an instance exists before attempting to release it.
		// ------------------------------------------------------------------------------
		void GameTalkManager::ReleaseInstance(bool freeGameTalkProtocol)
		{
			USER_GAMETALK_ASSERT(sInstance != NULL, "Instance has not yet been created.");
			if (sInstance == NULL)
				return;

			sInstance->mFreeGameTalkProtocol = freeGameTalkProtocol;

			delete sInstance;
			sInstance = NULL;
		}

		// ------------------------------------------------------------------------------
		// GameTalkManager :: IsConnected
		//
		// Is the game connected to the pc server
		// ------------------------------------------------------------------------------
		bool GameTalkManager::IsConnected() const
		{
			GameTalkInstance* gameTalkInstance = (GameTalkInstance*)USER_GAMETALK_TLS_GET_VALUE();
			bool connected = false;
			if (sInstance != NULL)
			{
				connected = gameTalkInstance->IsConnected();
			}
			return connected;
		}

		// ------------------------------------------------------------------------------
		// GameTalkManager :: SendMessage
		// ------------------------------------------------------------------------------
		bool GameTalkManager::SendMessage(const char* target, GameTalkMessage* message)
		{
			GameTalkInstance* gameTalkInstance = (GameTalkInstance*)USER_GAMETALK_TLS_GET_VALUE();
			bool sendSuccessful = false;
			if (sInstance != NULL)
			{
				sendSuccessful = gameTalkInstance->SendMessage(target, message);
			}
			return sendSuccessful;
		}

		// ------------------------------------------------------------------------------
		// GameTalkManager :: Poll
		//
		// Poll the network for messages
		// ------------------------------------------------------------------------------
		int GameTalkManager::Poll()
		{
			GameTalkInstance* gameTalkInstance = (GameTalkInstance*)USER_GAMETALK_TLS_GET_VALUE();
			if ((sInstance != NULL) && (gameTalkInstance != NULL))
				return gameTalkInstance->Poll();

			return 0;
		}

		// ------------------------------------------------------------------------------
		// GameTalkManager :: AdoptInstance
		//
		// Allows user to give a different GameTalkInstance to the GameTalkManager singleton
		// ------------------------------------------------------------------------------
		void GameTalkManager::AdoptInstance(GameTalkInstance * gameTalkInstance)
		{
			GameTalkInstance* currentGameTalkInstance = (GameTalkInstance*)USER_GAMETALK_TLS_GET_VALUE();
			USER_GAMETALK_ASSERT(currentGameTalkInstance == NULL, "The existing GameTalk index in the TLS refers to a non-null GameTalkInstance pointer.  If the user doesn't have a reference to the currently contained GameTalkInstance - memory leak!");
			USER_GAMETALK_USE_VAR_TO_AVOID_WARNING(currentGameTalkInstance);

			USER_GAMETALK_TLS_SET_VALUE(gameTalkInstance);
		}

		// ------------------------------------------------------------------------------
		// GameTalkManager :: GetAdoptedInstance
		//
		// Allows user to get a reference to the GameTalkInstance pointer that the GameTalkManager is using
		// ------------------------------------------------------------------------------
		GameTalkInstance * GameTalkManager::GetAdoptedInstance()
		{
			GameTalkInstance* gameTalkInstance = (GameTalkInstance*)USER_GAMETALK_TLS_GET_VALUE();
			return gameTalkInstance;
		}

		// ------------------------------------------------------------------------------
		// GameTalkManager :: RegisterMessageHandler
		//
		// Registers a MessageHandler to receive messages.
		// ------------------------------------------------------------------------------
		bool GameTalkManager::RegisterMessageHandler(GameTalkHandlerFnc* handlerFunc, const char* filter)
		{
			GameTalkInstance* gameTalkInstance = (GameTalkInstance*)USER_GAMETALK_TLS_GET_VALUE();
			USER_GAMETALK_ASSERT( gameTalkInstance != NULL, "GameTalkManager's instance of GameTalkInstance is null");

			if (gameTalkInstance != NULL)
				return gameTalkInstance->RegisterMessageHandler(handlerFunc, filter);

			return false;
		}

		// ------------------------------------------------------------------------------
		// GameTalkManager :: RegisterMessageHandler
		//
		// Registers a MessageHandler to receive messages - allows user to specify user data to pass through to callback.
		// ------------------------------------------------------------------------------
		bool GameTalkManager::RegisterMessageHandler(UserGameTalkHandlerFnc* handlerFunc, const char* filter, void * userData)
		{
			GameTalkInstance* gameTalkInstance = (GameTalkInstance*)USER_GAMETALK_TLS_GET_VALUE();
			USER_GAMETALK_ASSERT( gameTalkInstance != NULL, "GameTalkManager's instance of GameTalkInstance is null");

			if (gameTalkInstance != NULL)
				return gameTalkInstance->RegisterMessageHandler(handlerFunc, filter, userData);

			return false;
		}

		// ------------------------------------------------------------------------------
		// GameTalkManager :: UnregisterMessageHandler

		// Removes a MessageHandler from receiving messages.
		// ------------------------------------------------------------------------------
		void GameTalkManager::UnregisterMessageHandler(GameTalkHandlerFnc* handlerFunc)
		{
			GameTalkInstance* gameTalkInstance = (GameTalkInstance*)USER_GAMETALK_TLS_GET_VALUE();
			USER_GAMETALK_ASSERT( gameTalkInstance != NULL, "GameTalkManager's instance of GameTalkInstance is null");

			if (gameTalkInstance != NULL)
				gameTalkInstance->UnregisterMessageHandler(handlerFunc);
		}

		// ------------------------------------------------------------------------------
		// GameTalkManager :: UnregisterMessageHandler

		// Removes a MessageHandler from receiving messages.
		// ------------------------------------------------------------------------------
		void GameTalkManager::UnregisterMessageHandler(UserGameTalkHandlerFnc* handlerFunc)
		{
			GameTalkInstance* gameTalkInstance = (GameTalkInstance*)USER_GAMETALK_TLS_GET_VALUE();
			USER_GAMETALK_ASSERT( gameTalkInstance != NULL, "GameTalkManager's instance of GameTalkInstance is null");

			if (gameTalkInstance != NULL)
				gameTalkInstance->UnregisterMessageHandler(handlerFunc);
		}

		// ------------------------------------------------------------------------------
		// GameTalkManager :: IsMessageMatching
		// ------------------------------------------------------------------------------
		bool GameTalkManager::IsMessageMatching(const char* messageId, const char* messageFilter)
		{
			return GameTalkInstance::IsMessageMatching(messageId, messageFilter);
		}

		// ------------------------------------------------------------------------------
		// GameTalkManager :: TargetName
		// ------------------------------------------------------------------------------
		const char * GameTalkManager::TargetName ()
		{
			GameTalkInstance* gameTalkInstance = (GameTalkInstance*)USER_GAMETALK_TLS_GET_VALUE();
			USER_GAMETALK_ASSERT( gameTalkInstance != NULL, "GameTalkManager's instance of GameTalkInstance is null");

			if (gameTalkInstance != NULL)
				return gameTalkInstance->TargetName();

			return NULL;
		}
	}
}


