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


#include "GameTalkWin32.hpp"
#include "GameTalkLog.hpp"
#include "GameTalkThread.hpp"
#include "GameTalk.hpp"
#include "GameTalkMessages.hpp"
#include "FileTalk.hpp"
#include "FileTalkMessages.hpp"
#include "FileTalkInstaller.hpp"
#include "Interop.IWshRuntimeLibrary.hpp"

#include "psapi.h"  // for enumerating processes
#include <windows.h>  // for enumerating processes
#include <winnt.h>  // for registry key methods
#include <direct.h>  // for creating directories
#include "shlobj.h"  // for SHGetFolderPath

static int GAMETALK_PORT = 9876; //@dan todo: read this from registry	

namespace EA
{
	//
	//
	namespace GameTalkInstaller
	{
		class Installer
		{
		public:
			Installer()		{;}
			~Installer()	{;}

			void RunFileTalkInstaller(const char* thisVersion);
			void CreateFileFromResource(const char *path, const char *fileName, const char *resourceData, int resourceSize );
		};

	} // namespace GameTalk

	//
	//
	namespace GameTalk
	{
		enum hostType 
		{
			NAME, 
			ADDRESS
		};

		const int HOST_NAME_LENGTH = 255;

		class HostInfo
		{
		public:
			HostInfo();
			// Retrieves the host entry based on the host name or address
			HostInfo(string hostName, hostType type);

			~HostInfo() {}

			// Retrieves the hosts IP address
			char* GetHostIPAddress() const
			{
				struct in_addr *addr_ptr;
				// the first address in the list of host addresses
				addr_ptr = (struct in_addr *)*hostPtr->h_addr_list;
				// changed the address format to the Internet address in standard dot notation
				return inet_ntoa(*addr_ptr);
			}    

			// Retrieves the hosts name
			char* GetHostName() const { return hostPtr->h_name; }

		private:
			void DetectErrorGethostbyname(int *, string &);
			void DetectErrorGethostbyaddr(int *, string &);

			struct hostent *hostPtr;    // Entry within the host address database
		};
	} // namespace GameTalk

	//
	//
	namespace GameTalk
	{
		const int MAX_RECV_LEN	= 8096;
		const int MAX_MSG_LEN	= 1024;
		const int PORTNUM		= 2000;

		class Socket
		{
		public:
			Socket(int);        // given a port number, create a socket
			virtual ~Socket()
			{
				closesocket(socketId);
			}

			// socket options : ON/OFF
			void setDebug(int);
			void setReuseAddr(int);
			void setKeepAlive(int);
			void setLingerOnOff(bool);
			void setLingerSeconds(int);
			void setSocketBlocking(int);

			// size of the send and receive buffer
			void setSendBufSize(int);
			void setReceiveBufSize(int);

			// retrieve socket option settings
			int  getDebug();
			int  getReuseAddr();
			int  getKeepAlive();
			int  getSendBufSize();
			int  getReceiveBufSize();
			int  getSocketBlocking() { return blocking; }
			int  getLingerSeconds();
			bool getLingerOnOff();

			// returns the socket file descriptor
			int getSocketId()		{ return socketId; }

			// returns the port number
			int getPortNumber()		{ return portNumber; }

			// show the socket 
			friend ostream& operator<<(ostream&, Socket&);

		protected:
			Socket() {}
			void setSocketId(int socketFd) { socketId = socketFd; }

			int portNumber;        // Socket port number    
			int socketId;          // Socket file descriptor

			int blocking;          // Blocking flag
			int bindFlag;          // Binding flag

			struct sockaddr_in clientAddr;    // Address of the client that sent data

		private:
			// Gets the system error
			void detectErrorOpenWinSocket(int *, string &);
			void detectErrorSetSocketOption(int *, string &);
			void detectErrorGetSocketOption(int *, string &);
		};

		class TcpSocket : public Socket
		{
		public:
			// initialization work, the clean up is in the destructor
			static bool Initialize();

		public:
			TcpSocket() {};
			~TcpSocket();

			// Used to create a new TCP socket given a port
			TcpSocket(int portId) : Socket(portId) { };

			// Sends a message to a connected host. The number of bytes sent is returned.
			int SendMessage(string &);

			// Sends a message to a connected host. The number of bytes sent is returned.
			int SendMessage(char *sendMsg, size_t size );

			// receive messages and stores the message in a buffer
			int ReceiveMessage(string &);

			// receive messages and stores the message in a buffer
			int ReceiveMessage(char **receiveMsg , int size );

			// Binds the socket to an address and port number - server call
			bool BindSocket();

			// accepts a connecting client.  The address of the connected client 
			// is stored in the parameter - server call
			TcpSocket *AcceptClient(string &);

			// Listens to connecting clients - server call
			void ListenToClient(int numPorts = 5);

			// connect to the server - client call
			virtual bool ConnectToServer(string &, hostType );

		private:
			void DetectErrorBind(int *, string &);
			void DetectErrorSend(int *, string &);
			void DetectErrorRecv(int *, string &);
			void DetectErrorConnect(int *, string &);
			void DetectErrorAccept(int *, string &);
			void DetectErrorListen(int *, string &);
		};

	} // namespace GameTalk

	// GameTalk Implementation
	namespace GameTalk
	{
		GameTalkManager* GameTalkManager::mInstance = NULL;

		GameTalkManager::GameTalkManager()
		{
			mConnection	= NULL;
			mPort = GAMETALK_PORT;
			mName = NULL;
			mMsgHandler = NULL;
		}

		GameTalkManager::~GameTalkManager()
		{
			delete mName;
			if (mConnection)
			{
				delete mConnection;
				mConnection = NULL;
			}
		}

		void GameTalkManager::Initialize(const string toolName)
		{
			// Initialize the winsock library
			// Must be done right away before anything else
			bool result = TcpSocket::Initialize();
			if (result)
			{
				// get server's information - local host always
				HostInfo serverInfo;
				string serverName = serverInfo.GetHostName();
				string serverIPAddress = serverInfo.GetHostIPAddress();
				cout << "Name: " << serverName << endl;
				cout << "Address: " << serverIPAddress << endl;

				GetInstance()->Start(toolName, serverIPAddress);
			}
		}

		void GameTalkManager::Initialize(const string toolName, const string serverName)
		{
			// Initialize the winsock library
			// Must be done right away before anything else
			bool result = TcpSocket::Initialize();
			if (result)
			{
				GetInstance()->Start(toolName, serverName);
			}
		}

		void GameTalkManager::Shutdown()
		{
			// Destroy the events
			mInstance->DestroyNetworkEvents();

			if (mInstance != NULL)
			{
				if (mInstance->mConnection != NULL)
				{
					delete mInstance->mConnection;
					mInstance->mConnection = NULL;
				}

				delete mInstance;
				mInstance = NULL;
			}
		}

		GameTalkManager* GameTalkManager::GetInstance()
		{
			if (NULL == mInstance)
			{
				mInstance = new GameTalkManager();
			}
			return mInstance;
		}

		void GameTalkManager::Start(const string toolName, const string serverName)
		{
			// Current name of the tool
			mName = new string(toolName);

			// Install all the other tools - server and viewer
			const char* version = ClientVersion();
			GameTalkInstaller::Installer installer;
			installer.RunFileTalkInstaller(version);

			// create the socket for client
			mConnection = new TcpSocket(mPort);	

			// connect to the server.
			cout << "connecting to the server [" << serverName << "] ... " << endl;
			HostInfo serverInfo(serverName, NAME);
			string serverIPAddress = serverInfo.GetHostIPAddress();
			mConnection->ConnectToServer(serverIPAddress, ADDRESS);

			// create the thread to listen to receive network event
			CreateNetworkEvents(mConnection->getSocketId());

			// send the initialize packet
			SendInitializePacket();
		}

		// Send the initialize message with the target name identification
		// Total Length
		// "initialize" length
		// "initialize" string
		// "target name" length
		// "target name" string
		bool GameTalkManager::SendInitializePacket()
		{
			bool sendSuccessful = false;
			const char* const initString = "initialize";
			const int initStringLength = static_cast<int>(strlen(initString));
			const int targetStringLength = static_cast<int>(mName->size());
			const int bufferSize = 4 + 4 + initStringLength + 4 + targetStringLength;

			char *buffer = new char[bufferSize];
			char *outBuffer = buffer;

			// Set the total length
			int* intBuffer = (int*)outBuffer;
			*intBuffer = bufferSize;
			outBuffer += 4;
			// Set the initialize length
			intBuffer = (int*)outBuffer;
			*intBuffer = initStringLength;
			outBuffer += 4;
			// Set the initialize string
			strncpy_s(outBuffer, bufferSize-(outBuffer-buffer), initString, initStringLength);
			outBuffer += initStringLength;
			// Set the initialize length
			intBuffer = (int*)outBuffer;
			*intBuffer = targetStringLength;
			outBuffer += 4;
			// Set the initialize string
			strncpy(outBuffer, mName->c_str(), targetStringLength);

			if (mConnection)
			{
				int amountSent = mConnection->SendMessage(buffer, bufferSize);
				if (amountSent == bufferSize)
					sendSuccessful = true;
			}
			delete[] buffer;
			return sendSuccessful;
		}

