#include "ClientHandler.h"
#include "ClientAcceptor.h"
#include <clipMagicNumber.h>

// *****************************************************************************
// * ClientHandler:
// *	Default constructor for the ClientHandler class
// *****************************************************************************
ClientHandler::ClientHandler (cdevSessionManager & s) 
	: server(s), queue(NULL), clientQuitFlag(0),
	  SocketReader(CLIP_MAGIC_NUMBER), SocketWriter(CLIP_MAGIC_NUMBER),
	  packetsSent(0), packetsRecv(0)
	{
	hostName[0] = 0;
	}

// *****************************************************************************
// * ClientHandler::getHostName:
// *	This function returns the name of the remote host.
// *****************************************************************************
char * ClientHandler::getHostName( void )
	{
	if(*hostName==0)
		{
		cdevInetAddr addr;		
		if(stream.getRemoteAddress (addr)==0)
			{
			const char * ptr=addr.getHostName();
			if(ptr) strncpy (hostName, ptr, MAXHOSTNAMELEN+1);
			}
		}
	return hostName;
	}
			  
// *****************************************************************************
// * open:
// *	Initializes the new socket and adds this ClientHandler class to 
// *	the reactor
// *****************************************************************************
int ClientHandler::open  (class ClientAcceptor * ) 
	{
	cdevInetAddr addr;
	int result = 0;
		
	if (stream.getRemoteAddress (addr) == -1) 
		{
		outputError(CDEV_SEVERITY_SEVERE, (char *)getName(),
			    "Couldn't get local address");
		result = -1;
		}
	else
		{
		if (server.Reactor.registerHandler (this, READ_MASK|WRITE_MASK|EXCEPT_MASK)!=0)
			{
			outputError(CDEV_SEVERITY_SEVERE, (char *)getName(),
				    "Cannot register handler with reactor");
			result = -1;
			}
		else	{
			// *****************************************************
			// * Get the socket number and use it to identify the
			// * socket within the cdevSessionManager.
			// *****************************************************
			int handle = getHandle();
			if((queue = server.findSocket(handle))!=NULL)
				{
				server.removeSocket(handle);
				}
			queue = server.addSocket(handle);

			outputError(CDEV_SEVERITY_INFO, (char *)getName(),
				    "Establishing connection to %s on socket %i", 
				    getHostName(), handle);
			}
		}
	return result;
	}		

// *****************************************************************************
// * getHandle:
// *	Returns the device descriptor for the underlying socket
// *****************************************************************************
int ClientHandler::getHandle (void) const 	
	{
	return stream.getHandle ();
	}

// *****************************************************************************
// * ~ClientHandler:
// *	Destructor for the ClientHandler object
// *****************************************************************************
ClientHandler::~ClientHandler (void) 
	{
	if(stream.getHandle()>0 && clientQuitFlag==0) writeGoodbye();
	if(reactor) reactor->extractHandler(this);
	handleClose();	
	} 

// *****************************************************************************
// * handleClose:
// *	Removes the object from the reactor class and then deletes it.
// *****************************************************************************
int ClientHandler::handleClose (void)
	{
	outputError(CDEV_SEVERITY_INFO, (char *)getName(),
		    "Terminating connection to %s on socket %i", 
		    getHostName(), queue?queue->getSocketID():0);

	stream.close();
	if(queue) 
		{
		server.removeSocket(queue->getSocketID());
		queue = NULL;
		}
	return 0;
	}


