/************************************************************/
/*                                                          */
/*  The cpp file of main program                            */
/*  For CS 145 (Fall 2002) Lab 3                            */
/*  Author: Xiaoliang (David) Wei                           */
/*  Last Change Date:Dec 12, 2000                           */
/*  Original Date:   Dec 20, 2000                           */
/*                                                          */
/************************************************************/



#ifndef MAIN_CPP
#define MAIN_CPP

#include <assert.h>
#include <sys/time.h>
#include <sys/types.h>
#include <unistd.h>
#include <sys/socket.h>
#include <stdio.h>
#include <string.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#include <stdlib.h>

#include "flmodel.cpp"

#define CONFIG_FILE "mybgp.cfg"
//Format:
//int LISTEN_PORT
//int LISTEN_QUEUE_LENGTH
//int CHECK_INT;	//the frequency of the selection check (s)
//int TIME_OUT;		//the time out threshold of communicaition (clock==CHECK_INT s)
//int UPDATE_INT;	//the frequency of update (clock==CHECK_INT s)
//int RETRY_INT;	//the frequency of reconnection for failure init state(clock==CHECK_INT ss)
//int ip1 ip2 ip3 ip4	//local IP

#define DATA_FILE "mybgp.dat"
//Format:
//<data number>
//<tick> <ip1> <ip2> <ip3> <ip4> <port>
//<tick> <ip1> <ip2> <ip3> <ip4> <port>
//...

fd_set readfds;
fd_set writefds;
//for select check

int myclock;	//clock counter


int LISTEN_PORT;
int LISTEN_QUEUE_LENGTH;
int CHECK_INT;	//the frequency of the selection check (ms)
int TIME_OUT;	//the time out threshold of communicaition (ms)
int UPDATE_INT;	//the frequency of update (clock==CHECK_INT ms)
int RETRY_INT;	//the frequency of reconnection for failure init state
long unsigned int LOCAL_IP;



struct client_node
{
	int startTick;
	int port;
	long unsigned int ip;
};


struct client_node clientList[100];
int clptr;


long unsigned int ipList[1000];
int ilptr;
//ip control






int sock;		//sock for listen

Host* hostHead;


void readdata()
{
	int ip1,ip2,ip3,ip4;
	long unsigned int temp;	

	printf("\n");	
	FILE* file;
	file=fopen(DATA_FILE,"r");	
	fscanf(file,"%d",&clptr);
	printf("Totally %d connections:\n",clptr);
	for (int i=0;i<clptr;i++)
	{
		if ( feof(file))
		{
			printf("Data File Error!!!\n");
			exit(1);			
		}
		else
		{		
			fscanf(file,"%d %d %d %d %d %d\n",
			&clientList[i].startTick,
			&ip1,
			&ip2,
			&ip3,
			&ip4,
			&clientList[i].port);		
			
			clientList[i].ip=ip4toip(ip1,ip2,ip3,ip4);		
			
			printf("%d %d %d %d =%d   Port: %d   Start at: %d:\n",
				ip1,ip2,ip3,ip4,clientList[i].ip, 
				clientList[i].port,clientList[i].startTick);
						
		}
	}	
	fclose(file);
}

void myinit()
{
	int ip1,ip2,ip3,ip4;
	FILE* file;
	file=fopen(CONFIG_FILE,"r");
	fscanf(file,"%d",&LISTEN_PORT);
	fscanf(file,"%d",&LISTEN_QUEUE_LENGTH);
	fscanf(file,"%d",&CHECK_INT);
	fscanf(file,"%d",&TIME_OUT);
	fscanf(file,"%d",&UPDATE_INT);
	fscanf(file,"%d",&RETRY_INT);
	fscanf(file,"%d %d %d %d",&ip1,&ip2,&ip3,&ip4);
	LOCAL_IP=ip4toip(ip1,ip2,ip3,ip4);
	fclose(file);	
	printf("\nLocal IP: %d %d %d %d==%d, PORT: %d\n",ip1,ip2,ip3,ip4, LOCAL_IP, LISTEN_PORT);
	printf("Listen Queue: %d,  select check interval: %d, time out threshold: %d\n",
		LISTEN_QUEUE_LENGTH,CHECK_INT,TIME_OUT);
	printf("Update interval: %d       Reconnect Interval %d\n", UPDATE_INT, RETRY_INT);
}



