// from: http://beej.us/guide/bgnet/html/#blocking

/*
** pollserver.c -- a cheezy multiperson chat server
*/

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

#define PORT "47380"   // Port we're listening on

// 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);
}

// Return a listening socket
int get_listener_socket(void)
{
    int listener;     // Listening socket descriptor
    int yes=1;        // For setsockopt() SO_REUSEADDR, below
    int rv;

    struct addrinfo hints, *ai, *p;

    // Get us a socket and bind it
    memset(&hints, 0, sizeof hints);
    hints.ai_family = AF_UNSPEC;
    hints.ai_socktype = SOCK_STREAM;
    hints.ai_flags = AI_PASSIVE;
    if ((rv = getaddrinfo(NULL, PORT, &hints, &ai)) != 0) {
        fprintf(stderr, "selectserver: %s\n", gai_strerror(rv));
        exit(1);
    }
    
    for(p = ai; p != NULL; p = p->ai_next) {
        listener = socket(p->ai_family, p->ai_socktype, p->ai_protocol);
        if (listener < 0) { 
            continue;
        }
        
        // Lose the pesky "address already in use" error message
        setsockopt(listener, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(int));

        if (bind(listener, p->ai_addr, p->ai_addrlen) < 0) {
            close(listener);
            continue;
        }

        break;
    }

    freeaddrinfo(ai); // All done with this

    // If we got here, it means we didn't get bound
    if (p == NULL) {
        return -1;
    }

    // Listen
    if (listen(listener, 10) == -1) {
        return -1;
    }

    return listener;
}

// Add a new file descriptor to the set
void add_to_pfds(struct pollfd *pfds[], int newfd, int *fd_count, int *fd_size)
{
    // If we don't have room, add more space in the pfds array
    if (*fd_count == *fd_size) {
        *fd_size *= 2; // Double it

        *pfds = realloc(*pfds, sizeof(**pfds) *       (*fd_size));
	//                     sizeof(struct pollfd)
    }

    (*pfds)[*fd_count].fd = newfd;
    (*pfds)[*fd_count].events = POLLIN; // Check ready-to-read

    (*fd_count)++;
}

// Remove an index from the set
void del_from_pfds(struct pollfd pfds[], int i, int *fd_count)
{
    // Copy the one from the end over this one
    pfds[i] = pfds[*fd_count-1];

    (*fd_count)--;
}

