////
// GameTalkCPP v1.0 17mar2005 dan_chitan
//
////
#include "GameTalkCPP.hpp"
#include <sys/fcntl.h>
#include <sched.h>
#include <ppc_intrinsics.h>

#define TEST_AND_FAIL(cond, message) if (cond) { perror(message); exit(1); }


static int	GAMETALK_PORT = 9876; //@dan todo: read this from registry	
namespace EA
{
	//
	//
	namespace GameTalk
	{
		void* GameTalkThread_Run (void* param)
		{
			Manager* mgr = static_cast<Manager*>(param);
			mgr->ReceiveThread();
			return(NULL);
		}
		
		//
		// global object
		Manager theGameTalkManager;


		Manager::Manager()
		{
			mConnection		= NULL;
			mPort			= GAMETALK_PORT;
			mName.clear();
			mDone			= false;
			mInited			= false;
			mNumMessages	= 0;
			mMsgHandler		= NULL;
		}

		Manager::~Manager()
		{
			if (mInited)
			{
				delete mConnection;
				pthread_join(mThread, NULL);

				if (mNumMessages > 0)
				{
					pthread_mutex_lock(&mMutex);
					for (unsigned i = 0; i < mNumMessages; ++i)
						delete mMessages[i];
					pthread_mutex_unlock(&mMutex);
				}
			
				pthread_mutex_destroy(&mMutex);
				mInited = false;
			}
		}

		void Manager::Initialize (const std::string &toolName, const std::string& gtServerName)
		{
			//
			// current name of the tool
			mName = toolName;

			//
			// get client's information (assume neither the name nor the address is given)
			HostInfo		clientInfo;
			std::string		clientName		= clientInfo.getHostName();
			std::string		clientIPAddress = clientInfo.getHostIPAddress();
			std::cout << "Name: " << clientName << std::endl;
			std::cout << "Address: " << clientIPAddress << std::endl;

			//
			// get server's information - local host always
			HostInfo		serverInfo(gtServerName, NAME);
			std::string		serverName		= serverInfo.getHostName();
			std::string		serverIPAddress	= serverInfo.getHostIPAddress();
			std::cout << "Name: " << serverName << std::endl;
			std::cout << "Address: " << serverIPAddress << std::endl;

			//
			// create the socket for client
			mConnection = new Socket(mPort);	
		
			//
			// connect to the server.
			std::cout   << "connecting to the server [" << serverName << "] ... " << std::endl;
			mConnection->ConnectToServer(serverIPAddress, ADDRESS);
		
			//
			// create the thread to listen to receive network event
			CreateReceiveThread();

			//
			// send the initialize packet
			SendInitializePacket();
			
			mInited = true;
		}

		void Manager::Shutdown()
		{
			mDone = true;

			if( mConnection )
			{
				delete mConnection;
				mConnection = NULL;
			}
		}

		int Manager::SendInitializePacket()
		{
			// Send the initialize message with the target name identification
			// Total Length
			// "initialize" length
			// "initialize" std::string
			// "target name" length
			// "target name" std::string

			const char	*const initString	= "initialize";
			const int	initStringLength	= strlen(initString);
			const int	targetStringLength	= mName.size();
			const int	bufferSize			= 4 + 4 + initStringLength + 4 + targetStringLength;

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

			__stwbrx(bufferSize, outBuffer, 0);
			__stwbrx(initStringLength, outBuffer, 4);
			outBuffer += 8;
			
			//
			// Set the initialize std::string
			strncpy(outBuffer, initString, initStringLength);
			outBuffer += initStringLength;
			
			//
			// Set the initialize length
			__stwbrx(targetStringLength, outBuffer, 0);
			outBuffer += 4;

			//
			// Set the initialize std::string
			strncpy(outBuffer, mName.c_str(), targetStringLength);

			if( mConnection )
				mConnection->SendMessage( buffer, bufferSize );

			delete []buffer;
			return 1;
		}