////////////////////////////////////////////
////  start listen socket
////////////////////////////////////////////
void startListen()
{
   int length;
   struct sockaddr_in server;
      
   

   sock=socket(AF_INET,SOCK_STREAM,0);
   if (sock<0)
   {
	 perror("opening stream socket");
	 exit(1);
   }

   server.sin_family=AF_INET;
   server.sin_addr.s_addr=INADDR_ANY;
   server.sin_port=htons(LISTEN_PORT);   
   if (bind(sock,(struct sockaddr *)&server,sizeof server)<0)
   {
         server.sin_port=0;
         if (bind(sock,(struct sockaddr *)&server,sizeof server)<0)
         {
	   perror("binding stream socket");
	   exit(1);
         }
         else
           printf("Specified port used, changed port automatically.\n");
   }

   length=sizeof server;
   if (getsockname(sock,(struct sockaddr *)&server,(socklen_t *)&length)<0)
   {
	 perror("getting socket name");
	 exit(1);
   }
   printf("Socket port # %d\n",ntohs(server.sin_port));   

   listen(sock,LISTEN_QUEUE_LENGTH);
   
   	
}


////////////////////////////////////////////
////  The following functions implements the IP list.
////////////////////////////////////////////
int inIPList(long unsigned int ip)
{
	for (int i=0;i<ilptr;i++)
		if (ipList[i]==ip) return 1;
	return 0;
}
int addIP(long unsigned int ip)
{
	for (int i=0;i<ilptr;i++)
		if (ipList[i]==ip) return 0;
	ipList[ilptr]=ip;
	ilptr++;
	return 1;
}
int delIP(long unsigned int ip)
{
	for (int i=0;i<ilptr;i++)
		if (ipList[i]==ip)
		{
			ilptr--;
			ipList[i]=ipList[ilptr];
			return 1;			
		}

	return 0;
}
void ipListInit()
{	
	ilptr=0;
	//addIP(LOCAL_IP);	
};

////////////////////////////////////////////
////  The following functions implements the Host list.
////////////////////////////////////////////
void hostListInit()
{
	hostHead=NULL;
}
Host* addHost(int sock,int type)
{
	struct sockaddr_in hostname;
	int length=sizeof hostname;
	if (getpeername(sock,  (struct sockaddr *)&hostname, (socklen_t *)&length)<0)
	{
		printf("Failed to get peer Name!\n");
		return NULL;
	}	
	long unsigned int ip=hostname.sin_addr.s_addr;
	if (addIP(ip)==0)
	{
		printf("Multiple IP Connection\n");
		close(sock);
		return NULL;
	}
	printf("ok, adding new Host:%d...\n",ip);
	
	Host* temp=new Host(sock,type,ip);
	temp->next=hostHead;
	hostHead=temp;
	printf("ok, finished adding\n");	
	return hostHead;	
}



void clockInit()
{
	myclock=0;
};

int makeCheckList()
{
	int max=0;
	FD_ZERO(&readfds);
	FD_ZERO(&writefds);
	
	
	//for hosts
	Host* temp=hostHead;
	while (temp!=NULL)
	{
		if ((temp->rwState==1)||(temp->rwState==2))
		{
			FD_SET(temp->sock,&readfds);
			if ((temp->sock)>=max) max=temp->sock+1;
		}
		else
		if (temp->rwState==3) 
		{
			FD_SET(temp->sock,&writefds);
			if ((temp->sock)>=max) max=temp->sock+1;
		}
		else
		if ((temp->rwState==0)&&(temp->macroState==STATE_ESTABLISHED))
		{
			FD_SET(temp->sock,&readfds);
			if ((temp->sock)>=max) max=temp->sock+1;
		}
		temp=temp->next;		
	}
	
	FD_SET(sock,&readfds);//add listen socket	
	if (sock>=max) max=sock+1;
	return max;
}