		// Send a message in the format below:
		// [Length of entire message] 
		// [Length of target name]
		// [Target name (game.ps2)]
		// [Length of message]
		// [message (channel id and key/value pair)]
		bool GameTalkManager::Send(const string &target, GameTalkMessage &message)
		{
			bool sendSuccessful = false;
			char *myMessage = NULL;
			int myMessageSize = 0;
			message.AssembleToBuffer(&myMessage, &myMessageSize);

			// compute the length of the entire message
			size_t FinalMessageSize = sizeof(int) + sizeof(int) + target.length() + sizeof(int) + myMessageSize;

			// allocate the required space
			char *FinalMessage	= new char[FinalMessageSize];

			// fill in the full message
			if (FinalMessage)
			{
				char *crtFM = FinalMessage;
				// rezerve space for [Length of entire message]
				memset(crtFM, 0, FinalMessageSize * sizeof(char));
				crtFM += sizeof(int);
				// [Length of target name]
				*(int*)crtFM = static_cast<int>(target.length());
				crtFM += sizeof(int);
				// [Target name (game.ps2)]
				memcpy(crtFM, target.c_str(), target.length());
				crtFM += target.length();
				// [Length of message]
				*(int*)crtFM = myMessageSize;
				crtFM += sizeof(int);
				// [message (channel id and key/value pair)]
				memcpy(crtFM, myMessage, myMessageSize);
				crtFM += myMessageSize;

				int testFinalMessageSize = static_cast<int>((crtFM - FinalMessage) * sizeof(char));
				if (FinalMessageSize != testFinalMessageSize)
				{	// problem here - computed size not matching the real size of the message
					cout << "ERROR: ConnectionManager[Send] wrong message size" << endl;	
				}
				*(int *)FinalMessage = FinalMessageSize;										// [Length of entire message]

				if (mConnection)
				{
					int amountSent = mConnection->SendMessage(FinalMessage, FinalMessageSize);
					if (amountSent == FinalMessageSize)
						sendSuccessful = true;
				}
			}
			// cleanup the temp buffer
			delete []FinalMessage;
			return sendSuccessful;
		}

		void GameTalkManager::StartBuffering (const string &target, int maxBufferedMessages, bool reset)
		{
			char intAsCharArray[10];
			_itoa(maxBufferedMessages, intAsCharArray, 10);
			string maxBufferedMessagesStr (intAsCharArray); 
			GameTalkMessage myMessage("Client Message");
			myMessage.AddKeyContent("StartBuffering", maxBufferedMessagesStr);
			// We don't need to send the request to reset (clear the buffer) unless it is true
			if (reset)
				myMessage.AddKeyContent("ClearBuffer", "True");
			Send( target, myMessage );
		}

		void GameTalkManager::StopBuffering(const string &target, bool reset)
		{
			GameTalkMessage myMessage("Client Message");
			myMessage.AddKeyContent("StopBuffering", "");
			// We don't need to send the request to reset (clear the buffer) unless it is true
			if (reset)
				myMessage.AddKeyContent("ClearBuffer", "True");
			Send(target, myMessage);
		}

		void GameTalkManager::RequestServerVersion()
		{
			GameTalkMessage myMessage("Client Message");
			myMessage.AddKeyContent("VersionRequest", "");
			Send("GameTalkServer", myMessage);
		}

		const char * GameTalkManager::ClientVersion()
		{
			return "2.9.102.0";
		}

		bool GameTalkManager::IsConnected()
		{
			return (mConnection != NULL);
		}

		int GameTalkManager::Receive()
		{
			int recvBytes = 0;

			GameTalkMessage receiveMessage;
			char* buffer = new char[MAX_RECV_LEN];
			if (buffer)
			{
				memset(buffer, 0, MAX_RECV_LEN);
				recvBytes = mInstance->mConnection->ReceiveMessage(&buffer, MAX_RECV_LEN);
				if (recvBytes > 0)
				{
					int pingReceived = 0;
					receiveMessage.AssembleFromBuffer(buffer, recvBytes);
					string channelId = receiveMessage.GetChannelId();
					if (!channelId.compare("PING"))
					{
						// is just a ping - don't call the callback
						pingReceived = 1;
					}
					else
					{
						if (mInstance->mMsgHandler != NULL)
						{
							// call the receive handler
							mInstance->mMsgHandler(&receiveMessage);
						}
					}
				}
				delete[] buffer;
				buffer = NULL;
			}

			return recvBytes;
		}

		// Create a thread to handle the network events
		void GameTalkManager::CreateNetworkEvents(int socketId)
		{
			mEvents = new GameTalkThread("receive", socketId, GameTalkManager::Receive);
			mEvents->start();
		}
		
		// Destroy the network events
		void GameTalkManager::DestroyNetworkEvents()
		{
			if (mEvents)
			{
				mEvents->stop();
				mEvents->destroy();
				delete mEvents;
			}
		}

		void GameTalkManager::RegisterMessageHandler(GameTalkMessageHandlerFnc* handlerFnc)
		{
			mMsgHandler = handlerFnc;
		}

		void GameTalkManager::UnRegisterMessageHandler(GameTalkMessageHandlerFnc* handlerFnc)
		{
			// cleanup the handler and make it available
			mMsgHandler = NULL;
		}

	} // namespace GameTalk

	// 
	namespace GameTalk
	{	
		GameTalkMessage::GameTalkMessage(const string &messageId)
		{
			MessageId	= messageId;
			ClearKeyContent();
			mBuffer		= NULL;
			mBufferSize = 0;
		}

		GameTalkMessage::GameTalkMessage()
		{
			MessageId.empty();
			ClearKeyContent();
			mBuffer		= NULL;
			mBufferSize = 0;
		}

		GameTalkMessage::GameTalkMessage(const GameTalkMessage &from )
		{
			MessageId=from.MessageId;
			mBufferSize=from.mBufferSize;
			if(mBufferSize)
			{
				mBuffer=new char[mBufferSize];
				memcpy(mBuffer,from.mBuffer,mBufferSize);
			}
			else
			{
				mBuffer=NULL;
			}

			int i;
			for(i=0;i<from.NumberOfKeys();++i)
			{
				AddKeyContent(from.GetKey(i),from.GetContent(i),from.GetContentSize(i),from.GetType(i));
			}
		}

		GameTalkMessage::~GameTalkMessage()
		{
			MessageId	= "";
			ClearKeyContent();
			if (mBuffer)
			{
				delete []mBuffer;
				mBuffer = NULL;
			}
			mBufferSize = 0;
		}

		GameTalkMessage &GameTalkMessage::operator=(const GameTalkMessage &from )
		{
			MessageId=from.MessageId;
			delete [] mBuffer;
			mBufferSize=from.mBufferSize;
			if(mBufferSize)
			{
				mBuffer=new char[mBufferSize];
				memcpy(mBuffer,from.mBuffer,mBufferSize);
			}
			else
			{
				mBuffer=NULL;
			}

			int i;
			for(i=0;i<from.NumberOfKeys();++i)
			{
				AddKeyContent(from.GetKey(i),from.GetContent(i),from.GetContentSize(i),from.GetType(i));
			}

			return *this;
		}

		int GameTalkMessage::NumberOfKeys() const
		{
			return static_cast<int>(mKeysContents.size());
		}
		
		void GameTalkMessage::AddKeyContent(const string &key, const string &content)
		{
			KeyContent *k	= new KeyContent;
			if (k)
			{
				k->Key			= key;
				k->Size			= static_cast<int>(content.length());
				k->Content		= new char[k->Size];
				memcpy(k->Content, content.c_str(), k->Size);
				mKeysContents.push_back(k);
			}
		}

		void GameTalkMessage::AddKeyContent(const string &key, void* content, int size, FormatTypes type)
		{
			KeyContent *k	= new KeyContent;
			if (k)
			{
				k->Type			= type;
				k->Key			= key;
				k->Size			= size;
				k->Content		= new char[size];
				memcpy(k->Content, content, k->Size);
				mKeysContents.push_back(k);
			}
		}

		void GameTalkMessage::ClearKeyContent()
		{
			while(!mKeysContents.empty())
			{	
				KeyContent *k = mKeysContents.back();
				mKeysContents.pop_back();
				delete k;
				k = NULL;
			}
			mKeysContents.clear();
		}

		string GameTalkMessage::GetKey(int index) const
		{
			if (index < (int)mKeysContents.size())
			{
				KeyContent* k = mKeysContents[index];
				return k->Key;
			}
			else
			{
				return NULL;
			}
		}

		void* GameTalkMessage::GetContent(int index) const
		{
			if (index < (int)mKeysContents.size())
			{
				KeyContent* k = mKeysContents[index];
				return k->Content;
			}
			else
			{
				return NULL;
			}
		}

		string GameTalkMessage::GetContentAsString(int index) const
		{
			string result;
			if (index < (int)mKeysContents.size())
			{
				KeyContent* k = mKeysContents[index];
				result.insert(0,(char*)k->Content,k->Size);
			}

			return result;
		}

		int GameTalkMessage::GetContentSize(int index) const
		{
			if (index < (int)mKeysContents.size())
			{
				KeyContent* k = mKeysContents[index];
				return k->Size;
			}
			else
			{
				return -1;
			}
		}

		GameTalkMessage::FormatTypes GameTalkMessage::GetType(int index) const
		{
			if (index < (int)mKeysContents.size())
			{
				KeyContent* k = mKeysContents[index];
				return k->Type;
			}
			else
			{
				return StringFormat;
			}
		}

