/*
 * client.c -- a stream socket client demo
 */

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <netdb.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <netinet/tcp.h>
#include <sys/socket.h>
#include <unistd.h>
#include <arpa/inet.h>

extern char *optarg;
extern int optind;
extern int optopt;
extern int opterr;
extern int optreset;

#define PORT "3490" // the port client will be connecting to 

#define MAXDATASIZE 1024 // max number of bytes we can get at once 

// get sockaddr, IPv4 or IPv6:
void *get_in_addr(struct sockaddr *sa)
{
    if (sa->sa_family == AF_INET) {
        return &(((struct sockaddr_in*)sa)->sin_addr);
    }

    return &(((struct sockaddr_in6*)sa)->sin6_addr);
}

void
usage()
{
  fprintf(stderr,"usage: client hostname\n") ;
  fprintf(stderr,"  Options:\n") ;
  fprintf(stderr,"    -d secs - TCP_DEFER_ACCEPT set to secs\n");
  fprintf(stderr,"    -w secs - sleep for secs seconds before sending to the server\n") ;
}

int main(int argc, char *argv[])
{
  int sockfd, numbytes;  
  char buf[MAXDATASIZE];
  struct addrinfo hints, *servinfo, *p;
  int rv;
  char s[INET6_ADDRSTRLEN];
  int sleeptimer =  0 ;
  int apt = 0;
  int bflag = 0;
  char ch ;
  
  // get command line args  
  while ((ch = getopt(argc, argv, "d:w:")) != -1) {
    switch (ch) {
    case 'd':
      apt = atoi(optarg);
      break;
    case 'w':
      sleeptimer = atoi(optarg) ;
      break;
    case '?':
    default:
      usage();
      }
    }
  argc -= optind;
  argv += optind;

  memset(&hints, 0, sizeof hints);
  hints.ai_family = AF_UNSPEC;
  hints.ai_socktype = SOCK_STREAM;

  printf("optind = %d\nConnection to %s\n",optind,argv[0]) ;

  if ((rv = getaddrinfo(argv[0], PORT, &hints, &servinfo)) != 0) {
    fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(rv));
    return 1;
    }

  // loop through all the results and connect to the first we can
  for(p = servinfo; p != NULL; p = p->ai_next) {
    if ((sockfd = socket(p->ai_family, p->ai_socktype,
                p->ai_protocol)) == -1) {
      perror("client: socket");
      continue;
      }

#ifdef TCP_DEFER_ACCEPT
    if (apt > 0) {
      if (setsockopt(sockfd, IPPROTO_TCP, TCP_DEFER_ACCEPT, &apt,
                  sizeof(int)) == -1) {
        perror("setsockopt");
        exit(1);
        }
      printf("TCP_DEFER_ACCEPT set to %d seconds\n",apt) ;
      }
#endif

    if (connect(sockfd, p->ai_addr, p->ai_addrlen) == -1) {
      close(sockfd);
      inet_ntop(p->ai_family, get_in_addr((struct sockaddr *)p->ai_addr),s, sizeof s);
      printf("client: connection to %s\n", s);
      perror("client: connect fail: ");
      continue;
      }

    break;
    }

  if (p == NULL) {
    fprintf(stderr, "client: failed to connect\n");
    return 2;
    }



  inet_ntop(p->ai_family, get_in_addr((struct sockaddr *)p->ai_addr),s, sizeof s);
  printf("client: connected to %s\n", s);

  freeaddrinfo(servinfo); // all done with this structure

  if (sleeptimer > 0) {
    printf("SLEEP for %d seconds\n",sleeptimer) ;
    sleep(sleeptimer) ; 
    printf("Done!\n") ;
    }
  if (send(sockfd, "XXXXXXXXXXX!  ", 13, 0) == -1)
    perror("send");
  if ((numbytes = recv(sockfd, buf, MAXDATASIZE-1, 0)) == -1) {
    perror("recv");
    exit(1);
    }

  buf[numbytes] = '\0';
  printf("client: received '%s'\n",buf);
  close(sockfd);
  return 0;
}
