// from: http://beej.us/guide/bgnet/html/#a-simple-stream-client

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

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <poll.h>
#include <errno.h>
#include <string.h>
#include <netdb.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <sys/socket.h>

#include <arpa/inet.h>

#define MAXDATASIZE 100 // 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);
}

int do_connection(int argc, char * argv[]) {
  int sockfd;
  
    struct addrinfo hints, *servinfo;
    int rv;

    memset(&hints, 0, sizeof hints);
    hints.ai_family = AF_INET; // AF_UNSPEC;

    if (strcmp(argv[3], "UDP") == 0) 
      hints.ai_socktype = SOCK_DGRAM;
    else
      hints.ai_socktype = SOCK_STREAM;
    
    //printf("Trying to connect to %s on port %s\n", argv[1], argv[2]);
    if ((rv = getaddrinfo(argv[1], argv[2], &hints, &servinfo)) != 0) {
        fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(rv));
        return 1;
    }

    // note: better to loop through servinfo...

    if ((sockfd = socket(servinfo->ai_family, servinfo->ai_socktype,
			 servinfo->ai_protocol)) == -1) {
      perror("client: socket");
      exit(1);
    }
    //printf("Socket created. sockfd: %d\n", sockfd);

    if (connect(sockfd, servinfo->ai_addr, servinfo->ai_addrlen) != 0) {
      close(sockfd);
      perror("client: connect, nothing listeing on that port??");
      exit(1);
    }

    //printf("Connection success.\n");
    freeaddrinfo(servinfo); // all done with this structure

    return sockfd;
}

int main(int argc, char *argv[])
{
    int sockfd;  
    char buf[MAXDATASIZE];

    if (argc < 5) {
        fprintf(stderr,"usage: send_file hostname port TCP|UDP filename\n"
		" filename - file to send\n"
		" TCP|UDP - specify either TCP or UDP\n");
        exit(1);
    }

    // argv[1] is hostname, argv[2] is port
    sockfd = do_connection(argc, argv);

    // we have a socket that we can send on.
    // send a file, or stdin
    FILE *f = fopen(argv[4], "r");
    if (f == NULL) {
      fprintf(stderr, "Unable to open file %s for reading.\n", argv[4]);
      exit(0);
    }
    //printf("Opened file for reading, success.\n");

    size_t result;
    
    // if UDP first send total # of bytes
    if (strcmp(argv[3], "UDP") == 0) {
      long int total_bytes = 100;
      
      // set total_bytes to file size of the file
      fseek(f, 0L, SEEK_END); // go to end of file
      total_bytes = ftell(f); // how many bytes is that
      rewind(f);              // set back to beginning of file
	
      result = send(sockfd, &total_bytes, sizeof(total_bytes), 0);
      if (result != sizeof(total_bytes)) {
	fprintf(stderr, "Not able to send all stuf...\n");
	exit(0);
      }
    }

    // loop through reading the file and sending on the socket
    // sequentially.
    while (1) {
      // read from the file
      result = fread(buf, sizeof(char), MAXDATASIZE, f);
      //printf("fread result %lu\n", result);
      if (result <= 0) {
	break;
      }

      //printf("Read %lu bytes from file.\n", result);
      
      // send on the socket
      ssize_t num_sent = send(sockfd, buf, result, 0);
      //printf("Sent %lu bytes.\n", num_sent);
      
      if (num_sent != result) {
	fprintf(stderr, "Unable to send all bytes. Tried %lu, sent %lu\n", result, num_sent);
      }
    }

    close(sockfd);
    fclose(f);

    return 0;
}