		int GameTalkMessage::AssembleFromBuffer(char* buffer, int bufferSize)
		{
			int retVal = 0;
			
			if (buffer != NULL)
			{
				char	*crt				= buffer;
				int		MessageId_length	= *(int *)crt;		crt += 4;
				MessageId.append(crt, MessageId_length);		crt += MessageId_length;

				int KeysContents_size	= *(int *)crt;			crt += 4;
				if (KeysContents_size > 0)
				{
					for(int i=0; i<KeysContents_size; i++)
					{
						// Read key
						int KeyName_size = *(int *)crt;			crt += 4;
						string KeyName;
						KeyName.append(crt, KeyName_size);		crt += KeyName_size;

						// Read type
						int FormatType = *(int *)crt;			crt += 4;

						// Read content
						int KeyValue_size = *(int *)crt;		crt += 4;
						
						// decimal 65280 = binary 1111111100000000 - I don't think the &'ing is necessary - but meh...
						int FormatTypeAsOneOrZero = ((FormatType & 65280) >> 8);
						AddKeyContent(KeyName, crt, KeyValue_size, static_cast<FormatTypes>(FormatTypeAsOneOrZero));
						crt += KeyValue_size;
					}
				}
			}

			return retVal;
		}

		char *GameTalkMessage::AssembleToBuffer(char **pBuff, int *pLenBuff)
		{
			if (mBuffer)
			{
				delete []mBuffer;
				mBuffer = NULL;
			}
			
			mBufferSize 	= ComputeBufferSize();
			mBuffer			= new char[mBufferSize];
			if (mBuffer)
			{
				char *crt	= mBuffer;

				*(int*)crt = static_cast<int>(MessageId.length());
				crt	+= 4;					// message length
				memcpy(crt, MessageId.c_str(), MessageId.length());	crt += MessageId.length();	// message
				*(int*)crt = static_cast<int>(mKeysContents.size());
				crt += 4;					// number of key/value pairs
				
				for(vector<KeyContent *>::iterator it = mKeysContents.begin(); it != mKeysContents.end(); ++it)
				{
					KeyContent *crtKC = *it;
					
					// Write Key
					*(int*)crt = static_cast<int>(crtKC->Key.length());
					crt += 4;					// key length
					memcpy(crt, crtKC->Key.c_str(), crtKC->Key.length()); crt += crtKC->Key.length();	// key

					// Write Format
					*(int*)crt = static_cast<int>(crtKC->Type);
					crt += 4;						// value length

					// Write Content
					*(int*)crt = crtKC->Size;
					crt += 4;						// value length
					memcpy(crt, crtKC->Content, crtKC->Size); crt += crtKC->Size;	// value
				}
				
				int bufflen = static_cast<int>((crt - mBuffer) * sizeof(char));
				if (bufflen != mBufferSize)
				{ // detected a problem - computed buffer size does not match the actual size

				}
			}
			
			*pBuff		= mBuffer;
			*pLenBuff	= (mBuffer!=NULL) ? mBufferSize : 0;

			return mBuffer;
		}

		int GameTalkMessage::ComputeBufferSize()
		{	
			int crtBufferSize = 0;
			crtBufferSize += sizeof(int) + static_cast<int>(MessageId.length());	// mesage lenght(4B) + mesage
			crtBufferSize += sizeof(int);						// num keys(4B)	
			for(vector<KeyContent *>::iterator it = mKeysContents.begin(); it != mKeysContents.end(); ++it)
			{
				KeyContent *crtKC = *it;
				crtBufferSize += sizeof(int) + static_cast<int>(crtKC->Key.length());		// key lenght(4B) + key
				crtBufferSize += 4; // Format Type
				crtBufferSize += sizeof(int) + crtKC->Size;	// content lenght(4B) + content
			}

			return crtBufferSize;
		}
	}

	//
	namespace GameTalk
	{
		Exception::Exception(int errCode, const string &errMsg)
		{
			InitVars();
			mErrorCode = errCode;
			if (errMsg[0]) 
			{
				mErrorMsg.append(errMsg);
			}
		}

		void Exception::InitVars()
		{
			mErrorCode = 0;
			mErrorMsg = "";
		}

		void Exception::Response()
		{
			*Log::GetInstance() << "Error detect: " << endl;
			*Log::GetInstance() << "		==> error code: " << mErrorCode << endl;
			*Log::GetInstance() << "		==> error message: " << mErrorMsg << endl;
		}
	} // namespace GameTalk

	//
	namespace GameTalk
	{
		HostInfo::HostInfo()
		{
			char sName[HOST_NAME_LENGTH+1];
			memset(sName,0,sizeof(sName));
			gethostname(sName,HOST_NAME_LENGTH);

			try 
			{
				hostPtr = gethostbyname(sName);
				if (hostPtr == NULL)
				{
					int errorCode = 0;
					string	errorMsg = "";
					DetectErrorGethostbyname(&errorCode, errorMsg);
					Exception gethostbynameException(errorCode, errorMsg);
					throw gethostbynameException;
				}
			}
			catch (Exception &excp)
			{
				excp.Response();
			}
		}

		HostInfo::HostInfo(string hostName, hostType type)
		{
			try 
			{
				char nameBuf[256];
				strcpy(nameBuf, hostName.c_str());
				if ((stricmp(hostName.c_str(), "localhost") == 0) || (strcmp(hostName.c_str(), "127.0.0.1") == 0))
					gethostname(nameBuf, sizeof(nameBuf));

				if (type == NAME)
				{
					// Retrieve host by name
					hostPtr = gethostbyname(nameBuf);

					if (hostPtr == NULL)
					{
						int		errorCode;
						string	errorMsg = "";
						DetectErrorGethostbyname(&errorCode,errorMsg);
						Exception gethostbynameException(errorCode,errorMsg);
						throw gethostbynameException;
					}
				}
				else if (type == ADDRESS)
				{
					// Retrieve host by address
					unsigned long netAddr = inet_addr(nameBuf);
					if (netAddr == -1)
					{
						Exception inet_addrException(0,"Error calling inet_addr()");
						throw inet_addrException;
					}

					hostPtr = gethostbyaddr((char *)&netAddr, sizeof(netAddr), AF_INET);
					if (hostPtr == NULL)
					{
						int errorCode;
						string errorMsg = "";
						DetectErrorGethostbyaddr(&errorCode,errorMsg);
						Exception gethostbyaddrException(errorCode,errorMsg);
						throw gethostbyaddrException;
					}
				}
				else
				{
					Exception unknownTypeException(0,"unknown host type: host name/address has to be given ");
					throw unknownTypeException;
				}
			}
			catch (Exception& excp)
			{
				excp.Response();
			}
		}

		void HostInfo::DetectErrorGethostbyname(int* errCode, string &errorMsg)
		{
			*errCode = WSAGetLastError();
			
			if (*errCode == WSANOTINITIALISED)
				errorMsg.append("need to call WSAStartup to initialize socket system on Window system.");
			else if (*errCode == WSAENETDOWN)
				errorMsg.append("The network subsystem has failed.");
			else if (*errCode == WSAHOST_NOT_FOUND)
				errorMsg.append("Authoritative Answer Host not found.");
			else if (*errCode == WSATRY_AGAIN)
				errorMsg.append("Non-Authoritative Host not found, or server failure.");
			else if (*errCode == WSANO_RECOVERY)
				errorMsg.append("Nonrecoverable error occurred.");
			else if (*errCode == WSANO_DATA)
				errorMsg.append("Valid name, no data record of requested type.");
			else if (*errCode == WSAEINPROGRESS)
				errorMsg.append("A blocking Windows Sockets 1.1 call is in progress, or the service provider is still processing a callback function.");
			else if (*errCode == WSAEFAULT)
				errorMsg.append("The name parameter is not a valid part of the user address space.");
			else if (*errCode == WSAEINTR)
				errorMsg.append("A blocking Windows Socket 1.1 call was canceled through WSACancelBlockingCall.");
		}

		void HostInfo::DetectErrorGethostbyaddr(int* errCode,string& errorMsg)
		{
			*errCode = WSAGetLastError();

			if (*errCode == WSANOTINITIALISED)
				errorMsg.append("A successful WSAStartup must occur before using this function.");
			if (*errCode == WSAENETDOWN)
				errorMsg.append("The network subsystem has failed.");
			if (*errCode == WSAHOST_NOT_FOUND)
				errorMsg.append("Authoritative Answer Host not found.");
			if (*errCode == WSATRY_AGAIN)
				errorMsg.append("Non-Authoritative Host not found, or server failed."); 
			if (*errCode == WSANO_RECOVERY)
				errorMsg.append("Nonrecoverable error occurred.");
			if (*errCode == WSANO_DATA)
				errorMsg.append("Valid name, no data record of requested type.");
			if (*errCode == WSAEINPROGRESS)
				errorMsg.append("A blocking Windows Sockets 1.1 call is in progress, or the service provider is still processing a callback function.");
			if (*errCode == WSAEAFNOSUPPORT)
				errorMsg.append("The type specified is not supported by the Windows Sockets implementation.");
			if (*errCode == WSAEFAULT)
				errorMsg.append("The addr parameter is not a valid part of the user address space, or the len parameter is too small.");
			if (*errCode == WSAEINTR)
				errorMsg.append("A blocking Windows Socket 1.1 call was canceled through WSACancelBlockingCall.");
		}
	} // namespace GameTalk
	//

	//
	namespace GameTalk
	{
		const int MSG_HEADER_LEN = 4;