// *****************************************************************************
// # handleInput :
// #	This function is called when data is ready to be read from a connected
// #	socket.  This function will read the data from the socket, and will then
// #	submit the buffer to the processIncomingPacket function for processing.
// *****************************************************************************
int ClientHandler::handleInput (void)
{
int        retval = 0;
char     * buf    = NULL;
size_t     len    = 0;
int        result = 1;

// *****************************************************************************
// * Record oriented semantics dictate that the length of the transmission
// * always preceeds the actual data.  Therefore, read the length of the
// * transmission into the len variable.
// *****************************************************************************
while(result>0)	
	{
	if(buf==NULL) result = read(&buf, (int *)&len);
	else          result = readNextPacket(&buf, (int *)&len);
	switch(result)
		{
		// *************************************************************
		// * A return value of SHUTDOWN_CODE indicates that a negative 
		// * one was provided as the packet length - indicating a 
		// * shutdown...
		// *************************************************************
		case SHUTDOWN_CODE:
		outputError(CDEV_SEVERITY_INFO, (char *)getName(),
			    "Connection to %s terminated by client", getHostName());      
		retval = -1;
		break;
	
		// *************************************************************
		// * A return value of -1 indicates an error occured while 
		// * reading from the socket.  A value of -1 is returned to 
		// * shutdown the socket and remove it from the reactor.
		// *************************************************************
		case -1:
		outputError(CDEV_SEVERITY_WARN, (char *)getName(),
			    "Error reading from connection to client %s", getHostName());      
		retval = -1;
		break;
	
		// *************************************************************
		// * A return value of 0 means that no data was ready to be 
		// * retrieved from the socket.
		// *************************************************************
		case 0:
		break;
		
		// *************************************************************
		// * Any other value returned from the socket represents the 
		// * number of bytes actually read into the local buffer object.
		// *************************************************************
		default:
		server.enqueue(getHandle(), buf, len);
		packetsSent++;
		break;		
       		}
	}

// *****************************************************************************
// * Return the value specified by retval.
// *****************************************************************************
return retval;
}


// *****************************************************************************
// # handleOutput :
// #	This function is called when data is ready to be transmitted to a 
// #	connected socket.  This function will read the data from the queue, 
// #	translate it to XDR, and then transmit it to the client.
// *****************************************************************************
int ClientHandler::handleOutput    (void)
{
int        retval   = 0;
char     * buf      = NULL;
size_t     len      = 0;

// *****************************************************************************
// * Attempt to transmit or continue transmitting the data.  Note, the peek
// * method is used to attempt to writeEnqueue a data item without actually
// * removing it from the outbound queue.  If the item can be writeEnqueued, 
// * then the dequeue method is called to remove it from the queue...
// * This method is repeated until the output buffer is fully populated.
// *****************************************************************************
if(!writing() && queue!=NULL && queue->peek(&buf, &len)==0)
	{
	int full = 0;
	while(!full)
		{
		full = writeEnqueue(buf, len);
		if(!full)
			{
			queue->dequeue(&buf, &len);
			delete buf;
			buf = NULL;
			len = 0;
			if(queue->peek(&buf, &len)!=0) full=-1;
			packetsRecv++;
			}
		}
	if(writeContinue()<0) retval = -1;
	}
else if(writing())
	{
	if(writeContinue()<0) retval = -1;
	}

// *****************************************************************************
// * Display an error message if the transmission failed.
// *****************************************************************************
if(retval!=0) 
	{
	outputError(CDEV_SEVERITY_WARN, (char *)getName(),
		    "Error transmitting to %s", getHostName());
	}

// *****************************************************************************
// * If there are no further messages in the outbound queue and the
// * ACE_Event_Handler has finished servicing the current message, turn off the
// * WRITE_MASK for this ACE_Event_Handler.
// *****************************************************************************
if(retval!=0 || ((queue==NULL || queue->empty()) && !writing())) 
	{
	setMask(READ_MASK|EXCEPT_MASK);
	}
	
// *****************************************************************************
// * Return the value specified by retval.
// *****************************************************************************
return retval;
}


// *****************************************************************************
// * ClientHandler::handleExcept :
// *	This function is called when out of band data is ready to be received
// *	from the socket.
// *****************************************************************************
int ClientHandler::handleExcept(void)
	{
	clientQuitFlag=1; 
	return -1; 
	}