		int Manager::Send( const std::string &target, Message &message )
		{
			// Format:
			//[Length of entire message] 
			//[Length of target name]
			//[Target name (game.ps2)]
			//[Length of message]
			//[message (channel id and key/value pair)]

			char	*myMessage		= NULL;
			int		myMessageSize	= 0;
			message.GetBuffer( &myMessage, &myMessageSize );

			// compute the length of the entire message
			int 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 )
			{
				memset( finalMessage, 0, finalMessageSize * sizeof(char) );
				__stwbrx(finalMessageSize, finalMessage, 0);						// length of whole packet
				__stwbrx(target.length(), finalMessage, 4);						// length of target
				memcpy(finalMessage+8, target.c_str(), target.length());
				__stwbrx(myMessageSize, finalMessage, 8+target.length());		// length of message
				memcpy(finalMessage+8+target.length(), myMessage, myMessageSize);

				if( mConnection )
					mConnection->SendMessage( finalMessage, finalMessageSize );
			}
			// cleanup the temp buffer
			delete [] finalMessage;

			return 1;
		}
		
		void Manager::HandleMessageArrival()
		{
			Message* msg = new Message;
			int recvBytes = 0;
			
			memset(msg->mBuffer, 0, MAX_RECV_LEN );
			recvBytes = mConnection->ReceiveMessage(msg->mBuffer, MAX_RECV_LEN);
			msg->mBufferSize = recvBytes;
			if(recvBytes > 0 )
			{
				msg->AssembleFromBuffer();
				if ((msg->MessageId.compare("PING") != 0) && (mNumMessages < MAX_MSG_QUEUE))
				{
					pthread_mutex_lock(&mMutex);
					
					mMessages[mNumMessages++] = msg;
					msg = NULL;
					
					pthread_mutex_unlock(&mMutex);
				}
			}
			
			delete msg;
		}

		void Manager::Receive ()
		{
			if ((mMsgHandler != NULL) && (mNumMessages > 0)) 
			{
				pthread_mutex_lock(&mMutex);
				
				for (unsigned i = 0; i < mNumMessages; ++i)
				{
					Message* msg = mMessages[i];
					mMessages[i] = NULL;
					
					mMsgHandler(msg);
					delete msg;
				}
				mNumMessages = 0;

				pthread_mutex_unlock(&mMutex);
			}
		}

		int Manager::CreateReceiveThread()
		{
			// create mutex for common data structures
			pthread_mutexattr_t mattr;
			pthread_mutexattr_init(&mattr);
			pthread_mutexattr_settype(&mattr, PTHREAD_MUTEX_RECURSIVE);
			pthread_mutex_init(&mMutex, &mattr);
			pthread_mutexattr_destroy(&mattr);
			
			//
			// create a thread to handle the network events
			pthread_attr_t attr;
			int thread_policy;
			struct sched_param thread_param;
			
			int minprio = sched_get_priority_min(SCHED_FIFO);
			int maxprio = sched_get_priority_max(SCHED_FIFO);
			size_t stacksize = 0;
			
			pthread_attr_init(&attr);
			pthread_attr_getschedpolicy(&attr, &thread_policy);
			pthread_attr_getschedparam(&attr, &thread_param);
			pthread_attr_getstacksize(&attr, &stacksize);
			
			thread_param.sched_priority += 1;
			pthread_attr_setschedparam(&attr, &thread_param);
			pthread_attr_setschedpolicy(&attr, SCHED_FIFO);
			pthread_attr_setinheritsched(&attr, PTHREAD_EXPLICIT_SCHED);
			
			int err = pthread_create(&mThread, &attr, GameTalkThread_Run, this);
			TEST_AND_FAIL(err != 0, "failed pthread_create");
			
			pthread_attr_destroy(&attr);
			return 1;
		}
		
		void Manager::ReceiveThread()
		{
			while( !mDone )
			{
				struct timeval timeout;
				timeout.tv_sec = 0;
				timeout.tv_usec = (1000*1000)*1/20;	// 1/20th of a second
				int status = mConnection->Select(timeout);

				if (status < 0)
					mDone = true;
				else if (status > 0)
					theGameTalkManager.HandleMessageArrival();
			}
		}
		
		int Manager::RegisterReceiveMessageHandler( GameTalkMessageHandlerFnc *handlerFnc )
		{
			mMsgHandler = handlerFnc;
			return(1);
		}

		int Manager::UnRegisterReceiveMessageHandler( GameTalkMessageHandlerFnc *handlerFnc )
		{
			mMsgHandler = NULL;
			return(1);
		}

