#include <CLIPService.h>
#include <CLIPRequestObject.h>
#include <cdevMessage.h>

// *****************************************************************************
// * newCLIPService:
// *	This function will be called by the cdevSystem object to create an 
// *	initial instance of the CLIPService.
// *****************************************************************************
extern "C" cdevService * newCLIPService (char * name, cdevSystem * system)
	{
	return new CLIPService(name, *system);
	}

void NULLError (int, char *, cdevRequestObject *)
	{
	}
	
// *****************************************************************************
// * CLIPService::CLIPService :
// *	This is the constructor for the CLIPService.  It initializes the
// *	underlying CLIPService by specifying that it is in the domain of
// *	VIRTUAL.
// *****************************************************************************
CLIPService::CLIPService ( char * name, cdevSystem & system)
	: cdevClientService("CLIP", name, system) 
	{
	system.reportError(CDEV_SEVERITY_INFO, "CLIPService", NULL, 
			   "Constructing a new CLIPService"); 
	
	}

// *****************************************************************************
// * CLIPService::getRequestObject :
// *	This is the interface that cdev objects will use to obtain a
// *	CLIPRequestObject object.  The CLIPRequestObject 
// *	represents a combined device and message pair that is associated 
// *	with the CLIPService.
// *
// *	Returns CDEV_SUCCESS on success or CDEV_ERROR on error.
// *****************************************************************************
int CLIPService::getRequestObject ( char * device, char * message, cdevRequestObject * &req)
	{
	req = new CLIPRequestObject (device, message, system_);
	return (req ? CDEV_SUCCESS : CDEV_ERROR);
	}


// *****************************************************************************
// * CLIPService::connect :
// *	This mechanism will establish a new connection with a server and will
// *	will return a pointer to the ServerHandler.  If the host and port are
// *	specified in the arguments, then the method will attempt to connect 
// *	using those values without going through the CDEV Name Server.
// * 
// *	Note:  This method will insert the ServerHandler into the connections
// *	object and will install it in the Reactor.
// *****************************************************************************
ServerHandler * CLIPService::connect ( char * server, char * host, short port )
	{
	ServerHandler  * handler  = NULL;

	// *********************************************************************
	// * First attempt to locate the handler from the existing connections.
	// *********************************************************************
	if(server && *server && (handler = connections.find(server))==NULL)
		{
		int                 errCode = CDEV_SUCCESS;
		char                serverHost[255];
		short               serverPort;
		int                 serverStatus;
		int                 hostMatchesNS;

		// *************************************************************
		// * Initialize these values to reflect a server that is not 
		// * defined within the name server.
		// *************************************************************
		*serverHost   = 0;
		serverPort    = 0;
		serverStatus  = 2;
		hostMatchesNS = 0;

		// *************************************************************
		// * Raise the error threshold to reduce noise while the service
		// * tries to resolve the location of the server.
		// *************************************************************
		system_.setThreshold(100);
		
		// *************************************************************
		// * In this implementation we will always attempt to resolve
		// * the location of the server using the name server first, to
		// * determine if monitoring can be installed later...
		// *************************************************************
		cdevData            input;
		cdevData            output;
		cdevRequestObject & rnsReq = 
			cdevRequestObject::attachRef("NameServer", "get");

		// *****************************************************
		// * Insert domain and name in the input request object 
		// * to be sent to the CDEV Name Server.
		// *****************************************************
		input.insert("name", server);
		input.insert("domain", domain);
		
		// *****************************************************
		// * Query the Name Server to obtain the hostname and 
		// * port of the specified domain and server.
		// *****************************************************
		if(rnsReq.send(input, output)==CDEV_SUCCESS)
			{
			output.get("host", serverHost, 255);
		   	output.get("port", &serverPort);
		   	output.get("status", &serverStatus);
			}
		
		// *************************************************************
		// * Restore the error threshold to a reasonable level.
		// *************************************************************
		system_.setThreshold(CDEV_SEVERITY_ERROR);

		// *************************************************************
		// * If the host and port have been specified, then use them
		// * rather than going through the CDEV Name Server.
		// *************************************************************
		if(host!=NULL && *host && port>0)
			{
			if(strcmp(serverHost, host) && serverPort==port)
				hostMatchesNS = 1;

			strcpy(serverHost, host);
			serverPort = port;
			}
		else	{
			if(*serverHost == 0 || serverPort<=0 || serverStatus!=0)
				{
				errCode = CDEV_ERROR;
				
				// *********************************************
				// * If the hostname could not be found, report 
				// * the error and set the return value to 
				// * CDEV_ERROR.
				// *********************************************
				outputError(CDEV_SEVERITY_ERROR, 
				            "CLIPService::connect",
					    "Cannot find host for server \"%s\" in domain \"%s\"",
					    server, domain);
				}
			else hostMatchesNS = 1;
			}

		// *************************************************************
		// * If the hostname was discovered, then the errCode should be
		// * CDEV_SUCCESS.  Attempt to connect to the server using the
		// * cdevReactor mechanisms.
		// *************************************************************
		if(errCode==CDEV_SUCCESS)
			{
			cdevInetAddr addr;
			
			addr.set   (serverPort, serverHost);

			handler = new ServerHandler(server, this);

			if(handler->open(addr)!=0)
				{
				outputError(CDEV_SEVERITY_ERROR,
					    "CLIPService::connect",
					    "Failed to connect to %s on port %i",
					    serverHost, 
					    serverPort);
				delete handler;
				handler = NULL;
				}
			else	{
				outputError(CDEV_SEVERITY_INFO,
					    "CLIPService::connect",
					    "Connected to %s on port %i",
					    serverHost, 
					    serverPort);
				connections.insert(handler);
				
				// *********************************************
				// * This calls the registerFd method of the 
				// * service which will cause the FD Changed 
				// * callbacks to be triggered.
				// *********************************************
				registerFd(handler->getHandle(), 1);
				
				// *********************************************
				// * Install a Name Server monitor to maintain
				// * the status of the connection.
				// * Note:  If a monitor has already been 
				// * installed using this informations, then the
				// * CDEV Name Server service should not create 
				// * a second monitor.
				// *
				// * Note: I am using the FifoQueue associated
				// * with the specific server in order to 
				// * differentiate between the various servers.
				// *********************************************
				if(hostMatchesNS)
					{
					cdevData            monData;
					cdevCallback        monCb(nameServerCallback, (void *)getCallbackArg(server));
					cdevRequestObject & monReq = cdevRequestObject::attachRef("NameServer", "monitorOn");

					monData.insert("name", server);
					monData.insert("domain", domain);
					monReq.sendCallback(monData, monCb);
					}
					
				// *********************************************
				// * Here is where a "set ClientInfo" message
				// * will be sent to the server to provide
				// * descriptive information about the client.
				// *********************************************
 				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;
 				handler->setTagChangeFlag(0);
 
				cdevMessageBinary packet(handler->getClientID(), 
							 0, 0, 0, 0, CDEV_SERVER_OP, 0, 0, 
							 NULL, "set ClientInfo",
							 &clientInfo.getClientData(), 
							 NULL, &tagMap);
		
				packet.streamOut(&binary, &binaryLen);
				packet.detachData();

				ServerInterface::enqueue(handler, binary, binaryLen);
				}
			}
		}
	return handler;
	}