		Socket::Socket(int pNumber)
		{
			portNumber	= pNumber;
			blocking	= 1;

			try
			{	// last parameter to socket constructor can optionally be 0 as well	
				if ((socketId = static_cast<int>(socket(AF_INET, SOCK_STREAM, IPPROTO_TCP))) == -1)
				{
					int errorCode;
					string errorMsg = "";
					detectErrorOpenWinSocket(&errorCode,errorMsg);
					Exception openWinSocketException(errorCode,errorMsg);
					throw openWinSocketException;
				}
			}
			catch (Exception &excp)
			{
				excp.Response();
			}

			clientAddr.sin_family		= AF_INET;
			clientAddr.sin_addr.s_addr	= htonl(INADDR_ANY);
			clientAddr.sin_port			= htons(portNumber);
		}
		    
		void Socket::setDebug(int debugToggle)
		{
			try 
			{
				if (setsockopt(socketId,SOL_SOCKET,SO_DEBUG,(char *)&debugToggle,sizeof(debugToggle)) == -1)
				{
					int		errorCode;
					string	errorMsg = "DEBUG option:";
					detectErrorSetSocketOption(&errorCode,errorMsg);
					Exception socketOptionException(errorCode,errorMsg);
					throw socketOptionException;
				}
			}
			catch (Exception &excp)
			{
				excp.Response();
			}
		}

		void Socket::setReuseAddr(int reuseToggle)
		{
			try 
			{
				if (setsockopt(socketId,SOL_SOCKET,SO_REUSEADDR,(char *)&reuseToggle,sizeof(reuseToggle)) == -1)
				{
					int		errorCode;
					string	errorMsg = "REUSEADDR option:";
					detectErrorSetSocketOption(&errorCode,errorMsg);
					Exception socketOptionException(errorCode,errorMsg);
					throw socketOptionException;
				}
			}
			catch (Exception &excp)
			{
				excp.Response();
			}
		} 

		void Socket::setKeepAlive(int aliveToggle)
		{
			try 
			{
				if (setsockopt(socketId,SOL_SOCKET,SO_KEEPALIVE,(char *)&aliveToggle,sizeof(aliveToggle)) == -1)
				{
					int		errorCode;
					string	errorMsg = "ALIVE option:";
					detectErrorSetSocketOption(&errorCode,errorMsg);
					Exception socketOptionException(errorCode,errorMsg);
					throw socketOptionException;
				}
			}
			catch (Exception &excp)
			{
				excp.Response();
			}
		} 

		void Socket::setLingerSeconds(int seconds)
		{
			struct linger lingerOption;
			
			if (seconds > 0)
			{
				lingerOption.l_linger = seconds;
				lingerOption.l_onoff = 1;
			}
			else 
			{
				lingerOption.l_onoff = 0;
			}
			 
			try 
			{
				if (setsockopt(socketId,SOL_SOCKET,SO_LINGER,(char *)&lingerOption,sizeof(struct linger)) == -1)
				{
					int		errorCode;
					string	errorMsg = "LINGER option:";
					detectErrorSetSocketOption(&errorCode,errorMsg);
					Exception socketOptionException(errorCode,errorMsg);
					throw socketOptionException;
				}
			}
			catch (Exception &excp)
			{
				excp.Response();
			}
		}

		void Socket::setLingerOnOff(bool lingerOn)
		{
			struct linger lingerOption;

			if (lingerOn) 
				lingerOption.l_onoff = 1;
			else 
				lingerOption.l_onoff = 0;

			try 
			{
				if (setsockopt(socketId,SOL_SOCKET,SO_LINGER,(char *)&lingerOption,sizeof(struct linger)) == -1)
				{
					int		errorCode;
					string	errorMsg = "LINGER option:";
					detectErrorSetSocketOption(&errorCode,errorMsg);
					Exception socketOptionException(errorCode,errorMsg);
					throw socketOptionException;
				}
			}
			catch (Exception &excp)
			{
				excp.Response();
			}
		}

		void Socket::setSendBufSize(int sendBufSize)
		{
			try 
			{
				if (setsockopt(socketId,SOL_SOCKET,SO_SNDBUF,(char *)&sendBufSize,sizeof(sendBufSize)) == -1)
				{
					int		errorCode;
					string	errorMsg = "SENDBUFSIZE option:";
					detectErrorSetSocketOption(&errorCode,errorMsg);
					Exception socketOptionException(errorCode,errorMsg);
					throw socketOptionException;
				}
			}
			catch (Exception &excp)
			{
				excp.Response();
			}
		} 

		void Socket::setReceiveBufSize(int receiveBufSize)
		{
			try 
			{
				if (setsockopt(socketId,SOL_SOCKET,SO_RCVBUF,(char *)&receiveBufSize,sizeof(receiveBufSize)) == -1)
				{
					int		errorCode;
					string	errorMsg = "RCVBUF option:";
					detectErrorSetSocketOption(&errorCode,errorMsg);
					Exception socketOptionException(errorCode,errorMsg);
					throw socketOptionException;
				}
			}
			catch (Exception& excp)
			{
				excp.Response();
			}
		}

		void Socket::setSocketBlocking(int blockingToggle)
		{
			if (blockingToggle)
			{
				if (getSocketBlocking()) 
					return;
				else 
					blocking = 1;
			}
			else
			{
				if (!getSocketBlocking()) 
					return;
				else 
					blocking = 0;
			}

			try 
			{
				if (ioctlsocket(socketId,FIONBIO,(unsigned long *)&blocking) == -1)
				{
					int		errorCode;
					string	errorMsg = "Blocking option: ";
					detectErrorSetSocketOption(&errorCode,errorMsg);
					Exception socketOptionException(errorCode,errorMsg);
					throw socketOptionException;
				}
			}
			catch (Exception &excp)
			{
				excp.Response();
			}
		}

		int Socket::getDebug()
		{
			int Option;
			int OptionLen = sizeof(Option);

			try 
			{
				if (getsockopt(socketId,SOL_SOCKET,SO_DEBUG,(char *)&Option,&OptionLen) == -1)
				{
					int		errorCode;
					string	errorMsg = "get DEBUG option: ";
					detectErrorGetSocketOption(&errorCode,errorMsg);
					Exception socketOptionException(errorCode,errorMsg);
					throw socketOptionException;
				}
			}
			catch (Exception &excp)
			{
				excp.Response();
			}
		    
			return Option;
		}

		int Socket::getReuseAddr()
		{
			int Option;        
			int OptionLen = sizeof(Option);

			try 
			{
				if (getsockopt(socketId,SOL_SOCKET,SO_REUSEADDR,(char *)&Option,&OptionLen) == -1)
				{
					int		errorCode;
					string	errorMsg = "get REUSEADDR option: ";
					detectErrorGetSocketOption(&errorCode,errorMsg);
					Exception socketOptionException(errorCode,errorMsg);
					throw socketOptionException;
				}
			}
			catch (Exception &excp)
			{
				excp.Response();
			}

			return Option;
		}

		int Socket::getKeepAlive()
		{
			int Option;        
			int OptionLen = sizeof(Option);

			try 
			{
				if (getsockopt(socketId,SOL_SOCKET,SO_KEEPALIVE,(char *)&Option,&OptionLen) == -1)
				{
					int		errorCode;
					string	errorMsg = "get KEEPALIVE option: ";
					detectErrorGetSocketOption(&errorCode,errorMsg);
					Exception socketOptionException(errorCode,errorMsg);
					throw socketOptionException;
				}
			}
			catch (Exception &excp)
			{
				excp.Response();
			}
			return Option;    
		}

		int Socket::getLingerSeconds()
		{
			struct linger	lingerOption;
			int				OptionLen = sizeof(struct linger);

			try 
			{
				if (getsockopt(socketId,SOL_SOCKET,SO_LINGER,(char *)&lingerOption,&OptionLen) == -1)
				{
					int		errorCode;
					string	errorMsg = "get LINGER option: ";
					detectErrorGetSocketOption(&errorCode,errorMsg);
					Exception socketOptionException(errorCode,errorMsg);
					throw socketOptionException;
				}
			}
			catch (Exception &excp)
			{
				excp.Response();
			}

			return lingerOption.l_linger;
		}

		bool Socket::getLingerOnOff()
		{
			struct linger	lingerOption;
			int				OptionLen = sizeof(struct linger);

			try 
			{
				if (getsockopt(socketId,SOL_SOCKET,SO_LINGER,(char *)&lingerOption,&OptionLen) == -1)
				{
					int		errorCode;
					string	errorMsg = "get LINGER option: ";
					detectErrorGetSocketOption(&errorCode,errorMsg);
					Exception socketOptionException(errorCode,errorMsg);
					throw socketOptionException;
				}
			}
			catch (Exception &excp)
			{
				excp.Response();
			}

			if (lingerOption.l_onoff == 1) 
				return true;
			else 
				return false;
		}

		int Socket::getSendBufSize()
		{
			int sendBuf;
			int OptionLen = sizeof(sendBuf);

			try 
			{
				if (getsockopt(socketId, SOL_SOCKET, SO_SNDBUF, (char *)&sendBuf, &OptionLen) == -1)
				{
					int		errorCode;
					string	errorMsg = "get SNDBUF option: ";
					detectErrorGetSocketOption(&errorCode,errorMsg);
					Exception socketOptionException(errorCode,errorMsg);
					throw socketOptionException;
				}
			}
			catch (Exception &excp)
			{
				excp.Response();
			}
			return sendBuf;
		}    