// Main
int main(void)
{
    int listener;     // Listening socket descriptor

    int newfd;        // Newly accept()ed socket descriptor
    struct sockaddr_storage remoteaddr; // Client address
    socklen_t addrlen;

    char buf[256];    // Buffer for client data

    char remoteIP[INET6_ADDRSTRLEN];

    // Start off with room for 5 connections
    // (We'll realloc as necessary)
    int fd_count = 0;
    int fd_size = 5;
    struct pollfd *pfds = malloc(sizeof *pfds * fd_size);

    // Set up and get a listening socket
    listener = get_listener_socket(); // getaddrinfo, socket, bind, listen

    if (listener == -1) {
        fprintf(stderr, "error getting listening socket\n");
        exit(1);
    }

    // Add the listener to set
    pfds[0].fd = listener;
    pfds[0].events = POLLIN; // Report ready to read on incoming connection

    fd_count = 1; // For the listener

    // hack - assume only one client for now...
    char *data_buffer = NULL;   // everything we have received and not yet dealt with
    int data_buffer_length = 0; // total amount we have received

    // Main loop.  probably there is accept.  probably also recv (then send).  probably poll.
    for(;;) {
        int poll_count = poll(pfds, fd_count, -1);

        if (poll_count == -1) {
            perror("poll");
            exit(1);
        }

        // Run through the existing connections looking for data to read
        for(int i = 0; i < fd_count; i++) {

            // Check if someone's ready to read
            if (pfds[i].revents & POLLIN) { // We got one!!

	      if (pfds[i].fd == listener) { // could have used, if (i == 0) 
                    // If listener is ready to read, handle new connection

                    addrlen = sizeof remoteaddr;
                    newfd = accept(listener,
                        (struct sockaddr *)&remoteaddr,
                        &addrlen);

                    if (newfd == -1) {
                        perror("accept");
                    } else {
                        add_to_pfds(&pfds, newfd, &fd_count, &fd_size);

                        printf("pollserver: new connection from %s on "
                            "socket %d\n",
                            inet_ntop(remoteaddr.ss_family,
                                get_in_addr((struct sockaddr*)&remoteaddr),
                                remoteIP, INET6_ADDRSTRLEN),
                            newfd);

			// send message to this client - "welcome"?
			// send message to all the clients that there is a new one?
                    }
                } else {
                    // If not the listener, we're just a regular client
                    int nbytes = recv(pfds[i].fd, buf, sizeof buf, 0);

                    int sender_fd = pfds[i].fd;

                    if (nbytes <= 0) {
                        // Got error or connection closed by client
                        if (nbytes == 0) {
                            // Connection closed
                            printf("pollserver: socket %d hung up\n", sender_fd);
                        } else {
                            perror("recv");
                        }

                        close(pfds[i].fd); // Bye!

                        del_from_pfds(pfds, i, &fd_count);

                    } else {
		      // debug print, what we received
		      buf[nbytes] = '\0';
		      printf("received: \n%s\n", buf);

		      // add in buf (nbytes) into data_buffer

		      // realloc data_buffer to be bigger, and strcpy the new stuff
		      data_buffer = realloc(data_buffer, nbytes + data_buffer_length);
		      strncpy(data_buffer+data_buffer_length, buf, nbytes);
		      data_buffer_length += nbytes;

		      // check for \r\n\r\n
		      int end_of_header_found = 0;
		      for(int i=0; i < data_buffer_length-4; i++) {
			if (strncmp("\r\n\r\n", data_buffer+i,4) == 0) {
			  end_of_header_found = 1;
			  break;
			}
		      }

		      // should be printing a complete header but isn't, so fix/debug that...

		      if (end_of_header_found) {
			printf("received complete header...\n");
			data_buffer[data_buffer_length+1] = '\0';
			printf("%s", data_buffer);

			free(data_buffer);
			data_buffer = NULL;
			data_buffer_length = 0;
		      }
		      
		      /*
		      // if GET
		      if (strncmp(buf,"GET ",4) == 0) {
			// pull out the first second "word"
			// GET /info/index.php HTTP/1.1
			//     ---------------|
			//     |              |
			//     relative_path  |
			//                    space
			char *relative_path = buf+4;
			char *space = strstr(relative_path," "); // first space in relative_path
			if (space != NULL)
			  *space = '\0'; // now if I use relative_path, it's just the part before the space
			printf("requested thing: %s\n", relative_path);

			char *data = (char *) malloc(10000);
			snprintf(data, 9999, "requested: %s", relative_path);
			
			// if request was ... then ->

			// /time -> reply with time
			if (strcmp(relative_path, "/time") == 0) {
			  time_t now; time(&now);
			  snprintf(data, 9999, "%s", ctime(&now));
			}

			// /uptime -> reply uptime

			// /fortune -> reply fortune

			char *response = (char *) malloc(10000);

			snprintf(response, 9999,
				 "HTTP/1.1 200 OK\r\n"
				 "Date: Fri, 31 Dec 1999 23:59:59 GMT\r\n"
				 "Content-Length: %lu\r\n"
				 "Content-Type: text/plain\r\n"
				 "\r\n"
				 "%s", strlen(data), data);
			if (send(sender_fd, response, strlen(response), 0) == -1) {
			  perror("send");
			}
			free(response);
			free(data);
		      }*/
		    }
	      } // END handle data from client
            } // END got ready-to-read from poll()
        } // END looping through file descriptors
    } // END for(;;)--and you thought it would never end!
    
    return 0;
}