/*
	Copyright 2004-2005 Electronic Arts Inc.
*/

// --- Include guard

#ifndef GAMETALKWIN32_HPP
#define GAMETALKWIN32_HPP

// --- Includes

#include "winsock2.h"
#include "stdio.h"
#include <atlstr.h>
#include <string>
#include <fstream>
#include <iostream>
#include <vector>

// --- Macros 

using namespace std;

namespace EA
{
namespace GameTalk
{
	// --- Forward Declaration
	class TcpSocket;
	class GameTalkThread;

	// --- Exception implementation
	class Exception
	{
	public:
		Exception(int errCode, const string &errMsg);   
		virtual ~Exception() {};

		virtual void Response();  

		int GetErrCode() { return mErrorCode; }
		string& GetErrMsg() { return mErrorMsg; }

	private:
		void InitVars();

		int mErrorCode;
		string mErrorMsg;
	};

	// --- GameTalkMessage implementation
	class GameTalkMessage
	{
	public:
		enum FormatTypes
		{
			StringFormat,
			BinaryFormat
		};

		GameTalkMessage(const string &messageId );
		GameTalkMessage(const GameTalkMessage &from );
		GameTalkMessage();
		~GameTalkMessage();

		GameTalkMessage &operator=(const GameTalkMessage &from );

		void AddKeyContent(const string &key, const string &content);
		void AddKeyContent(const string &key, void* content, int size, FormatTypes type);
		void ClearKeyContent();

		string GetChannelId() const { return MessageId; }
		string GetKey(int index) const;
		void* GetContent(int index) const;
		string GetContentAsString(int index) const;
		int GetContentSize(int index) const;
		FormatTypes GetType(int index) const;

		int NumberOfKeys() const;

		// assemble the buffer form KeyContent - receive
		char* AssembleToBuffer(char **, int * );
		// assemble the KeyContent form buffer - send
		int AssembleFromBuffer(char* buffer, int bufferSize);

	private:
		class KeyContent
		{
		public:
			KeyContent()
			{
				Type = StringFormat;
				Content = NULL;
				Size = 0;
			}
			~KeyContent()
			{
				if (Size > 0)
					delete[] Content;
			}

			FormatTypes Type;
			string Key;
			void* Content;
			int Size;
		};

		int ComputeBufferSize();

		vector<KeyContent *> mKeysContents;
		char* mBuffer;
		int mBufferSize;

		string MessageId;
	};

	typedef void GameTalkMessageHandlerFnc(GameTalkMessage *message );

	// --- GameTalkManager implementation
	class GameTalkManager
	{
	public:
		static void Initialize(const string toolName);
		static void Initialize(const string toolName, const string serverName);
		static void Shutdown();
		static GameTalkManager* GetInstance();

		bool Send(const string &target, GameTalkMessage &message);
		// Tell the server to start buffering messages from the current client to the requested target
		// maxBufferedMessages is the max number of messages that will be buffered for this particular client to client connection
		void StartBuffering (const string &target, int maxBufferedMessages, bool reset);
		// Tells the server to stop buffering any messages from the current client for the specified target
		void StopBuffering (const string &target, bool reset);
		// Requests the assembly version of the server
		void RequestServerVersion();
		// Return the hardcoded version number in this function
		const char * ClientVersion();
		// Do NOT make IsConnected send a message over the wire - the user can do that themselves
		// However, feel free to add code to check for an invalid socket - rather than just a 
		// null pointer
		bool IsConnected();
		void RegisterMessageHandler(GameTalkMessageHandlerFnc* handlerFnc);
		void UnRegisterMessageHandler(GameTalkMessageHandlerFnc* handlerFnc);

	private:
		GameTalkManager();
		~GameTalkManager();

		// Internal receive thread.
		static int Receive();

		void Start(const string toolName, const string serverName);
		bool SendInitializePacket();
		void CreateNetworkEvents(int socketId);
		void DestroyNetworkEvents();

		TcpSocket* mConnection;
		int mPort;
		string* mName;
		GameTalkThread* mEvents;

		GameTalkMessageHandlerFnc* mMsgHandler;

		static GameTalkManager* mInstance;
	};
	
	extern GameTalkManager theGameTalkManager;

} // namespace GameTalk
} // namespace EA
#endif GAMETALKWIN32_HPP

