#include "acumulador.h"

int option=0,voto=0,total=0,cero=0,uno=0,dos=0,tres=0,cuatro=0,cinco=0;

void reusePort(int s)
{
  int one=1;
  if ( setsockopt(s,SOL_SOCKET,SO_REUSEADDR,(char *) &one,sizeof(one)) == -1 ) {
    printf("error in setsockopt,SO_REUSEPORT \n");
    exit(-1);
  }
} 

main(int argc, char *argv[]) {
  int s,sMonitor;
  struct sockaddr_in server,from;
  struct  hostent *hp;
  int length,fromlen;
  fd_set readfds, readfdsCopy;
  int n,i;
  int portType[MAX], sockList[MAX], lastSock=0;
  char str[INET_ADDRSTRLEN];
  char ThisHost[MAX];

  //Verificamos la sintaxis
  if (argc != 3) {
    printf("Usar: acumulador <puerto_votacion> <puerto_monitor>\n");
    exit(1);
  }

  //Get Server Host information: NAME and INET ADDRESS
   gethostname(ThisHost, sizeof(ThisHost));//OR strcpy(ThisHost,"localhost");
  if  ( (hp = gethostbyname(ThisHost)) == NULL ) { //Rellenamos info asociada al host.
    fprintf(stderr, "Can't find host %s\n", ThisHost);
    exit(-1);
  }
  //Ahora rellenamos la direccion del server. 
  bcopy (hp->h_addr, &(server.sin_addr), hp->h_length);
  inet_ntop(AF_INET, &(server.sin_addr),str,INET_ADDRSTRLEN);
  printf("Server running at : %s (%s)\n", ThisHost, str);


  //Construct name of socket to send to.
  server.sin_family = AF_INET;
  server.sin_addr.s_addr = htonl(INADDR_ANY);

  //Configuramos el primer socket(votacion)
  server.sin_port = htons(atoi(argv[1])); //Puerto votacion como segundo arguemento
  s = socket (AF_INET,SOCK_STREAM,0); 
  if (s<0) {
    perror("error opening stream socket");
    exit(-1);
  }
  reusePort(s);

  //Asociamos descriptor del socket con IP:Puerto
  if ( bind(s, (struct sockaddr *)&server, sizeof(server)) ) {
    close(s);
    perror("error binding name to stream socket");
    exit(-1);
  }
  
  //Asociamos la informacion del socket para poder acceder a ella
  length = sizeof(server);
  if ( getsockname (s, (struct sockaddr *)&server,&length) ) {
    perror("error getting socket name");
    exit(0);
  }
  printf("Server Port Votacion: %d\n", ntohs(server.sin_port));

  //Lister for connections
  listen(s,SOMAXCONN);

  //Configuramos el segundo socket(monitor)
  server.sin_port = htons(atoi(argv[2]));//Puerto monitor como tercer arguemento
  sMonitor = socket(AF_INET,SOCK_STREAM,0); 
  if (sMonitor<0) {
    perror("error opening stream socket");
    exit(-1);
  }
  reusePort(sMonitor);

  //Asociamos descriptor del socket con IP:Puerto
  if ( bind(sMonitor, (struct sockaddr *)&server, sizeof(server)) ) {
    close(s);
    perror("error binding name to stream socket");
    exit(-1);
  }
  
  //Asociamos la informacion del socket para poder acceder a ella
  length = sizeof(server);
  if ( getsockname (sMonitor, (struct sockaddr *)&server,&length) ) {
    perror("error getting socket name");
    exit(0);
  }
  printf("Server Port Monitor: %d\n", ntohs(server.sin_port));

  //Lister for connections
  listen(sMonitor,SOMAXCONN);

  fromlen = sizeof(from);
  FD_ZERO(&readfdsCopy); //Inicializamos el arreglo en cero
  FD_SET(s,&readfdsCopy);//añadimos el descriptor del socket 's', al listado
  FD_SET(sMonitor,&readfdsCopy);//añadimos el descriptor del socket 'sMonitor', al listado

  for(;;){
    memcpy(&readfds, &readfdsCopy, sizeof(fd_set)); //destino, fuente, numero de bytes a copiar
    n = select(FD_SETSIZE, &readfds, (fd_set *) 0, (fd_set *) 0, NULL);
    //Salimos de select(), se leyó algo ó señal.
    if (n > 0) { //Hay actividad en el listado de descriptores
      if (FD_ISSET(s, &readfds)) {//Preguntamos si es 's', el de la actividad->nueva conexion.
        sockList[lastSock] = accept(s, (struct sockaddr *)&from, &fromlen);
	portType[lastSock++] = 1;
        FD_SET(sockList[lastSock-1], &readfdsCopy);//añadimos nuevo descriptor al listado "fijo".
      }
      if (FD_ISSET(sMonitor, &readfds)) {//Preguntamos si es 'sMonitor', el de la actividad->nueva conexion.
        sockList[lastSock] = accept(sMonitor, (struct sockaddr *)&from, &fromlen);
	portType[lastSock++] = 2;
        FD_SET(sockList[lastSock-1], &readfdsCopy);//añadimos nuevo descriptor al listado "fijo".
      }
      for (i=0; i < lastSock; i++) {
        if (FD_ISSET(sockList[i], &readfds)) //Cual descriptor presento actividad. "Evento".
          if (EchoServe(sockList[i], portType[i]) < 0) { //Si se desconecto un cliente.
            FD_CLR(sockList[i], &readfdsCopy); //Se remueve del listado "fijo".
            close (sockList[i]); //Se cierra el socket
            sockList[i] = sockList[--lastSock]; //Se reemplaza el removido por el ultimo agregado.
	    portType[i] = portType[lastSock]; //Se reemplaza el puerto removido por el ultimo agregado.
            i--; //Para la proxima iteracion chequear por el reemplazado pues lastSock se decremento.
          }
      }
    }
  }//fin for(;;)
}

