/************************************************************/
/*                                                          */
/*  The cpp file of Connection Management for the server    */
/*  For CS 145 (Fall 2003) Lab 1                            */
/*  Author: Xiaoliang (David) Wei                           */
/*  Date:   Oct 14, 2003                                    */
/*                                                          */
/************************************************************/

#ifndef CONNECTION_CPP
#define CONNECTION_CPP
#include "connection.h"
#include <string.h>
#include <stdio.h>

	void Connection::run()
	/* connection behaviour, here is echoing.*/
	{
		int rval;
		char buf[1024];

		memset(buf,0,sizeof buf);
		/* Initialization of the buf */

		printf("Connection %d:", sock);
	 	if ((rval=read(sock,buf,1024))<0)
	 	{
	 		 printf("ERROR: reading stream message\n");
	 	}
	 	else
	 	if (rval!=0)
	 	{
			printf("get %d characters: %s\n", rval, buf);
	 		write(sock,buf,rval);
	 	}
	 	else
	 	{
			printf("Client disconnected\n");
	 		stopped=1;
	 	};

	 	refreshActiveTime();
		/* The connection is now active, refresh the timer. */
	}

	int ConnectionList::allow(struct sockaddr *addr)
	//return 1 if it's allowed. otherwise return 0
	{
		//check if there are too many connections
		if (connNumber>=MAX_CONNECTION_NUMBER) return 0;

		//OK: the new connection is allowed
		return 1;
	};
	
	int ConnectionList::addNewConnection(struct sockaddr *addr, int newSock)
	//return 1 when there is an error, otherwise, return 0.
	{
		printf("Trying to add a new socket: %d...", newSock);
		if (!allow(addr)) 
		{
		    printf("refused\n");
		    return 1;
		}
		connList[connNumber] = new Connection(addr, newSock);
		connNumber++;		
		printf("accepted\n");
		return 0;
	};
	
	
	int ConnectionList::delConnection(int sock)
	{
		for (int i=0;i<connNumber;i++)
		{
			if (connList[i]->getSock()==sock)
			/*OK, we find the entry int the connList.*/
			{
				connList[i]=connList[connNumber-1];
				connNumber--;
				return 0;
			}
		}
		return 1;
	};
	
	void ConnectionList::clean()
	/* Delete all the inactive(dead) connections in the list */
	{
		int i=0;
		while (i<connNumber)
		{
			if (connList[i]->inactive())
			{
				printf("Shutting down the connection %d\n", connList[i]->getSock());
				close(connList[i]->getSock());
				delete connList[i];
				connNumber--;
				connList[i]=connList[connNumber];				
				connList[connNumber]=NULL;
			}
			else
			{
				i++;
			}
			
		}
	}
	
	void ConnectionList::checkExpire(struct timeval *now, int sec)
	/* now should be the time right after last "select"*/
	{
		for (int i=0;i<connNumber;i++)
		{
			long temp_sec=now->tv_sec-connList[i]->getActiveTime()->tv_sec;
			long temp_usec=now->tv_usec-connList[i]->getActiveTime()->tv_usec;
			temp_sec=temp_sec-sec;
			if ((temp_sec>0) || ((temp_sec==0) && (temp_usec<0)))
			{
				printf("connection:%d timeout\n", connList[i]->getSock());
				connList[i]->stop();
			}
		}
	};
	
	int ConnectionList::nearestExpire(struct timeval * timeout, struct timeval *now, int sec)
	/* This function calculate the next event (TimeOut) that is going to happen, the return value will be fit into the select function as the expiration timer */
	{
		if (connNumber==0) 
		{	    
		    return 1;
		}
		struct timeval * nearest=NULL;
		for (int i=0;i<connNumber;i++)
		if (!connList[i]->inactive())
		{
			struct timeval *temp=connList[i]->getActiveTime();
			
			if (nearest==NULL) nearest=temp;
			else
			if  (   (nearest->tv_sec>temp->tv_sec)
			     || ((nearest->tv_sec=temp->tv_sec) && (nearest->tv_usec>temp->tv_usec)))
			{
				nearest=temp;
			}
		};
		
		if (nearest==NULL) return 1;
		
		if ((now->tv_sec>nearest->tv_sec+sec)
		|| ((now->tv_sec==nearest->tv_sec+sec) &&(now->tv_usec>nearest->tv_usec)))
			{
				timeout->tv_usec=0;timeout->tv_sec=0;
			}
		else
		{
			if (now->tv_usec>nearest->tv_usec)
			{
				timeout->tv_sec=nearest->tv_sec+sec-now->tv_sec-1;
				timeout->tv_usec=nearest->tv_usec+1000000-now->tv_usec;			
			}
			else
			{
				timeout->tv_sec=nearest->tv_sec+sec-now->tv_sec;
				timeout->tv_usec=nearest->tv_usec-now->tv_usec;
			}
		}
		return 0;
	};	
	
	int ConnectionList::selectParam(fd_set *set)
	/* This function return the maximum ID of fd_set */
	{
		int max=0;
		FD_ZERO(set);
		for (int i=0;i<connNumber;i++)
			if (!(connList[i]->inactive()))
			{
				int tsock=connList[i]->getSock();
				FD_SET(tsock, set);
				if (tsock>max) max=tsock;
			}
		return max;
	}
	
	void ConnectionList::run(fd_set *set)
	{
		for (int i=0;i<connNumber;i++)
			if ((!(connList[i]->inactive()))&&(FD_ISSET(connList[i]->getSock(),set)))
			{
				connList[i]->run();
			};
	};

#endif /* CONNECTION_CPP */