		//
		//
		Message::Message( const std::string &messageId )
		{
			MessageId	= messageId;
			ClearKeyContent();
			mBufferSize = 0;
		}

		Message::Message()
		{
			MessageId.empty();
			ClearKeyContent();
			mBufferSize = 0;
		}

		Message::~Message()
		{
			MessageId	= "";
			ClearKeyContent();
			mBufferSize = 0;
		}

		int Message::NumberOfKeys()
		{
			return mKeysContents.size();
		}
		
		void Message::AddKeyContent( const std::string &key, const std::string &content )
		{
			KeyContent *k	= new KeyContent;
			if( k )
			{
				k->Key			= key;
				k->Content		= content;
				mKeysContents.push_back( k );
			}
		}

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


		int Message::AssembleFromBuffer()
		{
			int retVal = 0;
			
			char	*crt				= mBuffer;
			unsigned MessageId_length	= __lwbrx(crt, 0);		crt += 4;
			MessageId.append( crt, MessageId_length );			crt += MessageId_length;
			unsigned KeysContents_size	= __lwbrx(crt, 0);		crt += 4;
			
			if( KeysContents_size > 0 )
			{
				for( int i=0; i<KeysContents_size; i++ )
				{
					std::string KeyName, KeyValue;
					unsigned KeyName_size = __lwbrx(crt, 0);	crt += 4;
					KeyName.append( crt, KeyName_size );		crt += KeyName_size;
					unsigned KeyValue_size = __lwbrx(crt,0);	crt += 4;
					KeyValue.append( crt, KeyValue_size );		crt += KeyValue_size;

					AddKeyContent( KeyName, KeyValue );
				}
			}
			return retVal;
		}

		char *Message::GetBuffer( char **pBuff, int *pLenBuff )
		{
			mBufferSize	= ComputeBufferSize();
			char *crt	= mBuffer;

			__stwbrx(MessageId.length(), crt, 0);					crt	+= 4;					// message length
			memcpy( crt, MessageId.c_str(), MessageId.length() );	crt += MessageId.length();	// message
			__stwbrx(mKeysContents.size(), crt, 0);					crt += 4;					// number of key/value pairs
			
			for( std::vector<KeyContent *>::iterator it = mKeysContents.begin(); it != mKeysContents.end(); ++it )
			{
				KeyContent *crtKC = *it;
				
				__stwbrx(crtKC->Key.length(), crt, 0);							crt += 4;					// key length
				memcpy( crt, crtKC->Key.c_str(), crtKC->Key.length() );			crt += crtKC->Key.length();	// key
				__stwbrx(crtKC->Content.length(), crt, 0);						crt += 4;						// value length
				memcpy( crt, crtKC->Content.c_str(), crtKC->Content.length() ); crt += crtKC->Content.length();	// value
			}
			
			int bufflen = (crt - mBuffer) * sizeof(char);
			if( bufflen != mBufferSize )
			{ // detected a problem - computed buffer size does not match the actual size

			}
			
			*pBuff		= mBuffer;
			*pLenBuff	= mBufferSize;
			return mBuffer;
		}

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