		int Socket::getReceiveBufSize()
		{
			int rcvBuf;
			int OptionLen = sizeof(rcvBuf);

			try 
			{
				if (getsockopt(socketId, SOL_SOCKET,SO_RCVBUF, (char *)&rcvBuf, &OptionLen) == -1)
				{
					int		errorCode;
					string	errorMsg = "get RCVBUF option: ";
					detectErrorGetSocketOption(&errorCode,errorMsg);
					Exception socketOptionException(errorCode,errorMsg);
					throw socketOptionException;
				}
			}
			catch (Exception &excp)
			{
				excp.Response();
			}
			return rcvBuf;
		}


		void Socket::detectErrorOpenWinSocket(int *errCode, string &errMsg)
		{
			*errCode = WSAGetLastError();

			if (*errCode == WSANOTINITIALISED)
				errMsg.append("Successful WSAStartup must occur before using this function.");
			else if (*errCode == WSAENETDOWN)
				errMsg.append("The network subsystem or the associated service provider has failed.");
			else if (*errCode == WSAEAFNOSUPPORT)
				errMsg.append("The specified address family is not supported.");
			else if (*errCode == WSAEINPROGRESS)
				errMsg.append("A blocking Windows Sockets 1.1 call is in progress, or the service provider is still processing a callback function.");
			else if (*errCode == WSAEMFILE)
				errMsg.append("No more socket descriptors are available.");
			else if (*errCode == WSAENOBUFS)
				errMsg.append("No buffer space is available. The socket cannot be created.");
			else if (*errCode == WSAEPROTONOSUPPORT)
				errMsg.append("The specified protocol is not supported.");
			else if (*errCode == WSAEPROTOTYPE)
				errMsg.append("The specified protocol is the wrong type for this socket.");
			else if (*errCode == WSAESOCKTNOSUPPORT)
				errMsg.append("The specified socket type is not supported in this address family.");
			else errMsg.append("unknown problems!");
		}

		void Socket::detectErrorSetSocketOption(int *errCode, string &errMsg)
		{
			*errCode = WSAGetLastError();

			if (*errCode == WSANOTINITIALISED)
				errMsg.append("A successful WSAStartup must occur before using this function.");
			else if (*errCode == WSAENETDOWN)
				errMsg.append("The network subsystem has failed.");
			else if (*errCode == WSAEFAULT)
				errMsg.append("optval is not in a valid part of the process address space or optlen parameter is too small.");
			else if (*errCode == WSAEINPROGRESS)
				errMsg.append("A blocking Windows Sockets 1.1 call is in progress, or the service provider is still processing a callback function.");
			else if (*errCode == WSAEINVAL)
				errMsg.append("level is not valid, or the information in optval is not valid.");
			else if (*errCode == WSAENETRESET)
				errMsg.append("Connection has timed out when SO_KEEPALIVE is set.");
			else if (*errCode == WSAENOPROTOOPT)
				errMsg.append("The option is unknown or unsupported for the specified provider or socket (see SO_GROUP_PRIORITY limitations).");
			else if (*errCode == WSAENOTCONN)
				errMsg.append("Connection has been reset when SO_KEEPALIVE is set.");
			else if (*errCode == WSAENOTSOCK)
				errMsg.append("The descriptor is not a socket.");
			else errMsg.append("unknown problem!");
		}

		void Socket::detectErrorGetSocketOption(int *errCode,string &errMsg)
		{
			*errCode = WSAGetLastError();

			if (*errCode == WSANOTINITIALISED)
				errMsg.append("A successful WSAStartup must occur before using this function.");
			else if (*errCode == WSAENETDOWN)
				errMsg.append("The network subsystem has failed.");
			else if (*errCode == WSAEFAULT)
				errMsg.append("One of the optval or the optlen parameters is not a valid part of the user address space, or the optlen parameter is too small.");
			else if (*errCode == WSAEINPROGRESS)
				errMsg.append("A blocking Windows Sockets 1.1 call is in progress, or the service provider is still processing a callback function.");
			else if (*errCode == WSAEINVAL)
				errMsg.append("The level parameter is unknown or invalid.");
			else if (*errCode == WSAENOPROTOOPT)
				errMsg.append("The option is unknown or unsupported by the indicated protocol family.");
			else if (*errCode == WSAENOTSOCK)
				errMsg.append("The descriptor is not a socket.");

			else errMsg.append("unknown problems!");
		}

		ostream &operator<<(ostream &io, Socket &s)
		{
			string flagStr = "";

			io << endl;
			io << "Summary of socket settings:" << endl;
			io << "   Socket Id:     " << s.getSocketId() << endl;
			io << "   port #:        " << s.getPortNumber() << endl;
			io << "   debug:         " << (flagStr = s.getDebug()? "true":"false") << endl;
			io << "   reuse addr:    " << (flagStr = s.getReuseAddr()? "true":"false") << endl;
			io << "   keep alive:    " << (flagStr = s.getKeepAlive()? "true":"false") << endl;
			io << "   send buf size: " << s.getSendBufSize() << endl;
			io << "   recv bug size: " << s.getReceiveBufSize() << endl;
			io << "   blocking:      " << (flagStr = s.getSocketBlocking()? "true":"false") << endl;
			io << "   linger on:     " << (flagStr = s.getLingerOnOff()? "true":"false") << endl;
			io << "   linger seconds: " << s.getLingerSeconds() << endl;
			io << endl;
			return io;
		}

		TcpSocket::~TcpSocket()
		{
			// Close the winsock library
			*Log::GetInstance() << endl << "system shut down ...";		
			try 
			{
				if (WSACleanup())
				{
					Exception cleanupException(0, "Error: calling WSACleanup()");
					throw cleanupException;
				}
			}
			catch (Exception &excp)
			{
				excp.Response();
			}
			*Log::GetInstance() << "successful" << endl;
		}

		bool TcpSocket::Initialize()
		{
			bool result = false;

			WSADATA wsaData;
			WORD    wVer	= MAKEWORD(2,2); //MAKEWORD(1,1); //@dan - remember that i'm using 2.2
			*Log::GetInstance() << "system started ..." << endl;
			*Log::GetInstance() << endl << "initialize the winsock library ... ";
			try 
			{
				if (WSAStartup(wVer, &wsaData))
				{
					Exception initializationException(0, "Error: calling WSAStartup()");
					throw initializationException;
				}
				else
				{
					result = true;
				}
			}
			catch (Exception &excp)
			{
				excp.Response();
			}
			*Log::GetInstance() << "successful" << endl;

			return result;
		}

		bool TcpSocket::BindSocket()
		{
			bool result = false;
			try
			{
				if (bind(socketId, (struct sockaddr *)&clientAddr, sizeof(struct sockaddr_in))==-1)
				{
					int		errorCode = 0;
					string	errorMsg = "error calling bind(): \n";
					DetectErrorBind(&errorCode,errorMsg);
					Exception socketBindException(errorCode,errorMsg);
					throw socketBindException;
				}
				else
				{
					result = true;
				}
			}
			catch (Exception &excp)
			{
				excp.Response();
			}

			return result;
		}

		void TcpSocket::DetectErrorBind(int *errCode, string &errMsg)
		{
			*errCode = WSAGetLastError();

			if (*errCode == WSANOTINITIALISED)
				errMsg.append("A successful WSAStartup must occur before using this function.");
			else if (*errCode == WSAENETDOWN)
				errMsg.append("The network subsystem has failed.");
			else if (*errCode == WSAEADDRINUSE)
			{
				errMsg.append("A process on the machine is already bound to the same\n");
				errMsg.append("fully-qualified address and the socket has not been marked\n"); 
				errMsg.append("to allow address re-use with SO_REUSEADDR. For example,\n");
				errMsg.append("IP address and port are bound in the af_inet case");
			}
			else if (*errCode == WSAEADDRNOTAVAIL)
				errMsg.append("The specified address is not a valid address for this machine.");
			else if (*errCode == WSAEFAULT)
			{
				errMsg.append("The name or the namelen parameter is not a valid part of\n");
				errMsg.append("the user address space, the namelen parameter is too small,\n");
				errMsg.append("the name parameter contains incorrect address format for the\n");
				errMsg.append("associated address family, or the first two bytes of the memory\n");
				errMsg.append("block specified by name does not match the address family\n");
				errMsg.append("associated with the socket descriptor s.");
			}
			else if (*errCode == WSAEINPROGRESS)
			{
				errMsg.append("A blocking Windows Sockets 1.1 call is in progress, or the\n");
				errMsg.append("service provider is still processing a callback function.");
			}
			else if (*errCode == WSAEINVAL)
				errMsg.append("The socket is already bound to an address. ");
			else if (*errCode == WSAENOBUFS)
				errMsg.append("Not enough buffers available, too many connections.");
			else if (*errCode == WSAENOTSOCK)
				errMsg.append("The descriptor is not a socket.");
			else errMsg.append("unknown problems!");
		}

