#include "common.h"
#include "deal.h"
#include "getnet.h"


/* global variable that points to the main host table */
struct hostinfo *tablePtr = NULL;

/* is debugging on? */
int opt_debug = 0;

/* default number of points to assign a host */
int opt_points = 1;

/* set the default servername from the .h file */
char opt_server[MAXADDR] = SERVER;

/* main loop.  start up, get a listing, and get periodic updates */
int main(int argc, char *argv[]) {
  time_t ltime, ctime;

  extern char *optarg;
  int c;

  while ((c = getopt(argc, argv, "dhp:s:?")) != -1) {
    switch (c) {
    case 'd':
      opt_debug = 1;
      break;
    case 'h':
      usage(argv[0]);
      break;
    case 'p':
      opt_points = atoi(optarg);
      break;
    case 's':
      strcpy(opt_server,optarg);
      break;
    case '?':
    default:
      usage(argv[0]);
      break;
    }
  }

  /* catch SIGINT and SIGHUP and exit if you get them */
  signal(SIGINT,(void *) myexit);
  signal(SIGHUP,(void *) myexit);

  /* add this machine to the network */
  if ((addself()) != 0) {
    fprintf(stderr,"Couldn't join network.\n");
    return(0);
  }

  /* record the time you got the listing */
  ltime = time((time_t *)0);

  /* get the listing */
  if ((getnet()) != 0) {
    fprintf(stderr,"Error in getting new network status.\n");
    return(0);
  }

  /* loop and request an update every UPDATE_TIME seconds */
  while(1) {
    sleep(UPDATE_TIME);
    ctime = time((time_t *)0);
    getupdate(ltime);
    ltime = ctime;
  }
}


/* print a usage message */
void usage(char *name) {
  fprintf(stderr, "Usage: %s [-dh] [-s servername]\n"
	  "       -d            Turn on debugging.\n"
	  "       -h            Get this help message.\n"
	  "       -s servername Specify which server to ask about the network.\n",name);
  myexit();
}


/* get the initial network */
int getnet() {
  int n, sockfd;
  char buf[MAXLINE];
  FILE *dumpPtr;
  int dumpfd;

  /* open the dump file to write */
  if ((dumpPtr = fopen(DUMP_FILE,"w")) == NULL) {
    perror("Couldn't open dump file");
    return (1);
  }
  dumpfd = fileno(dumpPtr);
  
  /* open a socket to the server */
  if ((sockfd = sopen()) == 0) {
    fprintf(stderr,"Couldn't open socket to server.\n");
    return(1);
  }

  if (opt_debug) {
    fprintf(stderr,"Getting initial listing.\n");
  }

  /* send the request */
  write(sockfd,"list\n",5);

  /* read the response and write it out the dump file */
  n = MAXLINE;
  while(n != 0) {
    n = read(sockfd,buf,MAXLINE);
    n = write(dumpfd,buf,n);
  }

  /* read the dump file that we have just written */
  readdump();
  close(sockfd);
  fclose(dumpPtr);
  return(0);
}


/* get the changes since ltime */
int getupdate(time_t ltime) {
  int sockfd, logfd, n;
  FILE *logPtr;
  char buf[MAXLINE];

  /* open the log file for writing */
  if ((logPtr = fopen(GLOG_FILE,"w")) == NULL) {
    perror("Can't open GLOG_FILE");
    return(1);
  }
  logfd = fileno(logPtr);

  /* open a socket to the server */
  if ((sockfd = sopen()) == 0) {
    fprintf(stderr,"Couldn't open socket to server.\n");
    return(1);
  }
  if (opt_debug) {
    fprintf(stderr,"Getting update.\n");
  }

  /* send the request */
  sprintf(buf,"since %d\n",(int) ltime);
  write(sockfd,buf,MAXLINE);

  /* read the response and write it out the log file */
  n = MAXLINE;
  while(n != 0) {
    n = read(sockfd,buf,MAXLINE);
    n = write(logfd,buf,n);
  }

  /* close the log file */
  fclose(logPtr);

  /* read the log file that we have just written */
  readupdate();
  close(sockfd);
  return(0);
}