			return crtBufferSize;
		}

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

			hostPtr = gethostbyname(sName);
			TEST_AND_FAIL(hostPtr == NULL, "failed to gethostbyname");
		}

		HostInfo::HostInfo(const std::string &hostName, hostType type)
		{
			if (type == NAME)
			{	// Retrieve host by name
				hostPtr = gethostbyname(hostName.c_str());
				TEST_AND_FAIL(hostPtr == NULL, "failed to gethostbyname");
			}
			else if (type == ADDRESS)
			{
				// Retrieve host by address
				unsigned long netAddr = inet_addr(hostName.c_str());
				TEST_AND_FAIL(netAddr == -1, "failed inet_addr");

				hostPtr = gethostbyaddr((char *)&netAddr, sizeof(netAddr), AF_INET);
				TEST_AND_FAIL(hostPtr == NULL, "failed gethostbyaddr");
			}
			else
			{
				std::cerr << "unknown host type: host name/address has to be given";
				exit(1);
			}
		}
	
		//
		//
		Log theGameTalkLog;

		Log::Log()
		{
			initVars();
			init();
		} 

		Log::Log(const std::string& fileName)
		{
			initVars();
			init(fileName);
		}

		Log::Log(const std::string& fileName,enum logLevels levelIn)
		{
			initVars();
			logLevel = levelIn;
			init(fileName);
		}

		Log::~Log()
		{
			if(logLevel<QUIET_MODE)
			{
				clear(std::ios::goodbit);
				*this << std::endl;
				printHeader(1);    // add ending time to log file
			}
			close();
		}

		void Log::init(const std::string& fileName)
		{
			if( (fileName.c_str())[0] )
				openLog(fileName,LOG_WRITE);
			else
				openLog("syslog.log",LOG_WRITE);
		}

		void Log::init(const std::string& fileName, std::ios_base::openmode mode)
		{
			if( (fileName.c_str())[0] )
				openLog(fileName,mode);
			else
				openLog("syslog.log", mode);
		}

		void Log::init()
		{
			openLog("syslog.log",LOG_WRITE);
		}

		void Log::openLog(const std::string& fileName, std::ios_base::openmode mode)
		{
			if(logLevel < QUIET_MODE)
			{
				this->open(fileName.c_str(), mode);
			      
				if( fail() == 0 )
				{
					logName = fileName;
					printHeader(0);         // insert start time into top of log file
				}
				else
				{
					std::cout << "ERROR: Log file " << fileName.c_str() 
						<< " could not be opened for write access." << std::endl;
					logLevel = QUIET_MODE;
				}
			}
			else
			{
				std::cout << "Logging disabled (QUIET_MODE set)" << std::endl;
			}
		}

		void Log::initVars()
		{
			time(&startTime);
			logLevel = DEFAULT_LOG_LEVEL;
		}

		void Log::printHeader(int theEnd)
		{
			if( logLevel < QUIET_MODE )
			{
				clear(std::ios::goodbit);

				// setup time
				time_t sttime;
				time(&sttime);

				// convert to gm time
				struct tm *tim = gmtime(&sttime);

				// set data items
				int sec  = tim->tm_sec;           // second (0-61, allows for leap seconds)
				int min  = tim->tm_min;           // minute (0-59)
				int hour = tim->tm_hour;          // hour (0-23)

				int mon  = tim->tm_mon + 1;       // month (0-11)
				int mday = tim->tm_mday;          // day of the month (1-31)
				int year = tim->tm_year % 100;    // years since 1900

				char cur_time[9];
				char cur_date[9];

				sprintf(cur_time,"%02d:%02d:%02d",hour,min,sec);
				sprintf(cur_date,"%02d/%02d/%02d",mon,mday,year);

				char line_2[61];
				sprintf(line_2,"DATE: %s - %s%30s",cur_date,cur_time,logName.c_str());
				*this << line_2 << std::endl << std::endl;

				if (theEnd) *this << getExecTime() << std::endl;
			}   
		} 

		void Log::getExecTime(int* min, int* sec)
		{
			time_t endTime;
			time(&endTime);
			*min = (int)((endTime - startTime)/60);
			*sec = (int)((endTime - startTime)%60);
		} 

		char *Log::getExecTime()
		{
			int min = 0;
			int sec = 0;
			getExecTime(&min, &sec);
			static char execTime[128];
			sprintf(execTime, "Execution time: %d minutes %d seconds", min, sec);
			return execTime;
		}

		//
		//
		const int MSG_HEADER_LEN = 4;

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

			socketId = socket(AF_INET,SOCK_STREAM,0);
			TEST_AND_FAIL(socketId == -1, "failed to create socket");

			clientAddr.sin_family		= AF_INET;
			clientAddr.sin_addr.s_addr	= htonl(INADDR_ANY);
			clientAddr.sin_port			= htons(portNumber);
		}
		    
		void Socket::SetDebug(int debugToggle)
		{
			int err = setsockopt(socketId,SOL_SOCKET,SO_DEBUG,(char *)&debugToggle,sizeof(debugToggle));
			TEST_AND_FAIL(err == -1, "failed setsockopt");
		}

		void Socket::SetReuseAddr(int reuseToggle)
		{
			int err = setsockopt(socketId,SOL_SOCKET,SO_REUSEADDR,(char *)&reuseToggle,sizeof(reuseToggle));
			TEST_AND_FAIL(err == -1, "failed setsockopt");
		} 

		void Socket::SetKeepAlive(int aliveToggle)
		{
			int err = setsockopt(socketId,SOL_SOCKET,SO_KEEPALIVE,(char *)&aliveToggle,sizeof(aliveToggle));
			TEST_AND_FAIL(err == -1, "failed setsockopt");
		} 

		void Socket::SetLingerSeconds(int seconds)
		{
			struct linger lingerOption;
			
			if( seconds > 0 )
			{
				lingerOption.l_linger = seconds;
				lingerOption.l_onoff = 1;
			}
			else 
			{
				lingerOption.l_onoff = 0;
			}
			 
			int err = setsockopt(socketId,SOL_SOCKET,SO_LINGER,(char *)&lingerOption,sizeof(struct linger));
			TEST_AND_FAIL(err == -1, "failed setsockopt");
		}

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

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

			int err = setsockopt(socketId,SOL_SOCKET,SO_LINGER,(char *)&lingerOption,sizeof(struct linger));
			TEST_AND_FAIL(err == -1, "failed setsockopt");
		}

		void Socket::SetSendBufSize(int sendBufSize)
		{
			int err = setsockopt(socketId,SOL_SOCKET,SO_SNDBUF,(char *)&sendBufSize,sizeof(sendBufSize));
			TEST_AND_FAIL(err == -1, "failed setsockopt");
		} 

		void Socket::SetReceiveBufSize(int receiveBufSize)
		{
			int err = setsockopt(socketId,SOL_SOCKET,SO_RCVBUF,(char *)&receiveBufSize,sizeof(receiveBufSize));
			TEST_AND_FAIL(err == -1, "failed setsockopt");
		}

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

			int err = fcntl(socketId, F_SETFL, O_NONBLOCK);
			TEST_AND_FAIL(err == -1, "failed fcntl");
		}

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

			int err = getsockopt(socketId,SOL_SOCKET,SO_DEBUG,(char *)&Option,&OptionLen);
			TEST_AND_FAIL(err == -1, "failed getsockopt");
			return Option;
		}

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

			int err = getsockopt(socketId,SOL_SOCKET,SO_REUSEADDR,(char *)&Option,&OptionLen);
			TEST_AND_FAIL(err == -1, "failed getsockopt");
			return Option;
		}

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

			int err = getsockopt(socketId,SOL_SOCKET,SO_KEEPALIVE,(char *)&Option,&OptionLen);
			TEST_AND_FAIL(err == -1, "failed getsockopt");
			return Option;    
		}

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

			int err = getsockopt(socketId,SOL_SOCKET,SO_LINGER,(char *)&lingerOption,&OptionLen);
			TEST_AND_FAIL(err == -1, "failed getsockopt");
			return lingerOption.l_linger;
		}

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

			int err = getsockopt(socketId,SOL_SOCKET,SO_LINGER,(char *)&lingerOption,&OptionLen);
			TEST_AND_FAIL(err == -1, "failed getsockopt");

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

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

			int err = getsockopt(socketId, SOL_SOCKET, SO_SNDBUF, (char *)&sendBuf, &OptionLen);
			TEST_AND_FAIL(err == -1, "failed getsockopt");
			return sendBuf;
		}    

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

			int err = getsockopt(socketId, SOL_SOCKET,SO_RCVBUF, (char *)&rcvBuf, &OptionLen);
			TEST_AND_FAIL(err == -1, "failed getsockopt");
			return rcvBuf;
		}

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

			io << std::endl;
			io << "Summary of socket settings:" << std::endl;
			io << "   Socket Id:     " << s.GetSocketId() << std::endl;
			io << "   port #:        " << s.GetPortNumber() << std::endl;
			io << "   debug:         " << (flagStr = s.GetDebug()? "true":"false" ) << std::endl;
			io << "   reuse addr:    " << (flagStr = s.GetReuseAddr()? "true":"false" ) << std::endl;
			io << "   keep alive:    " << (flagStr = s.GetKeepAlive()? "true":"false" ) << std::endl;
			io << "   send buf size: " << s.GetSendBufSize() << std::endl;
			io << "   recv bug size: " << s.GetReceiveBufSize() << std::endl;
			io << "   blocking:      " << (flagStr = s.GetSocketBlocking()? "true":"false" ) << std::endl;
			io << "   linger on:     " << (flagStr = s.GetLingerOnOff()? "true":"false" ) << std::endl;
			io << "   linger seconds: " << s.GetLingerSeconds() << std::endl;
			io << std::endl;
			return io;
		}

		void Socket::BindSocket()
		{
			int err = bind(socketId, (struct sockaddr *)&clientAddr, sizeof(struct sockaddr_in));
			TEST_AND_FAIL(err == -1, "failed bind");
		}

		void Socket::ConnectToServer(std::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);

			// Connect...
			int err = connect(socketId,(struct sockaddr *)&serverAddress, sizeof(serverAddress));
			TEST_AND_FAIL(err == -1, "failed connect");
		}

		Socket *Socket::AcceptClient( std::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
			newSocket = accept(socketId, (struct sockaddr *)&clientAddress, &clientAddressLen);
			TEST_AND_FAIL(newSocket == -1, "failed accept");
		    
			// Get the host name given the address
			std::string	sAddress = inet_ntoa((struct in_addr)clientAddress.sin_addr);
			char		*hostName = HostInfo(sAddress, ADDRESS).getHostName();
			clientHost += std::string(hostName);
			
			// Create and return the new myTcpSocket object
			Socket *retSocket = new Socket();
			retSocket->setSocketId(newSocket);

			return retSocket;
		}

		void Socket::ListenToClient(int totalNumPorts)
		{
			int err = listen(socketId,totalNumPorts);
			TEST_AND_FAIL(err == -1, "failed listen");
		}

		//
		// Note:
		//	I'm using this only in my tests
		int Socket::SendMessage(std::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());
			std::string sendMsg = std::string(msgLength);
			sendMsg += message;
		#else
			// just send the message - the layers above will take care of message format
			std::string sendMsg = message;
		#endif
			// Sends the message to the connected host
			numBytes = send(socketId, sendMsg.c_str(), sendMsg.size(), 0);
			TEST_AND_FAIL(numBytes == -1, "failed send");

			return numBytes;
		}

		int Socket::SendMessage(char *sendMsg, int size )
		{
			int numBytes;  // the number of bytes sent

			// Sends the message to the connected host
			numBytes = send(socketId, sendMsg, size, 0);
			TEST_AND_FAIL(numBytes == -1, "failed send");
			return numBytes;

		}

		int Socket::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));

			while( received < msgSize )
			{
				numBytes = recv( socketId, charMsg+received, 1, 0 );
				if(numBytes == -1)
				{
					int errorcode = errno;
					if (errorcode == ENOTCONN)
						return(0);
					else
					{
						TEST_AND_FAIL(false, "failed recv");
					}
				}
				
				if( !headerFinished )
				{
					msgLength[received] = *(charMsg+received);
					received++;

					if( received == MSG_HEADER_LEN )
					{
						headerFinished	= true;
						received		= 0;
						memset( charMsg, 0, sizeof(charMsg) );
						msgSize = __lwbrx(msgLength, 0);
					}
				}
				else
				{
					received++;
				}
			}

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

			return msgSize;
		}

		int Socket::Select (struct timeval& timeout)
		{
			fd_set readfds, exceptfds;
			FD_ZERO(&readfds);
			FD_ZERO(&exceptfds);
			FD_SET(socketId, &readfds);
			FD_SET(socketId, &exceptfds);
			
			int num = select(socketId+1, &readfds, NULL, &exceptfds, /*&timeout*/ NULL);
			if (num == 0)
				return(0);
			else if (num == -1)
				return((errno == EBADF) ? -1 : 0);		// shutdown if descriptor is invalid, otherwise ignore the error
			else if (FD_ISSET(socketId, &exceptfds))
			{
				int optval, optlen = sizeof(int);
				int err = getsockopt(socketId, SOL_SOCKET, SO_ERROR, &optval, &optlen);
				if (err == -1)
					return(-1);
				else
					return(0);	// not sure how to interpret socket errors, so just try again -- will fail if socket is now invalid
			}
			else if (FD_ISSET(socketId, &readfds))
				return(1);
			else
				return(0);
		}

	} // namespace GameTalk

} // namespace EA
