#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <netdb.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include "../host_info.h" /* host info struct, TCP_HOSTS */

void str_cli(FILE *fp, int sockfd);
void writen(int fd, const void *buf, size_t count);

int main(int argc, char **argv) {
  int sock;
  struct sockaddr_in sa;
  struct in_addr inaddr;
  struct hostent *hostent;
  char buf[1024];
  struct host_info hosti;
  socklen_t optlen = sizeof(struct host_info);

  if (argc != 4) {
    printf("Usage: %s host port name\n", argv[0]);
    exit(1);
  }

  if ((sock = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
    printf("socket create error (%m)\n");
    return -1;
  }

  if ((hostent = gethostbyname(argv[1])) == NULL) {
    printf("gethostbyname error (%m)\n");
    return -1;
  }

  inaddr.s_addr = *(unsigned int*)(hostent->h_addr);

  sa.sin_family = AF_INET;
  sa.sin_addr = inaddr;
  sa.sin_port = htons(atoi(argv[2]));

  /* set "socket" option */
  hosti.snd_host_len = strlen(argv[3]);
  hosti.rcv_host_len = 7;
  memcpy(hosti.snd_host, argv[3], hosti.snd_host_len);
  memcpy(hosti.rcv_host, "rcv=bar", hosti.rcv_host_len);

  if (setsockopt(sock, SOL_TCP, TCP_HOSTS, &hosti, optlen) < 0) {
    printf("setsockopt() failed (%m)\n");
  }

  memset(&hosti, 0, sizeof (struct host_info));

  if (getsockopt(sock, SOL_TCP, TCP_HOSTS, &hosti, &optlen) < 0) {
    printf("getsockopt error (%m)\n");
  } else {
    memcpy(buf, hosti.snd_host, hosti.snd_host_len);
    buf[hosti.snd_host_len] = 0;
    printf("sockopt: snd_host = \"%s\"\n", buf);

    memcpy(buf, hosti.rcv_host, hosti.rcv_host_len);
    buf[hosti.rcv_host_len] = 0;
    printf("sockopt: rcv_host = \"%s\"\n", buf);
    printf("sockopt: snd_host_len = \"%d\"\n", hosti.snd_host_len);
    printf("sockopt: rcv_host_len = \"%d\"\n", hosti.rcv_host_len);
  }

  if (connect(sock, (struct sockaddr*)&sa, sizeof sa) < 0) {
    printf("connect error (%m)\n");
    close(sock);
    exit(1);
  }

  printf("Connected to %s (%s).\n", argv[1], inet_ntoa(inaddr));

  str_cli(stdin, sock);

  return 0;
}

#define MAXLINE 10

void str_cli(FILE *fp, int sockfd) {
  int maxfdp1;
  int read_;
  int fd;
  fd_set rset;
  char sendline[MAXLINE], recvline[MAXLINE];

  FD_ZERO(&rset);
  fd = fileno(fp);
  maxfdp1 = (fd > sockfd ? fd : sockfd) + 1;

  for (;;) {
    FD_SET(fd, &rset);
    FD_SET(sockfd, &rset);
    if (select(maxfdp1, &rset, NULL, NULL, NULL) < 0) {
      printf("select error (%m)\n");
      exit(1);
    }

    if (FD_ISSET(sockfd, &rset)) {
      if ((read_ = read(sockfd, recvline, MAXLINE)) == 0) {
        printf("\n\n\nstr_cli: server closed connection\n");
        exit(1);
      }

      if (read_ < 0) {
        printf("str_cli: read error (%m)\n");
        exit(1);
      }

      writen(fileno(stdout), recvline, read_);
    }

    if (FD_ISSET(fd, &rset)) {
      if (fgets(sendline, MAXLINE, fp) == NULL)
        return;

      writen(sockfd, sendline, strlen(sendline));
    }
  }
}

void writen(int fd, const void *buf, size_t count) {
  int written;

  while (count > 0) {
    if ((written = write(fd, buf, count)) < 0) {
      printf("writen: write error (%m)\n");
      exit(1);
    }

    buf += written;
    count -= written;
  }
}
