/*****************************************************************************
* 	Project 	: Network Based Message Queue System		    **
*	Course		: CIS 650 - Software Engineering		    **
*	Location 	: /research/paraducks3/courses/cis650/proj/rt	    **
*	File		: rt.cc						    **
*	Description 	: The router class. 				    **
*****************************************************************************/
#include <rtincl.h>

Router::Router(void)
{

}

Router::~Router(void)
{
	/* end of the router program */
	delete comm; /* destroy the communication module */
	delete stats; /* the statistics module */
	delete log; /* and the logging module */

}

int     Router::create_message_queue(int key, int perms)
{ 	/* Create the components of the message queue */


	comm = new CommunicationModule(); /* create the comm module */
	if(comm == (CommunicationModule *)NULL) return FAILURE;

	stats = new Statistics(); /* create the statistics module */
	if(stats == (Statistics *)NULL) return FAILURE;


	r->key = key; /* store the key */
	r->permissions = perms; /* store the permissions of the message queue */

	log = new Log(); /* create the logger */
	if(log == (Log *)NULL) 	return FAILURE;
	/* note : log file is created by the default permissions set by the 
		router - so keep Log after permissions have been filled in */
	return SUCCESS;
}


int	Router::process_message(Connection *c, Message *m)
/* process request */
{
	DEBUG("processing message %s\n", m->data);
	switch (m->header->opcode)
	{
		case DATA_MESSAGE :
					DEBUG("process data message\n");
					process_data_message(c, m);    
					break;

		case REQUEST_MESSAGE :
					DEBUG("process request message\n");
					process_request_message(c, m);
					break;
		case CONTROL_MESSAGE : 	
					DEBUG("process control message\n");
					process_control_message(c, m); 
					break;
		default :
			DEBUG("Default message  - opcode unknown - reject\n");
					return FAILURE;
					break;
	}
	

	return SUCCESS;
}


int 	Router::process_data_message(Connection *c, Message *m)
{ /* process the data message received from the client */
Connection *destination; 
WaitItem *waitListItem;
		DEBUG("processing data message ...\n");
		/* check the queue of pending requests */
		waitListItem = wl -> delete_item (m->header->msgType);
		if (waitListItem == (WaitItem *) NULL)
		{ 
		/* if no requests are pending for the message of this type,
		 then, add the item to the data message list */
			DEBUG("No requests pending - adding item to dml \n");
			dml -> add_item(m); /* add the data item */
		/* and update the statistics module */
			stats -> noOfMessages ++;
 		/* update the number of bytes on the  message queue */
               		stats -> noOfDataBytes += m->header->msgLength;
		}
		else 
		{
		/* There is a server process that needs the message that is
		received. So, send the message to the destination */
		/* just double check that things are ok */
			DEBUG("Found a server that needs the message - routing\n");
			if (waitListItem -> type != m->header->msgType)
				err_dump(-1, "wait list : Internal inconsistency error");
			/* everything is fine ... create a destination conn */
			destination = new Connection(waitListItem->sockFd);
		
			/* route the message received to the destination */
			do_route(destination, m); /* send message over */

			/* Now the waiting list has been processed */
		}
		return SUCCESS;
}


int 	Router::process_request_message(Connection *c, Message *m)
{ /* process request for a message of a particular type */
Message *dataItem; /* I get this from delete_item */

		dataItem = new Message();
		/* create a new data item */
		DEBUG("processing request message ...\n");
		/* get the item from the data message queue and check if its
		there */
		dataItem = dml -> delete_item(m->header->msgType);
		if(dataItem == (Message *) NULL)
		{ 
		/* if there is no such item - the requestor has to wait */
		/* add the item to the request waiting list */
			DEBUG("No data item found - add request to wl \n");
			wl -> add_item (m->header->msgType, c->sockId);
		/* add item consisting of message type that we are waiting for,
				and socket id of who is waiting for it */
		}
		else
		{
			do_route (c, dataItem); /* send the message to the dest */
			/* and update the statistics module */
			stats -> noOfMessages --;
			
 			/* update the number of bytes on the  message queue */
                	stats -> noOfDataBytes -= dataItem->header->msgLength;

			delete dataItem; /* I have no use for the message 
		once it has been sent to the destination */
		}
		return SUCCESS;
}

int 	Router::process_control_message(Connection *c, Message *m)
{ /* process control messages */
nmsqid_ds *request;

		request = new nmsqid_ds;
		request = (nmsqid_ds *)m->data;

		DEBUG("processing control message ...\n");
		if(m-> header -> msgType == IPC_RMID)
		{
			DEBUG("Shutting down the process connection\n");
		DEBUG("RECEIVED End of FILE EOF on socket \n");
		close(c->sockId);
		comm -> listOfSocketDescriptors -> delete_item(c->sockId);
		/* and update the statistics module */
		stats -> noOfConnections --; 
		/* and then see if the descriptor is in the waiting list - in 
		that case delete the socket entry from there. */
		wl -> check_and_delete(c->sockId);
		delete c;
		return FAILURE;

		}
		if (request -> numberMsgsType != 0)
			process_message_type_query(request);
		/* if the client needs information about the number of messages
		on the message queue of a particular type, then send this */
		answer_query(request);
		/* and answer the rest of the queries anyway */
		/* Now the request structure is filled up. Send it back. */
		
		/* The reply Message structure is also to be filled up */
		m -> header -> opcode = CONTROL_MESSAGE;
		m -> header -> msgLength = sizeof(*request);
		m -> header -> uniqueId = ngetownid();

		DEBUG("got numberMsgsType = %d\n", request->numberMsgsType);
		DEBUG("got numberMsgs = %d\n", request->numberMsgs);
		DEBUG("got numberBytes = %d\n", request->numberBytes);
		DEBUG("got numberPendingProcesses = %d\n", request->numberPendingProcesses);
		DEBUG("got numberConnections = %d\n", request->numberConnections);
		do_route(c, m); 
		/* send the message to the destination (same as source) */
		return SUCCESS;
}

int     Router::do_route(Connection *c, Message *m)
{
struct iovec iov[2];

 	iov[0].iov_len = sizeof(MsgHeader);
        iov[0].iov_base = (char *) m->header;
        iov[1].iov_len = m->header->msgLength;
        iov[1].iov_base = (char *) m->data;
	DEBUG("do_route: sending uniqueId = %d\n", m->header->uniqueId);
	ret = writev(c->sockId, &iov[0], 2);
	err_dump(ret, "writev error in writing to socket");
	log -> log_message(c,m);
	/* log the message sent */
	/* after this the message will not be required - it has been
	processed - so destroy the message at this stage */
	delete m; 
	return SUCCESS;
}

/* answer queries */
int     Router::answer_query(nmsqid_ds *request)
{
/* answer all the rest of the queries */

	request -> numberMsgs = stats -> get_number_of_messages();
	/* get number of messages on the message queue - from statistics */

	request -> numberBytes = stats -> get_number_of_data_bytes();
	/* get number of bytes on the message queue */ 

	request -> numberPendingProcesses = wl -> get_count_of_waiting_processes();
	/* get the number of processes pending for data on the waiting list */

	request -> numberConnections = stats -> get_number_of_connections();
	/* get the active connection count from Statistics */

	return SUCCESS;
}

/* process the query for the number of messages of a given type on the 
	data message list */
int     Router::process_message_type_query(nmsqid_ds *request) 
{
	int type;
	
	type = request -> numberMsgsType; 
	/* want the no of messages of this type */

	request -> numberMsgsType = dml -> number_of_messages_of_type(type); 
	/* the number is filled in the structure */

	return SUCCESS;
}