int EchoServe(int psd, int portType) {
  char buf[BUFSIZE];
  int rc;
  for(;;){
    //Se lee la data desde el socket aceptado y se guarda en buf.
    if( (rc= recv(psd, buf, sizeof(buf), 0) ) < 0)
      perror("error receiving stream  message");
    if (rc > 0) {
      if (portType == 1) { //Cliente conectado al puerto de votar
	option=atoi(buf);
	switch (option) {
	case 0:
	 voto=++cero;
	 break;
	case 1:
	 voto=++uno;
	 break;
	case 2:
	 voto=++dos;
	 break;
	case 3:
	 voto=++tres;
	 break;
	case 4:
	 voto=++cuatro;
	 break;
	case 5:
	 voto=++cinco;
	 break;
	default:
	 break;
	}
      }
      if (portType == 2){ //Monitor conectado
        if (strcmp(buf, "monitor") != 0)
          return(-1); //Si se envia algo distinto a monitor no se envia informacion
	total=cero+uno+dos+tres+cuatro+cinco;
	if (total == 0) total=1;
	sprintf(buf,"\n| \top \t| \tpref \t| \t\%\t |"); send(psd, buf, sizeof(buf), 0);
	sprintf(buf,"\n| \t0 \t|  \t%d \t| \t%.2f\t |",cero,(float)100*cero/total); send(psd, buf, sizeof(buf), 0);
	sprintf(buf,"\n| \t1 \t|  \t%d \t| \t%.2f\t |",uno,(float)100*uno/total); send(psd, buf, sizeof(buf), 0);
	sprintf(buf,"\n| \t2 \t|  \t%d \t| \t%.2f\t |",dos,(float)100*dos/total); send(psd, buf, sizeof(buf), 0);
	sprintf(buf,"\n| \t3 \t|  \t%d \t| \t%.2f\t |",tres,(float)100*tres/total); send(psd, buf, sizeof(buf), 0);
	sprintf(buf,"\n| \t4 \t|  \t%d \t| \t%.2f\t |",cuatro,(float)100*cuatro/total); send(psd, buf, sizeof(buf), 0);
	sprintf(buf,"\n| \t5 \t|  \t%d \t| \t%.2f\t |",cinco,(float)100*cinco/total); send(psd, buf, sizeof(buf), 0);
	sprintf(buf,"done"); send(psd, buf, sizeof(buf), 0);
      }
    }
    else { //Si no se recibe nada, cerramos
      return(-1);
    }
  }
}

     
