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

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

#include <errno.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>

#define MSG_NOBLOCK 0x01

/* 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"
	  "-a <ip address>: server address (INADDR_ANY if omitted)\n"
	  "-p <port>: server port number\n"
	  "-o <outfile>: file to be received over TCP\n"
	  "-s <chunk size (bytes)>: max # of bytes to read from the TCP socket at a time, default=1024(bytes)\n"
	  "-t <wait time (ms)>: wait time betwen two Read() calls, default=0\n\n",
	  argv[0]);
}

int Main(int argc, char **argv) {
  int sockfd, fd;
  struct sockaddr_in srv;
/*   struct sockaddr_in cli; */
  struct stat sb;
/*   int clilen; */
  char ch;
  char *buf;
  int buflen = 1024;
  int wait_time = 0;

  bzero(&srv, sizeof(struct sockaddr_in));
  srv.sin_family = AF_INET;
  srv.sin_port = htons(0);
  srv.sin_addr.s_addr = htonl(INADDR_ANY);
  // srv.sin_addr.s_addr = htonl(0x01010102);
  
  while((ch = getopt(argc, argv, "o:p:s:t:a:")) != -1) {
    switch(ch) {
    case 'o':
      if(stat(optarg, &sb) > 0 || errno != ENOENT) {
	fprintf(stderr,	"file %s exists.  exiting...\n",
		optarg);
	exit(1);
      }
      if((fd = open(optarg, O_CREAT | O_WRONLY, 0666)) < 0) {
	perror("open");
	exit(1);
      }
      break;
    case 'p':
      srv.sin_port = htons(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 't':
      wait_time = atoi(optarg);
      break;
    case 's':
      buflen = atoi(optarg);
      break;
    default:
      usage(argc, argv);
      exit(1);
    }
  }
  
  if(argc != optind) {
    usage(argc, argv);
    exit(1);
  }
  
  if(srv.sin_port == 0) {
    usage(argc, argv);
    exit(1);
  }

  printf("SOCK %u %u\n", SOCK_STREAM, SOCK_DGRAM);

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

  if(Bind(sockfd, (struct sockaddr*) &srv, sizeof(srv)) < 0) {
    perror("bind");
    exit(1);
  }

  /* allocate buffer */
  if ((buf = malloc(buflen+1)) == NULL) {
    fprintf(stderr, "out of memory\n");
    exit(1);
  }
  //  struct sockaddr* fromAddr = (struct sockaddr*)malloc(sizeof(struct sockaddr));
  int fromLen = sizeof(struct sockaddr_in);

  while(1) {
    int nbytes;
    

    if((nbytes = Recvfrom(sockfd, buf, buflen, MSG_NOBLOCK, NULL, &fromLen)) < 0) {
      perror("read");
      exit(1);
    }
    
    fprintf(stderr, "read %d bytes\n", nbytes); 
    

    if(nbytes == 0) {
          Sleep(1000);
	  continue;
    }
    if(nbytes == 100) break;

    buf[nbytes] = 0;
    // fprintf(stderr, "%s", buf);
    
    if(write(fd, buf, nbytes) < 0) {
      perror("write");
      exit(1);
    }
    
    fprintf(stderr, "sleeping for %d msec\n", wait_time);
    Sleep(wait_time);
  }
	  
  fprintf(stderr, "done, Closing socket...\n");
  close(fd);
  Close(sockfd);
  free(buf);
  return 0;
}