/* parse the log file and do the changes indicated */
int readupdate() {
  int changed = 1;
  int *ptime = malloc(sizeof(int)), *np = malloc(sizeof(int));
  char line[MAXLINE], x[1], addr[MAXADDR];
  FILE *logPtr;

  /* open the log file for reading */
  if ((logPtr = fopen(GLOG_FILE,"r")) == NULL) {
    perror("Can't open GLOG_FILE");
    return(1);
  }
  
  /* get and throw away the header line */
  fgets(line,MAXLINE,logPtr);

  /* go through the rest of the log file and parse it */
  while((fgets(line,MAXLINE,logPtr)) != NULL) {
    sscanf(line,"%s %s %d %d",x,addr,ptime,np);

    /* if there were any changes, set this variable */
    changed = 0;

    /* if the line begins with a plus sign, add the host */
    if ((strcmp(x,"+")) == 0) {
      addhost(addr,(time_t)*ptime,*np);
      if (opt_debug) {
	fprintf(stderr,"Adding %s %d %d.\n",addr,*ptime, *np);
      }
    }

    /* if the line begins with a minus sign, delete the host */
    else if ((strcmp(x,"-")) == 0) {
      delhost(addr);
      if (opt_debug) {
	fprintf(stderr,"Deleting %s.\n",addr);
      }
    }

    /* otherwise, we have a badly formatted line */
    else {
      fprintf(stderr,"Error in parsing results from since.\n");
    }
  }

  /* if anything changed, do a dump */
  if (changed == 0) {
    if (opt_debug) {
      fprintf(stderr,"Changes made, doing a dump.\n");
    }
    dump();
  }
  
  /* close the log file */
  fclose(logPtr);
  return(0);
}


/* add this host to the network */
int addself() {
  int sockfd, message_len;
  char message[MAXLINE], buf[MAXLINE];
  
  /* open a socket to the server */
  if ((sockfd = sopen()) == 0) {
    fprintf(stderr,"Couldn't open socket to server.\n");
    return(1);
  }
  if (opt_debug) {
    fprintf(stderr,"Joining network.\n");
  }

  /* message is join and the number of points to grab */
  sprintf(message,"join %d\n",opt_points);
  message_len = strlen(message);

  /* send the request */
  write(sockfd,message,message_len);

  /* read the response */
  read(sockfd,buf,MAXLINE);
  if (opt_debug) {
    fprintf(stderr,"%s",buf);
  }
  close(sockfd);
  return(0);
}

/* delete this host from the network */
int delself() {
  int sockfd;
  char buf[MAXLINE];

  /* open a socket to the server */
  if ((sockfd = sopen()) == 0) {
    fprintf(stderr,"Couldn't open socket to server.\n");
    return(1);
  }
  if (opt_debug) {
    fprintf(stderr,"Leaving network.\n");
  }

  /* send the request */
  write(sockfd,"dele\n",5);
    
  /* read the response */
  read(sockfd,buf,MAXLINE);
  if (opt_debug) {
    fprintf(stderr,"%s",buf);
  }
  close(sockfd);
  return(0);
}


/* open a socket to the server */
int sopen() {
  int sockfd;
  struct sockaddr_in serv_addr;
  struct hostent *hostPtr;
  int one = 1;

  /* translate the server's name to an IP address */
  if ((hostPtr = gethostbyname(opt_server)) == NULL) {
    perror("gethostbyname");
    return(0);
  }

  /* open the socket */
  if ((sockfd = socket(AF_INET,SOCK_STREAM,0)) < 0) {
    perror("socket");
    return(0);
  }


  /* clear out and set the struct for the server address */  
  memset(&serv_addr, 0, sizeof(serv_addr));
  serv_addr.sin_family = AF_INET;
  memcpy(&serv_addr.sin_addr, hostPtr->h_addr,sizeof(struct in_addr));
  serv_addr.sin_port = htons(SERV_PORT);

  /* allow the socket to be reused immediately */
  setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, (char *)&one, sizeof(one));

  /* connect to the server */
  if (connect(sockfd,(struct sockaddr *)&serv_addr,sizeof(serv_addr)) < 0) {
    perror("connect");
    return(0);
  }
  
  return(sockfd);
}


/* dump and exit on a signal */
int myexit() {
  if (opt_debug) {
    fprintf(stderr,"exiting.\n");
  }
  /* remove self from the network */
  delself();
  if ((dump()) != 0) {
    fprintf(stderr,"Error in dumping network status, data may be lost.\n");
  }
  exit(0);
}
