#if !defined (_SIMPLE_SERVER_HANDLER_H_)
#define _SIMPLE_SERVER_HANDLER_H_

#include "cdevEventHandler.h"
#include "cdevAddr.h"
#include "cdevSocketConnector.h"
#include "cdevReactor.h"
#include "ErrorReporter.h"
#include "SocketUtil.h"
#include "fifo.h"
#include "clipMagicNumber.h"
#include "cdevMessageBinary.h"
#include "cdevMessage.h"
#include "ClientInfo.h"


const  int       _CDEV_CONNECTION_RETRIES = 5;
const  int       MAX_SERVERS = 1024;
extern FifoQueue results        [MAX_SERVERS];
extern int       completionCodes[MAX_SERVERS];

class DumpAllHandler :	public cdevEventHandler, 
			public SocketReader,
			public SocketWriter
{
protected:
	char                        hostName   [MAXHOSTNAMELEN + 1];	
	FifoQueue                   queue;
	cdevSocketConnector         stream; 
	int                         serverQuitFlag;
	cdevReactor               & Reactor;
	int                         index;
	ClientInfo                  info;
			
public:
	DumpAllHandler (char * Host, unsigned short Port, cdevReactor & LocalReactor, int Index)
		: info(),
		  SocketReader(CLIP_MAGIC_NUMBER), 
		  SocketWriter(CLIP_MAGIC_NUMBER),
	  	  index(Index),
	  	  Reactor(LocalReactor), 
	  	  serverQuitFlag(0)
		{
		cdevInetAddr addr;
		hostName[0] = 0;
		addr.set   (Port, Host);
		if(open(addr)!=0)
			{
			outputError(CDEV_SEVERITY_ERROR,
				    "DumpAllHandler::connect",
				    "Failed to connect to %s on port %i",
				     Host, 
				     Port);
			completionCodes[index]=-1;
			}
		
 		completionCodes[index] = 0;
 		
 		cdevData tagMap;
		int    * tags;
 		char  ** ctags;
 		int      ntags;
 		char   * binary    = NULL;
		size_t   binaryLen = 0;
				
 		cdevData::readTagTable(tags, ctags, ntags);
 		tagMap.insert(1, tags, ntags);
 		tagMap.insert(2, ctags, ntags);
 				
 		delete tags;
 		delete ctags;
 
		cdevMessageBinary packet0(index, 0, 0, 0, 0, CDEV_SERVER_OP, 0, 0,
					  NULL, "set ClientInfo", &info.getClientData(),
					  NULL, &tagMap);
					  
		packet0.streamOut(&binary, &binaryLen);
		packet0.detachData();
		enqueue(binary, binaryLen);
		
		cdevMessageBinary packet1(index, 1, 0, 0, 0, CDEV_SERVER_OP, 0, 0, 
					  NULL, "get ServerInfo", NULL, NULL, &tagMap);
		
		packet1.streamOut(&binary, &binaryLen);
		packet1.detachData();
		enqueue(binary, binaryLen);
		
		cdevMessageBinary packet2(index, 2, 0, 0, 0, CDEV_SERVER_OP, 0, 0, 
					  NULL, "get ClientInfo", NULL, NULL, &tagMap);
		packet2.streamOut(&binary, &binaryLen);
		packet2.detachData();
		enqueue(binary, binaryLen);
		}

	~DumpAllHandler (void)
		{
		if(completionCodes[index]!=1) completionCodes[index]=-1;
		if(stream.getHandle()>0 && serverQuitFlag==0)
			{
			writeGoodbye();
			}
			
		if(reactor) reactor->extractHandler(this);
		handleClose();
		}
		
	int open (const cdevAddr &addr)
		{
		int result = 0;
		int count  = 0;

		result = stream.connect (addr);

		if (result != -1)
			{
			if((result = Reactor.registerHandler(this, READ_MASK|WRITE_MASK|EXCEPT_MASK))==-1)
				{
				handleClose();
				}		
			}
		return result;
		}

	char * 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;
		}

	char * getName (void) const 
		{ 
		return "DumpAllHandler";
		}
	
	int getHandle (void) const 
		{ 
		return stream.getHandle(); 
		}
		
	void enqueue (char * buf, size_t  len) 
		{ 
		queue.enqueue(buf, len); 
		setMask(WRITE_MASK|EXCEPT_MASK); 
		} 
	
	void undequeue (char * buf, size_t  len) 
		{ 
		queue.undequeue(buf, len); 
		}
	
	int dequeue (char **buf, size_t *len) 
		{ 
		return queue.dequeue(buf, len); 
		}
	
	int empty (void)
		{ 
		return queue.empty(); 
		}

	int handleClose (void) 
		{
		stream.close(); 
		return 0;
		}
		
	int handleInput (void)
		{
		int        retval = 0;
		char     * buf    = NULL;
		size_t     len    = 0;
		int        result = 1;

		while(result>0)	
			{
			result = read(&buf, (int *)&len);
		
			switch(result)
				{
				case SHUTDOWN_CODE:
				outputError(CDEV_SEVERITY_INFO, (char *)getName(),
					    "Connection to %s terminated by client", getHostName());      
				retval = -1;
				break;
			
				case -1:
				outputError(CDEV_SEVERITY_WARN, (char *)getName(),
					    "Error reading from connection to client %s", getHostName());      
				retval = -1;
				break;
			
				case 0:
				break;
				
				default:
					{
					cdevMessage *message = new cdevMessage;
					unsigned transIndex = 0;
					unsigned clientID   = 0;
			
					if(message->streamIn(buf, len)==0      &&
					   (clientID=message->getClientID())==index &&
		   			   (transIndex=message->getTransIndex())>0 &&
					    transIndex<3)
						{
						results[index].enqueue((void *)message);
						if(transIndex==2) completionCodes[index] = 1;
						}
					else delete message;		
					}
				break;		
       				}
			}

		return retval;
		}

 	int handleOutput (void)
		{
		int                retval = 0;
		char             * buf    = NULL;
		size_t             len    = 0;

		if(!writing() && dequeue(&buf, &len)==0)
			{
			int full = 0;
			while(!full)
				{
				if(!(full = writeEnqueue(buf, len)))
					{
					delete buf;
					full = dequeue(&buf, &len);
					}
				else undequeue(buf, len);
				}
			if(writeContinue()<0) retval = -1;
			}
		else if(writing())
			{
			if(writeContinue()<0) retval = -1;
			}

		if(retval!=0) 
			{
			outputError(CDEV_SEVERITY_WARN, (char *)getName(),
				    "Error transmitting to %s", getHostName());
			}

		if(empty() && !writing()) 
			{
			setMask(READ_MASK|EXCEPT_MASK);
			}
	
		return retval;
		}
	
	int handleExcept (void)
		{
		stream.close();
		serverQuitFlag=1;
		return -1;
		}
};


#endif /* _SIMPLE_SERVER_HANDLER_H_ */ 
