/************************************************************/
/*                                                          */
/*  The cpp file of server program                          */
/*  For CS 145 (Fall 2002) Lab 1                            */
/*  Author: Xiaoliang (David) Wei                           */
/*  Date:   Oct 15, 2002                                    */
/*                                                          */
/*  
/************************************************************/

#include "connection.h"
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>

#define TRUE 1
#define MAX_CON_BUF 1024

#define TIMEOUT_SEC 10

unsigned short int PORT_NUM;
/* the port number that the server is listening to. 
   (In this reference solution, TCP and UDP shared one port. Which is described in the only parameter of the command line.) 
*/

int tcpsock;
/* The TCP listening socket */

int udpsock;
/* The UDP listening socket */

ConnectionList *cl=new ConnectionList();
/* Connection List */



int tcp_init()
{
    int length;
    struct sockaddr_in server;   	

    //Create a socket
    tcpsock=socket(AF_INET,SOCK_STREAM,0);
    if (tcpsock<0)
    {
 	 printf("ERROR at : opening stream socket\n");
 	 return 1;
    }

    //bind to a port
    server.sin_family=AF_INET;
    server.sin_addr.s_addr=INADDR_ANY;
    server.sin_port=htons(PORT_NUM);
    if (bind(tcpsock,(struct sockaddr *)&server,sizeof server)<0)
    {
    	printf("TCP Binding error, maybe port number occupied by others. Change another port");
   	server.sin_port=0;
   	if (bind(tcpsock,(struct sockaddr *)&server,sizeof server)<0)
   	{
	        printf("ERROR: binding stream socket\n");
	 	return 1;
   	}
    };

    //get the server's name
    length=sizeof server;
    if (getsockname(tcpsock,(struct sockaddr *)&server,(socklen_t *)&length)<0)
    {
	 printf("ERROR: getting socket name\n");
	 return 1;
    }
    PORT_NUM=ntohs(server.sin_port);
    printf("TCP Socket port # %d\n",ntohs(server.sin_port));
    
    //listen to the port
    listen(tcpsock,5);    
    return 0;
};

int udp_init()
{
    int length;
    struct sockaddr_in server;

    //create a socket   	
    udpsock=socket(AF_INET,SOCK_DGRAM,0);
    if (udpsock<0)
    {
 	 printf("ERROR: opening stream socket!\n");
 	 return 1;
    }

    //bind to the port
    server.sin_family=AF_INET;
    server.sin_addr.s_addr=INADDR_ANY;
    server.sin_port=htons(PORT_NUM);
    if (bind(udpsock,(struct sockaddr *)&server,sizeof server)<0)
    {
    	printf("UDP Binding error, maybe port number occupied by others. Change another port");
   	server.sin_port=0;
   	if (bind(udpsock,(struct sockaddr *)&server,sizeof server)<0)
   	{
	        printf("ERROR: binding stream socket\n");
	 	return 1;
   	}
    };
    PORT_NUM=ntohs(server.sin_port);
    length=sizeof server;
    if (getsockname(udpsock,(struct sockaddr *)&server,(socklen_t *)&length)<0)
    {
	 printf("ERROR: getting socket name\n");
	 return 1;
    }
    printf("UDP Socket port # %d\n",ntohs(server.sin_port)); 
    
    return 0;
};

void tcp_accepter()
/* Accept a tcp connection */
{
   struct sockaddr incoming_addr;
   socklen_t incoming_len;   
	
   // accept the connection
   incoming_len=sizeof incoming_addr;
   int msgsock=accept(tcpsock,(struct sockaddr *)&incoming_addr,(socklen_t *)&incoming_len);	 
   if (msgsock==-1)
	printf("ERROR: TCP accept\n");
   else 
   {
	//check if it's valid or not
	if (cl->addNewConnection(&incoming_addr,msgsock))
	{
		close(msgsock);
		printf("Connection Refused\n");
	};
   };
}

void udp_talker()
// echo udp packets
{
   struct sockaddr incoming_addr;
   socklen_t incoming_len;   
	
   char s[1024];
   int rval; 		
   
//read a packet
   memset(s,0,sizeof s);   
   incoming_len=sizeof incoming_addr;
   rval=recvfrom(udpsock,s,1024,0,&incoming_addr, &incoming_len);   
   if (rval<=0)
   	printf("ERROR: in reading UDP socket\n");
   else  
   { 
//reply the packet
	    printf("Get %d characters:%s\n", rval, s);
	   rval=sendto(udpsock,s,rval,0,&incoming_addr,incoming_len);
	   if (rval<=0) 
	   {
	   	printf("ERROR: in sending UDP packets\n");
	   	printf("%d\n",rval);
	   }
	   else
	    printf("sent back\n");
    }	
};

void printAddress(struct sockaddr * saddr)
{
    struct sockaddr_in* addr=(struct sockaddr_in*) saddr;
    uint32_t a=addr->sin_addr.s_addr;
    unsigned a1,a2,a3,a4;
    a1=a%256;    a=a/256;    
    a2=a%256;    a=a/256;    
    a3=a%256;    a=a/256;    
    a4=a%256;    
    printf("%d %d %d %d : %d\n",a1,a2,a3,a4,addr->sin_port);
};



int main (int argc, char* argv[])
{

   
   fd_set setForSelect;
   if (argc>3) 
   {
	printf("Error: Parameter Error! Should be: server [<tcp-port> [<udp-port>]]\n");
	return 1; 
   };
   if (argc>1) PORT_NUM=atoi(argv[1]); else PORT_NUM=0;  
   if (tcp_init()!=0) { printf("TCP Socket establish failure!\n"); return 1;};
   if (argc>2) PORT_NUM=atoi(argv[2]);
   if (udp_init()!=0) { printf("UDP Socket establish failure!\n"); return 1;};
   
   do 
   {
   	 printf("Listening...\n");
   	 

   	 struct timeval now;
   	 gettimeofday(&now,NULL);
	   	 
   	 struct timeval timeout;
	 struct timeval *selectTimeout=&timeout;
//	 printf("getting time\n");
   	 if (cl->nearestExpire(&timeout,&now,TIMEOUT_SEC)) selectTimeout=NULL;
//the timeout should be the expire time for the oldest connection.
	 
  //	 printf("getting set\n");
     	 int selectNum=cl->selectParam(&setForSelect);
//selectParam returns the select set of each connections, and also the highest number

   	 FD_SET(tcpsock,&setForSelect);
   	 if (tcpsock>selectNum) selectNum=tcpsock;
//add the TCP listening socket into the select set
   	 
   	 FD_SET(udpsock,&setForSelect);   	 
   	 if (udpsock>selectNum) selectNum=udpsock;
//add the UDP socket in the select set   	  

	 
   	 select(selectNum+1,&setForSelect,0,0,selectTimeout);

   	 
   	 gettimeofday(&now,NULL);
   	 
	 if (FD_ISSET(tcpsock,&setForSelect))
//a new connection is coming in
 	 {
 	 	 printf("TCP action\n");
 	 	 tcp_accepter();
	  }
	  else
	  if (FD_ISSET(udpsock,&setForSelect))
// a udp packet is coming in
	  {
	  	printf("UDP action\n");
	  	udp_talker();
	  }

	  cl->run(&setForSelect);
//check each connection

	  cl->checkExpire(&now,TIMEOUT_SEC);
//check if any connection expires

	  cl->clean();
//delete the inactive (dead) connection

   }while(TRUE);
};