		void TcpSocket::DetectErrorRecv(int *errCode,string &errMsg)
		{
			*errCode = WSAGetLastError();

			if (*errCode == WSANOTINITIALISED)
				errMsg.append("A successful WSAStartup must occur before using this function.");
			else if (*errCode == WSAENETDOWN)
				errMsg.append("The network subsystem has failed.");
			else if (*errCode == WSAEFAULT)
				errMsg.append("The buf parameter is not completely contained in a valid part of the user address space.");
			else if (*errCode == WSAENOTCONN)
				errMsg.append("The socket is not connected.");
			else if (*errCode == WSAEINTR)
				errMsg.append("The (blocking) call was canceled through WSACancelBlockingCall.");
			else if (*errCode == WSAEINPROGRESS)
			{
				errMsg.append("A blocking Windows Sockets 1.1 call is in progress, or the\n");
				errMsg.append("service provider is still processing a callback function.");
			}
			else if (*errCode == WSAENETRESET)
			{
				errMsg.append("The connection has been broken due to the keep-alive activity\n");
				errMsg.append("detecting a failure while the operation was in progress.");
			}
			else if (*errCode == WSAENOTSOCK)
				errMsg.append("The descriptor is not a socket.");
			else if (*errCode == WSAEOPNOTSUPP)
			{
				errMsg.append("MSG_OOB was specified, but the socket is not stream-style\n");
				errMsg.append("such as type SOCK_STREAM, out-of-band data is not supported\n");
				errMsg.append("in the communication domain associated with this socket, or\n");
				errMsg.append("the socket is unidirectional and supports only send operations.");
			}
			else if (*errCode == WSAESHUTDOWN)
			{
				errMsg.append("The socket has been shut down; it is not possible to recv on a\n");
				errMsg.append("socket after shutdown has been invoked with how set to SD_RECEIVE or SD_BOTH.");
			}
			else if (*errCode == WSAEWOULDBLOCK)
				errMsg.append("The socket is marked as nonblocking and the receive operation would block.");
			else if (*errCode == WSAEMSGSIZE)
				errMsg.append("The message was too large to fit into the specified buffer and was truncated.");
			else if (*errCode == WSAEINVAL)
			{
				errMsg.append("The socket has not been bound with bind, or an unknown flag\n");
				errMsg.append("was specified, or MSG_OOB was specified for a socket with\n");
				errMsg.append("SO_OOBINLINE enabled or (for byte stream sockets only) len was zero or negative.");
			}
			else if (*errCode == WSAECONNABORTED)
			{
				errMsg.append("The virtual circuit was terminated due to a time-out or\n");
				errMsg.append("other failure. The application should close the socket as it is no longer usable.");
			}
			else if (*errCode == WSAETIMEDOUT)
			{
				errMsg.append("The connection has been dropped because of a network\n");
				errMsg.append("failure or because the peer system failed to respond.");
			}
			else if (*errCode == WSAECONNRESET)
			{
				errMsg.append("The virtual circuit was reset by the remote side executing a\n");
				errMsg.append("\"hard\" or \"abortive\" close. The application should close\n");
				errMsg.append("the socket as it is no longer usable. On a UDP datagram socket\n");
				errMsg.append("this error would indicate that a previous send operation\n");
				errMsg.append("resulted in an ICMP \"Port Unreachable\" message.");
			}
			else errMsg.append("unknown problems!");
		}

		void TcpSocket::DetectErrorConnect(int *errCode,string &errMsg)
		{
			*errCode = WSAGetLastError();

			if (*errCode == WSANOTINITIALISED)
				errMsg.append("A successful WSAStartup must occur before using this function.");
			else if (*errCode == WSAENETDOWN)
				errMsg.append("The network subsystem has failed.");
			else if (*errCode == WSAEADDRINUSE)
			{
				errMsg.append("The socket's local address is already in use and the socket\n");
				errMsg.append("was not marked to allow address reuse with SO_REUSEADDR. This\n");
				errMsg.append("error usually occurs when executing bind, but could be delayed\n");
				errMsg.append("until this function if the bind was to a partially wild-card\n");
				errMsg.append("address (involving ADDR_ANY) and if a specific address needs\n");
				errMsg.append("to be committed at the time of this function.");
			}
			else if (*errCode == WSAEINTR)
				errMsg.append("The (blocking) Windows Socket 1.1 call was canceled through WSACancelBlockingCall.");
			else if (*errCode == WSAEINPROGRESS)
			{
				errMsg.append("A blocking Windows Sockets 1.1 call is in progress, or\n");
				errMsg.append("the service provider is still processing a callback function.");
			}
			else if (*errCode == WSAEALREADY)
			{
				errMsg.append("A nonblocking connect call is in progress on the specified socket.\n");
				errMsg.append("Note In order to preserve backward compatibility, this error is\n");
				errMsg.append("reported as WSAEINVAL to Windows Sockets 1.1 applications that\n");
				errMsg.append("link to either WINSOCK.DLL or WSOCK32.DLL.");
			}
			else if (*errCode == WSAEADDRNOTAVAIL)
				errMsg.append("The remote address is not a valid address (such as ADDR_ANY).");
			else if (*errCode == WSAEAFNOSUPPORT)
				errMsg.append("Addresses in the specified family cannot be used with this socket.");
			else if (*errCode == WSAECONNREFUSED)
				errMsg.append("The attempt to connect was forcefully rejected.");
			else if (*errCode == WSAEFAULT)
			{
				errMsg.append("The name or the namelen parameter is not a valid part of\n");
				errMsg.append("the user address space, the namelen parameter is too small,\n");
				errMsg.append("or the name parameter contains incorrect address format for\n");
				errMsg.append("the associated address family.");
			}
			else if (*errCode == WSAEINVAL)
			{
				errMsg.append("The parameter s is a listening socket, or the destination\n");
				errMsg.append("address specified is not consistent with that of the constrained\n");
				errMsg.append("group the socket belongs to.");
			}
			else if (*errCode == WSAEISCONN)
				errMsg.append("The socket is already connected (connection-oriented sockets only).");
			else if (*errCode == WSAENETUNREACH)
				errMsg.append("The network cannot be reached from this host at this time.");
			else if (*errCode == WSAENOBUFS)
				errMsg.append("No buffer space is available. The socket cannot be connected.");
			else if (*errCode == WSAENOTSOCK)
				errMsg.append("The descriptor is not a socket.");
			else if (*errCode == WSAETIMEDOUT)
				errMsg.append("Attempt to connect timed out without establishing a connection.");
			else if (*errCode == WSAEWOULDBLOCK)
			{
				errMsg.append("The socket is marked as nonblocking and the connection\n");
				errMsg.append("cannot be completed immediately.");
			}
			else if (*errCode == WSAEACCES)
			{
				errMsg.append("Attempt to connect datagram socket to broadcast address failed\n");
				errMsg.append("because setsockopt option SO_BROADCAST is not enabled.");
			}
			else errMsg.append("unknown problems!");
		}

		void TcpSocket::DetectErrorAccept(int *errCode,string &errMsg)
		{
			*errCode = WSAGetLastError();

			if (*errCode == WSANOTINITIALISED)
				errMsg.append("A successful WSAStartup must occur before using this FUNCTION.");
			else if (*errCode == WSAENETDOWN)
				errMsg.append("The network subsystem has failed.");
			else if (*errCode == WSAEFAULT)
				errMsg.append("The addrlen parameter is too small or addr is not a valid part of the user address space.");
			else if (*errCode == WSAEINTR)
				errMsg.append("A blocking Windows Sockets 1.1 call was canceled through WSACancelBlockingCall.");
			else if (*errCode == WSAEINPROGRESS)
			{
				errMsg.append("A blocking Windows Sockets 1.1 call is in progress, or the\n");
				errMsg.append("service provider is still processing a callback function.");
			}
			else if (*errCode == WSAEINVAL)
				errMsg.append("The listen function was not invoked prior to accept.");
			else if (*errCode == WSAEMFILE)
				errMsg.append("The queue is nonempty upon entry to accept and there are no descriptors available.");
			else if (*errCode == WSAENOBUFS)
				errMsg.append("No buffer space is available.");
			else if (*errCode == WSAENOTSOCK)
				errMsg.append("The descriptor is not a socket.");
			else if (*errCode == WSAEOPNOTSUPP)
				errMsg.append("The referenced socket is not a type that supports connection-oriented service.");
			else if (*errCode == WSAEWOULDBLOCK)
				errMsg.append("The socket is marked as nonblocking and no connections are present to be accepted.");
			else errMsg.append("unknown problems!");
		}

		void TcpSocket::DetectErrorListen(int *errCode,string &errMsg)
		{
			*errCode = WSAGetLastError();

			if (*errCode == WSANOTINITIALISED)
				errMsg.append("A successful WSAStartup must occur before using this function.");
			else if (*errCode == WSAENETDOWN)
				errMsg.append("The network subsystem has failed.");
			else if (*errCode == WSAEADDRINUSE)
			{
				errMsg.append("The socket's local address is already in use and the socket was\n");
				errMsg.append("not marked to allow address reuse with SO_REUSEADDR. This error\n");
				errMsg.append("usually occurs during execution of the bind function, but could\n");
				errMsg.append("be delayed until this function if the bind was to a partially\n");
				errMsg.append("wild-card address (involving ADDR_ANY) and if a specific address\n");
				errMsg.append("needs to be \"committed\" at the time of this function.");
			}
			else if (*errCode == WSAEINPROGRESS)
			{
				errMsg.append("A blocking Windows Sockets 1.1 call is in progress, or the service\n");
				errMsg.append("provider is still processing a callback function.");
			}
			else if (*errCode == WSAEINVAL)
				errMsg.append("The socket has not been bound with bind.");
			else if (*errCode == WSAEISCONN)
				errMsg.append("The socket is already connected.");
			else if (*errCode == WSAEMFILE)
				errMsg.append("No more socket descriptors are available.");
			else if (*errCode == WSAENOBUFS)
				errMsg.append("No buffer space is available.");
			else if (*errCode == WSAENOTSOCK)
				errMsg.append("The descriptor is not a socket.");
			else if (*errCode == WSAEOPNOTSUPP)
				errMsg.append("The referenced socket is not of a type that supports the listen operation.");
			else errMsg.append("unknown problems!");
		}

