#include <sys/types.h>
#include <sys/time.h>
#include <sys/param.h>
#include <sys/socket.h>

#include <netinet/in.h>
#include <arpa/inet.h>

#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <strings.h>
#include <unistd.h>

#include <project2/include/Socket.h>
#include <project2/include/types.h>

#ifndef min
#define min(x,y)	((x) < (y) ? (x) : (y))
#endif

/* sleep msec amount of time using select */
void Sleep(int msec) {
  struct timeval tv;
  
  tv.tv_sec = msec/1000;
  tv.tv_usec = 1000*(msec%1000);

  select(0, NULL, NULL, NULL, &tv);
  return;
}


void usage(int argc, char **argv) {
  fprintf(stderr,
	  "usage: %s \n"
	  "\t -a <ip address>: server address\n"
	  "\t -p <port>: server port number\n"
	  "\t -i <input file>: file to be sent over TCP\n"
	  "\t -s <chunk size (bytes)>: max # of bytes to read from the local file at a time, default=1024(bytes)\n"
	  "\t -t <wait time (ms)>: wait time between two Write() calls, default=0\n\n",
	  argv[0]);
}


int Main(int argc, char **argv) {
  char ch;
  struct sockaddr_in srv;
  int sockfd, fd = -1;
  int wait_time = 0;
  int buflen = 1024;
  char *buf;

  
  bzero(&srv, sizeof(srv));
  srv.sin_family = AF_INET;
  
  while((ch = getopt(argc, argv, "a:i:p:t:s:")) != -1) {
    switch(ch) {
    case 't':
      wait_time = atoi(optarg);
      break;
    case 's':
      buflen = atoi(optarg);
      break;
    case 'a':
      srv.sin_addr.s_addr = inet_addr(optarg);
      if(srv.sin_addr.s_addr == (u_int32_t) -1) {
	fprintf(stderr, "inet_network failed!\n");
	exit(1);
      }
      break;
    case 'i':
      if((fd = open(optarg, O_RDONLY)) < 0) {
	perror("open");
	exit(1);
      }
      break;
    case 'p':
      srv.sin_port = htons(atoi(optarg));
      break;
    default:
      usage(argc, argv);
      exit(1);
    }
  }
  
  if(argc != optind) {
    usage(argc, argv);
    exit(1);
  }
  
  if (fd == -1) {
    fprintf(stderr, "must provide input file (-i <file>)\n");
    usage(argc, argv);
    exit(-1);
  }
  
  if(srv.sin_addr.s_addr == INADDR_ANY) {
    fprintf(stderr, "Invalid Address %s\n", inet_ntoa(srv.sin_addr));
    exit(1);
  }
  
  if(srv.sin_port == 0) {
    fprintf(stderr, "Invalid Port 0\n");
    exit(1);
  }

  if((sockfd = Socket(AF_INET, SOCK_STREAM, 0)) < 0) {
    perror("socket");
    exit(1);
  }

  /* unset this to bind to an address/port */
#if 0
  {
    /* Bind to TCP socket with address/port (INADDR_ANY, 8000) */

    struct sockaddr_in t;
    
    bzero(&t, sizeof(struct sockaddr_in));
    t.sin_family = AF_INET;
    t.sin_port = 8000;
    t.sin_addr.s_addr = INADDR_ANY;
    /* t.sin_addr.s_addr = htonl(0x01010101); */
    
    if(Bind(sockfd, (struct sockaddr*) &t, sizeof(t)) < 0) {
      perror("bind");
      exit(1);
    }
  }
#endif

  if(Connect(sockfd, (struct sockaddr*) &srv, sizeof(srv)) < 0) {
    perror("connect");
    exit(1);
  }
  
  /* allocate buffer */
  if ((buf = malloc(buflen+1)) == NULL) {
    fprintf(stderr, "out of memory\n");
    exit(1);
  }


  while(1) {
    int rbytes;
    
    fprintf(stderr, "attempting to read %d bytes from local file\n", buflen);
    
    if((rbytes = read(fd, buf, buflen)) < 0) {
      perror("read");
      exit(1);
    }
    
    if(rbytes == 0)
      break;
    
    fprintf(stderr, "\t --- writing packet %d bytes\n", rbytes);
    
    if(Write(sockfd, buf, rbytes) < 0) {
      perror("write");
      exit(1);
    }

    fprintf(stderr, "sleeping for %d msec\n", wait_time);
    Sleep(wait_time);
  }
  
  fprintf(stderr, "done...\n");
  
  free(buf);
  Close(sockfd);
  close(fd);
  
  return 1;
}
