/*This program accepts connections from clients as well as from servers.
  The server will announces its availability and the services it offers
  and on which IP address/port. Dispatcher stored this information in 
  queues (different queues according to different services). When a client
  ask for a service, dispatcher will look up the according queue (in 
  round-robin rotaiton), and give the server's ip address and port number
  back to the client.*/

#include "service.h" 

  char buff[150];
  char reply[150]; 
  int sockfd;         
  int acsd;           
  struct sockaddr_in my_addr;    
  struct sockaddr_in client_addr;
  int len=sizeof(client_addr);
  int yes=1;                
  int myport=3498;    /*fix a port for dispatcher to 3498 */
  s_list *sum_s=NULL;
  s_list *product_s=NULL; 
  

int main(void)
{  
   char func[15]; /*to contain the function client requested.*/
   char addr[50]; /*to contain the server's ip address.*/
   int port;      /*to contain the server's port number.*/
   int status=0;
   s_list *tmp;

   /*create a socket, bind it to myport and listen on this port.
     if status is 1,then fails.*/
   if(status=bind_remote(myport,&sockfd,&my_addr)==1) exit(1);
 
   while(1)
   {
     /*accept incoming connections to the dispatcher.*/
    if ( (acsd = accept(sockfd, (struct sockaddr *) &client_addr, &len)) < 0)
    {
      perror("accept()");
      exit(1);
    }
    
    /*receive message from the connection.*/
    if(recv(acsd,buff,100,0)<0)
    {
      perror("recv");
      exit(1);
    }
    
    /*If it is a message from client.*/
    if(strcmp(strtok(buff," "),"client")==0)
      {
       printf("Received info from client %s at %d.\n",
            inet_ntoa(client_addr.sin_addr), ntohs(client_addr.sin_port));
       strcpy(buff,strtok(0," "));

       /*if the function requested is sum,look up sum list.*/
       if(strcmp(buff,"sum")==0)  
          searchlist(&sum_s);
       /*if the function requested is product,look up product list.*/
       else if(strcmp(buff,"product")==0) 
          searchlist(&product_s);
       
       printf("Sending server info to client at %s at %d ......",
             inet_ntoa(client_addr.sin_addr), ntohs(client_addr.sin_port));

       /*send the server's ip address and port number (stored in reply)
         to client.*/
       if(send(acsd, (void *) reply, strlen(reply) + 1, 0)<0)
       {
        perror("send");
        exit(1);
       }

       printf("Done!\n");
      }

    /*If it is a message from server.*/
    else 
      {
       printf("Received info from server %s at %d.\n",
            inet_ntoa(client_addr.sin_addr), ntohs(client_addr.sin_port));

       strcpy(func,strtok(0," ")); /*get the services provided by server.*/
       strcpy(addr,strtok(0," ")); /*get the ip address of server.*/
       port=atoi(strtok(0," "));   /*get the port number of server.*/

       /*create a new node */
       tmp=(s_list *)malloc(sizeof(s_list));
       strcpy(tmp->addr,addr);
       tmp->port=port;
       tmp->next=NULL;

       /*if the service is sum,put it in the sum list.*/
       if(strcmp(func,"sum")==0)  
	 addlist(&sum_s,tmp);
       /*if the service is product,put it int he product list.*/
       else if(strcmp(func,"product")==0) 
	 addlist(&product_s,tmp);
       /*if the services provided by server are both sum and product, put
         it into both sum and product list.*/
       else if(strcmp(func,"both")==0)
	 {
	   addlist(&sum_s,tmp);
	   tmp=(s_list *)malloc(sizeof(s_list));
           strcpy(tmp->addr,addr);
           tmp->port=port;
	   addlist(&product_s,tmp);
	 }
       /*if the service warns the dispatcher it is no longer available,
         delete it from the according list.*/
       else if(strcmp(func,"exit")==0)
         {
          deletelist(&sum_s,addr,port);
          deletelist(&product_s,addr,port);
         }
      }
   
    close(acsd);
   }
   close(sockfd);
  return (0);
}


/*A function implement round-robin rotation.*/
void roundrobin(s_list **list)
{
 s_list *tmp,*head;
 tmp=*list;

 if (*list==NULL ||(*list)->next==NULL) return;
 
 /*go through the list, until reaches the end.*/
 while(tmp->next!=NULL)
   {
    tmp=tmp->next;
   }
 
 /*pick up the first node, put it to the end of the queue.*/
 head=(*list)->next;
 tmp->next=*list;
 (*list)->next=NULL;
 (*list)=head;
 return;
}


/*A function to get the first node of the list,and round-robin the list
  once so that we will choose the next node next time.*/
void searchlist(s_list ** list)
  {
   s_list *tmp=*list;
  
      if(*list==NULL) strcpy(reply,"Not Available.");
      else 
	{
         sprintf(reply,"%s %d",(*list)->addr,(*list)->port);
         roundrobin(list);
	}  
  }

/*A function to add a node to the end of the list.*/
void addlist(s_list **list,s_list *elem)
  {
   s_list *tmp;
   tmp=*list;
   if(tmp==NULL) 
   {
      *list=elem;
      return;
   }
   /*go throught the list, until reaches the end.*/ 
   while(tmp->next!=NULL)
    {
      tmp=tmp->next;
    }
   /*add the node to the end of the list.*/    
   tmp->next=elem;
   return;
  }

/*A fucntion to delete a node according to the ip address and port number. */
void deletelist(s_list **list,char*addr, int port)
{
  s_list *cur,*pre;
  cur=*list;
  pre=NULL;
  if(cur==NULL)
    {
      printf("No server is working.\n");
      return;
    }
 
  while(cur!=NULL)
    {
      /*if we have found the node to delete.*/
      if(strcmp(cur->addr,addr)==0&& (cur->port==port))
	{
	  if(cur==*list)
	    *list=cur->next;
	  else if(cur!=NULL)
	    pre->next=cur->next;
	  printf("deleting %s\n",inet_ntoa((*(struct in_addr*)gethostbyname(cur->addr)->h_addr)));
	  return;
	}
      pre=cur;
      cur=cur->next;
    }
  /*After going through the list,such node is not found.*/
  printf("No such server at all.\n");
  return;
}