		void TcpSocket::DetectErrorSend(int *errCode,string &errMsg)
		{
			*errCode = WSAGetLastError();

			if (*errCode == WSANOTINITIALISED)
				errMsg.append("A successful WSAStartup must occur before using this function.");
			else if (*errCode == WSAENETDOWN)
				errMsg.append("The network subsystem has failed.");
			else if (*errCode == WSAEACCES)
			{
				errMsg.append("The requested address is a broadcast address,\n");
				errMsg.append("but the appropriate flag was not set. Call setsockopt\n");
				errMsg.append("with the SO_BROADCAST parameter to allow the use of the broadcast address.");
			}
			else if (*errCode == WSAEINTR)
			{
				errMsg.append("A blocking Windows Sockets 1.1 call was canceled\n");
				errMsg.append("through WSACancelBlockingCall.");
			}
			else if (*errCode == WSAEINPROGRESS)
			{
				errMsg.append("A blocking Windows Sockets 1.1 call is in progress,\n");
				errMsg.append("or the service provider is still processing a callback function.");
			}
			else if (*errCode == WSAEFAULT)
			{
				errMsg.append("The buf parameter is not completely contained in a\n");
				errMsg.append("valid part of the user address space.");
			}
			else if (*errCode == WSAENETRESET)
			{
				errMsg.append("The connection has been broken due to the keep-alive\n");
				errMsg.append("activity detecting a failure while the operation was in progress.");
			}
			else if (*errCode == WSAENOBUFS)
				errMsg.append("No buffer space is available.");
			else if (*errCode == WSAENOTCONN)
				errMsg.append("The socket is not connected.");
			else if (*errCode == WSAENOTSOCK)
				errMsg.append("The descriptor is not a socket.");
			else if (*errCode == WSAEOPNOTSUPP)
			{
				errMsg.append("MSG_OOB was specified, but the socket is not stream-style\n");
				errMsg.append("such as type SOCK_STREAM, out-of-band data is not supported\n");
				errMsg.append("in the communication domain associated with this socket,\n");
				errMsg.append("or the socket is unidirectional and supports only receive operations.");
			}
			else if (*errCode == WSAESHUTDOWN)
			{
				errMsg.append("The socket has been shut down; it is not possible to send\n");
				errMsg.append("on a socket after shutdown has been invoked with how set\n");
				errMsg.append("to SD_SEND or SD_BOTH.");
			}
			else if (*errCode == WSAEWOULDBLOCK)
				errMsg.append("The socket is marked as nonblocking and the requested operation would block.\n");
			else if (*errCode == WSAEMSGSIZE)
			{
				errMsg.append("The socket is message oriented, and the message is larger\n");
				errMsg.append("than the maximum supported by the underlying transport.");
			}
			else if (*errCode == WSAEHOSTUNREACH)
				errMsg.append("The remote host cannot be reached from this host at this time.");
			else if (*errCode == WSAEINVAL)
			{
				errMsg.append("The socket has not been bound with bind, or an unknown flag\n");
				errMsg.append("was specified, or MSG_OOB was specified for a socket with SO_OOBINLINE enabled.");
			}
			else if (*errCode == WSAECONNABORTED)
			{
				errMsg.append("The virtual circuit was terminated due to a time-out or \n");
				errMsg.append("other failure. The application should close the socket as it is no longer usable.");
			}
			else if (*errCode == WSAECONNRESET)
			{
				errMsg.append("The virtual circuit was reset by the remote side executing a \"hard\" \n");
				errMsg.append("or \"abortive\" close. For UPD sockets, the remote host was unable to\n");
				errMsg.append("deliver a previously sent UDP datagram and responded with a\n");
				errMsg.append("\"Port Unreachable\" ICMP packet. The application should close\n");
				errMsg.append("the socket as it is no longer usable.");
			}
			else if (*errCode == WSAETIMEDOUT)
			{
				errMsg.append("The connection has been dropped, because of a network failure\n");
				errMsg.append("or because the system on the other end went down without notice.");
			}
			else errMsg.append("unknown problems!");
		}

		bool TcpSocket::ConnectToServer(string &serverNameOrAddr, hostType hType)
		{ 
			HostInfo serverInfo(serverNameOrAddr,hType);
			
			// Store the IP address and socket port number	
			struct sockaddr_in serverAddress;
			serverAddress.sin_family		= AF_INET;
			serverAddress.sin_addr.s_addr	= inet_addr(serverInfo.GetHostIPAddress());
			serverAddress.sin_port			= htons(portNumber);

			bool result = false;
			// Connect...
			try 
			{
				if (connect(socketId,(struct sockaddr *)&serverAddress, sizeof(serverAddress)) == -1)
				{
					int		errorCode = 0;
					string	errorMsg = "error calling connect():\n";
					DetectErrorConnect(&errorCode,errorMsg);
					Exception socketConnectException(errorCode,errorMsg);
					throw socketConnectException;
				}
				else
				{
					result = true;
				}
			}
			catch (Exception &excp)
			{
				excp.Response();
			}

			return result;
		}

		TcpSocket *TcpSocket::AcceptClient(string &clientHost)
		{
			int newSocket;   // the new socket file descriptor returned by the accept system call

			// the length of the client's address
			int					clientAddressLen = sizeof(struct sockaddr_in);
			struct sockaddr_in	clientAddress;    // Address of the client that sent data

			// Accepts a new client connection and stores its socket file descriptor
			try 
			{
				if ((newSocket = static_cast<int>(accept(socketId, (struct sockaddr *)&clientAddress, &clientAddressLen))) == -1)
				{
					int		errorCode	= 0;
					string	errorMsg	= "error calling accept(): \n";
					DetectErrorAccept(&errorCode,errorMsg);
					Exception socketAcceptException(errorCode,errorMsg);
					throw socketAcceptException;
				}
			}
			catch (Exception &excp)
			{
				excp.Response();
				return NULL;
			}
		    
			// Get the host name given the address
			char		*sAddress = inet_ntoa((struct in_addr)clientAddress.sin_addr);
			HostInfo	clientInfo(string(sAddress),ADDRESS);
			char		*hostName = clientInfo.GetHostName();
			clientHost += string(hostName);
			
			// Create and return the new myTcpSocket object
			TcpSocket *retSocket = new TcpSocket();
			retSocket->setSocketId(newSocket);

			return retSocket;
		}

		void TcpSocket::ListenToClient(int totalNumPorts)
		{
			try 
			{
				if (listen(socketId,totalNumPorts) == -1)
				{
					int		errorCode	= 0;
					string	errorMsg	= "error calling listen():\n";
					DetectErrorListen(&errorCode,errorMsg);
					Exception socketListenException(errorCode,errorMsg);
					throw socketListenException;
				}
			}
			catch (Exception &excp)
			{
				excp.Response();
			}
		}

		//
		// Note:
		//	I'm using this only in my tests
		int TcpSocket::SendMessage(string &message)
		{
			int numBytes;  // the number of bytes sent
		#if 1		
			//@dan remember:
			//for each message to be sent, add a header which shows how long this message
			//is. This header, regardless how long the real message is, will always be
			//of the length MSG_HEADER_LEN.
			char msgLength[MSG_HEADER_LEN+1];
			sprintf(msgLength,"%4d",message.size());
			string sendMsg = string(msgLength);
			sendMsg += message;
		#else
			// just send the message - the layers above will take care of message format
			string sendMsg = message;
		#endif
			// Sends the message to the connected host
			try 
			{
				numBytes = static_cast<int>(send(socketId, sendMsg.c_str(), static_cast<int>(sendMsg.size()), 0));
				if (numBytes == -1)
				{
					int		errorCode	= 0;
					string	errorMsg	= "error calling send():\n";
					DetectErrorSend(&errorCode,errorMsg);
					Exception socketSendException(errorCode,errorMsg);
					throw socketSendException;
				}
			}
			catch (Exception &excp)
			{
				excp.Response();
			}

			return numBytes;
		}

		int TcpSocket::SendMessage(char *sendMsg, size_t size)
		{
			int numBytes = 0;  // the number of bytes sent
			// Sends the message to the connected host
			try 
			{
				if ((numBytes = send(socketId, sendMsg, size, 0)) == -1)
				{
					int		errorCode	= 0;
					string	errorMsg	= "error calling send():\n";
					DetectErrorSend(&errorCode,errorMsg);
					Exception socketSendException(errorCode,errorMsg);
					throw socketSendException;
				}
			}
			catch (Exception &excp)
			{
				excp.Response();
			}

			return numBytes;

		}