//the following 2 functions are for delete the died connections
void killAHost(Host* node)
{
	delIP(node->ip);	
	close(node->sock);
}
void clearSuicide()
{	
	Host *garbage;
	while ((hostHead!=NULL)&&(hostHead->alive==0))
	{
		killAHost(hostHead);
		garbage=hostHead;		
		hostHead=hostHead->next;
		delete garbage;
	}
	if (hostHead!=NULL)
	{
		Host *temp=hostHead;
		while (temp->next!=NULL)
		{			
			if (temp->next->alive==0)
			{
				killAHost(temp->next);
				garbage=temp->next;
				temp->next=temp->next->next;
				delete garbage;
			}
			else
				temp=temp->next;
		}
	}
};

//the following 2 functions are for start a new connection
void checkClientStart()
{
	
	struct sockaddr_in server;
	int sock;

	for (int i=0;i<clptr;i++)
	if ((clientList[i].startTick<=myclock)
		&&(!inIPList(clientList[i].ip))
		)	
	{
		
		clientList[i].startTick+=RETRY_INT;
		
		printf("Trying to connect %d:%d...", clientList[i].ip,clientList[i].port);
		
		server.sin_family=AF_INET;
	
	
		int sock=socket(AF_INET,SOCK_STREAM,0);
		if (sock<0)
		{
			printf("opening stream socket\n");
			return;
		}
		  
		server.sin_port=htons(clientList[i].port);
		memcpy((char *)&server.sin_addr,(char *)&clientList[i].ip,sizeof(long unsigned int));
		  
		
		if (connect(sock,(struct sockaddr *)&server,sizeof server)<0)
		{
			printf("Connection failed\n");
		}
		else
		{
		
			printf("Connection established.\n");		  
			addHost(sock,HOST_CLIENT);
			if (inIPList(clientList[i].ip))
				printf("OK. New Host saved into clientlist.\n");
			else
				printf("Cannot add new Host\n");
				
		}		
	}	
	
}
void acceptConnection()
{
	printf("Accepting connection as Server:\n");
	struct sockaddr_in hostname;
	int length;
	int newsock;
	length=sizeof hostname;
	newsock=accept(sock,(struct sockaddr *)&hostname,(socklen_t *)&length);
	if (newsock<0) 
	{
		printf("accepting failed.\n");
		return;
	}	
	printf("accepting suceed.\n");
	addHost(newsock,HOST_SERVER);
		
};
void main ()
{
	struct timeval tv;	
	printf("Clear IP List");
	ipListInit();
	printf("OK\n");
	printf("Clear Host List...");
	hostListInit();
	printf("OK\n");
	printf("Reading config file...");
	myinit();
	printf("OK\n");
	printf("Reading data file...");
	readdata();	
	printf("OK\n");
	printf("Start Listening...");
	startListen();
	printf("OK\n");	
	clockInit();
	
	while (1)					
	{
		
		myclock++;		
		printf("Now time:%d ticks\n",myclock);		
		checkClientStart();
		//printf("Checking blocked state...");
		int max=makeCheckList();		
		//printf("OK\n");
		
		tv.tv_sec=CHECK_INT;
		tv.tv_usec=0;
//		printf("max=%d %d\n",max, CHECK_INT);
		select(max,&readfds,&writefds,NULL,&tv);
//		printf("Finished\n");
//		printf("sock Status:%d:%d\n",sock,FD_ISSET(sock,&readfds));		
		if (FD_ISSET(sock,&readfds)) 		
			acceptConnection();
			
//		printf("Checking the host list...");
		Host * temp=hostHead;
		while (temp!=NULL)
		{
			temp->work();
			temp=temp->next;
		}
//		printf("OK\n");
//		printf("Clearing Suicide...");
		clearSuicide();
//		printf("OK\n");
	}
}










#endif