		int TcpSocket::ReceiveMessage(string &message)
		{
			int received		= 0;					// The number of bytes received
			int msgSize			= MAX_RECV_LEN;			// The number of bytes wanted to receive
			int numBytes		= 0;					// The number of bytes currently received
			int totalRecvNum	= 0;
			bool headerFinished = false;				// MSG_HEADER_LEN Bytes holding the total length of the message

			char charMsg[MAX_RECV_LEN+1];
			char msgLength[MSG_HEADER_LEN+1];
			memset(charMsg, 0, sizeof(charMsg));
			memset(msgLength, 0, sizeof(msgLength));

			try
			{
				while(received < msgSize)
				{
					numBytes = recv(socketId, charMsg+received, 1, 0);
					if (numBytes == -1)
					{
						int errorCode = 0;
						string errorMsg = "error calling recv():\n";
						DetectErrorRecv(&errorCode,errorMsg);
						Exception socketRecvException(errorCode,errorMsg);
						throw socketRecvException;
					}
					if (!headerFinished)
					{
						msgLength[received] = *(charMsg+received);
						received++;

						if (received == MSG_HEADER_LEN)
						{
							headerFinished	= true;
							received		= 0;
							memset(charMsg,0,sizeof(charMsg));
							msgSize = atoi(msgLength);
						}
					}
					else
					{
						received++;
					}
				}
			}
			catch (Exception &excp)
			{
				excp.Response();
				if (excp.GetErrCode() == WSAECONNRESET)
				{
					*Log::GetInstance() << "!! your party has shut down the connection... \n";
					return -99;
				}
			}

			message.append(string(charMsg));
			return msgSize;
		}

		int TcpSocket::ReceiveMessage(char **receiveMsg , int size)
		{
			int received		= 0;					// The number of bytes received
			int msgSize			= MAX_RECV_LEN;			// The number of bytes wanted to receive
			int numBytes		= 0;					// The number of bytes currently received
			int totalRecvNum	= 0;
			bool headerFinished = false;				// 4 B holding the total length of the message

			char charMsg[MAX_RECV_LEN+1];
			char msgLength[MSG_HEADER_LEN+1];
			memset(charMsg, 0, sizeof(charMsg));
			memset(msgLength, 0, sizeof(msgLength));

			try
			{
				while(received < msgSize)
				{
					numBytes = recv(socketId, charMsg+received, 1, 0);
					if (numBytes == -1)
					{
						int errorCode = 0;
						string errorMsg = "error calling recv():\n";
						DetectErrorRecv(&errorCode,errorMsg);
						Exception socketRecvException(errorCode,errorMsg);
						throw socketRecvException;
					}
					if (!headerFinished)
					{
						msgLength[received] = *(charMsg+received);
						received++;

						if (received == MSG_HEADER_LEN)
						{
							headerFinished	= true;
							received		= 0;
							memset(charMsg, 0, sizeof(charMsg));
							//msgSize = atoi(msgLength);
							msgSize = *(int*)&msgLength[0];
						}
					}
					else
					{
						received++;
					}
				}
			}
			catch (Exception &excp)
			{
				excp.Response();
				return 0;
			}

			if (msgSize <= size)
			{
				memcpy(*receiveMsg, charMsg, msgSize);
			}
			else
			{
				msgSize = 0; // force it to 0 if problems
			}

			return msgSize;
		}

	} // namespace GameTalk

	//
	namespace GameTalkInstaller
	{
		int CountProcesses(const char *name)
		{
			DWORD aProcesses[1024], cbNeeded;
			if( !EnumProcesses( aProcesses, sizeof(aProcesses), &cbNeeded ) )
			{
				return 0;
			}

			DWORD cProcesses = cbNeeded / sizeof(DWORD);
			int matchingProcesses(0);
			TCHAR szProcessName[MAX_PATH];
			unsigned int i;
			for ( i = 0; i < cProcesses; ++i )
			{
				// Get a handle to the process.
				HANDLE hProcess = OpenProcess( PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, aProcesses[i] );
				// Get the process name.
				if (NULL != hProcess )
				{
					HMODULE hMod;
					DWORD cbNeeded;

					if ( EnumProcessModules( hProcess, &hMod, sizeof(hMod), &cbNeeded) )
					{
						GetModuleBaseName( hProcess, hMod, szProcessName, sizeof(szProcessName)/sizeof(TCHAR) );
						if(_stricmp(name,szProcessName)==0)
						{
							++matchingProcesses;
						}
					}
				}

				CloseHandle( hProcess );
			}
			return matchingProcesses;
		}

		void KillAllProcessesWithName(const char *name)
		{
			DWORD aProcesses[1024], cbNeeded;
			if( !EnumProcesses( aProcesses, sizeof(aProcesses), &cbNeeded ) )
			{
				return;
			}

			DWORD cProcesses = cbNeeded / sizeof(DWORD);
			TCHAR szProcessName[MAX_PATH];
			unsigned int i;
			for ( i = 0; i < cProcesses; ++i )
			{
				// Get a handle to the process.
				HANDLE hProcess = OpenProcess( PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, aProcesses[i] );

				// Get the process name.
				if ( NULL != hProcess )
				{
					HMODULE hMod;
					DWORD cbNeeded;

					if ( EnumProcessModules( hProcess, &hMod, sizeof(hMod), &cbNeeded) )
					{
						GetModuleBaseName( hProcess, hMod, szProcessName, sizeof(szProcessName)/sizeof(TCHAR) );
						if(_stricmp(name,szProcessName) == 0)
						{
							// CAUTION!  terminating a process this way does NOT notify associated DLLs
							// and can muck things up for them.  Unfortunately this was the only way I could
							// find in the documentation
							// ms-help://MS.VSCC.2003/MS.MSDNQTR.2005APR.1033/dllproc/base/terminating_a_process.htm
							TerminateProcess( hProcess, 0 );
							//continue;
						}
					}

					CloseHandle( hProcess );
				}
			}
		}

		void RecursivelyCreateDirectory(const char *path) 
		{
			char current[MAX_PATH];
			const char * doubleBackSlash = "\\";
			char* position = strstr(const_cast<char *>(path), doubleBackSlash);
			while(position)
			{
				strncpy(current, path, (int)(position - path + 1));
				current[(int)(position - path + 1)] = NULL;
				CreateDirectory(current, NULL);
				position = strstr(position + 1, "\\");
				if (position == NULL)
				{
					// Do the last part
					CreateDirectory(path, NULL);
				}
			}
		}

		void Installer::RunFileTalkInstaller(const char* thisVersion)
		{
			string version(thisVersion);

			char path[MAX_PATH];
			SHGetFolderPath(0,CSIDL_LOCAL_APPDATA,NULL,0,path);
			string LocalApplicationPath(path);
			string versionPath(LocalApplicationPath + string("\\Electronic Arts Inc\\GameTalk\\" + version));

			// There is a very small chance that if two instances of this code were run 
			// almost simultaneously then they would not play nice together.  This could
			// happen if the program was set to run on single click and someone double clicked.
			// Maybe try a process-wide mutex (like a flag in the registry) to solve it.

			// Create folder if it doesn't exists
			RecursivelyCreateDirectory(versionPath.c_str());  // if it exists already, who cares?  nothing will change.

			// extract the file from resource 
			string fullGameTalkName(versionPath + string("\\GameTalk.dll"));
			CreateFileFromResource(versionPath.c_str(), fullGameTalkName.c_str(), (char*)bin_gametalk_dll, SIZEOF_BIN_GAMETALK_DLL);

			string fullGameTalkMessagesName(versionPath + string("\\GameTalkMessages.dll"));
			CreateFileFromResource(versionPath.c_str(), fullGameTalkMessagesName.c_str(), (char*)bin_gametalkmessages_dll, SIZEOF_BIN_GAMETALKMESSAGES_DLL);

			string fullFileTalkName(versionPath + string("\\FileTalk.dll"));
			CreateFileFromResource(versionPath.c_str(), fullFileTalkName.c_str(), (char*)bin_filetalk_dll, SIZEOF_BIN_FILETALK_DLL);

			string fullFileTalkMessagesName(versionPath + string("\\FileTalkMessages.dll"));
			CreateFileFromResource(versionPath.c_str(), fullFileTalkMessagesName.c_str(), (char*)bin_filetalkmessages_dll, SIZEOF_BIN_FILETALKMESSAGES_DLL);

			string fullFileTalkInstallerName(versionPath + string("\\FileTalkInstaller.exe"));
			CreateFileFromResource(versionPath.c_str(), fullFileTalkInstallerName.c_str(), (char*)bin_filetalkinstaller_exe, SIZEOF_BIN_FILETALKINSTALLER_EXE);

			string fullWshRuntimeLibraryName(versionPath + string("\\Interop.IWshRuntimeLibrary.dll"));
			CreateFileFromResource(versionPath.c_str(), fullWshRuntimeLibraryName.c_str(), (char*)bin_interop_iwshruntimelibrary_dll, SIZEOF_BIN_INTEROP_IWSHRUNTIMELIBRARY_DLL);

			// change path so sc.exe will work
			_getcwd(path,MAX_PATH);
			_chdir(versionPath.c_str());

			system("FileTalkInstaller.exe");
			_chdir(path);
		}

		void Installer::CreateFileFromResource(const char *path, const char *fileName, const char *resourceData, int resourceSize)
		{
			FILE *f = fopen(fileName, "w+b");
			if (f)
			{
				long num_written = static_cast<long>(fwrite(resourceData, 1, resourceSize, f));
				if (num_written != resourceSize)
				{
					cout << endl;
					cout << "Error: Writing to file [" << fileName << "] ... " << endl;
				}
				fclose(f);
			}
		}
	} // namespace GameTalkInstaller

} // namespace EA
