/**
 * server.c, copyright 2001 Steve Gribble
 *
 * The server is a single-threaded program.  First, it opens
 * up a "listening socket" so that clients can connect to
 * it.  Then, it enters a tight loop; in each iteration, it
 * accepts a new connection from the client, reads a request,
 * computes for a while, sends a response, then closes the
 * connection.
 */

#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <ctype.h>
#include <errno.h>
#include <sys/time.h>

#include "SocketLibrary/socklib.h"
#include "common.h"
#include "threadpool.h"
#include <pthread.h>

extern int errno;

#define NUM_LOOPS 500000
typedef struct stuff2clean {
    char ** char1;
    char ** char2;
    int  socketno;
} stuff2clean;  


int num_loops;
int   setup_listen(char *socketNumber);
char *read_request(int fd);
char *process_request(char *request, int *response_length);
void  send_response(int fd, char *response, int response_length);
void dispatchable_function (void * arg);
void cleanup_dispatch(void *);
/**
 * This program should be invoked as "./server <socketnumber>", for
 * example, "./server 4342".
 */

int main(int argc, char **argv)
{
  char buf[1000];
  int  socket_listen;
  int  socket_talk;
  int  dummy, len;
  threadpool tp;
  int count;
  struct timeval tval,t2val;

  if (argc < 2 || argc > 4)
  {
    fprintf(stderr, "(SERVER): Invoke as  './server socknum [threadpoolsize] [num_loops]'\n");
    fprintf(stderr, "(SERVER): for example, './server 4434'\n");
    exit(-1);
  }

  if(argc > 2)
    {
     tp = create_threadpool(atoi(argv[2]));
    }
  else
    {
      tp = create_threadpool(10);
    }
  //fprintf(stderr,"Threadpool made\n");
  if(argc > 3)
    {
      //fprintf(stderr,"argv[2] =%s ,argv[3]=%s\n",argv[2],argv[3]);
      num_loops = atoi(argv[3]);
    }
  else
    {
      num_loops = NUM_LOOPS;
    }
  //fprintf(stderr,"%d Loops set\n",num_loops);

  /* 
   * Set up the 'listening socket'.  This establishes a network
   * IP_address:port_number that other programs can connect with.
   */
  socket_listen = setup_listen(argv[1]);

  /* 
   * Here's the main loop of our program.  Inside the loop, the
   * one thread in the server performs the following steps:
   * 
   *  1) Wait on the socket for a new connection to arrive.  This
   *     is done using the "accept" library call.  The return value
   *     of "accept" is a file descriptor for a new data socket associated
   *     with the new connection.  The 'listening socket' still exists,
   *     so more connections can be made to it later.
   *
   *  2) Read a request off of the listening socket.  Requests
   *     are, by definition, REQUEST_SIZE bytes long.
   *
   *  3) Process the request.
   *
   *  4) Write a response back to the client.
   *
   *  5) Close the data socket associated with the connection
   */
  count = 0;
  while(count < 1101) {


    socket_talk = saccept(socket_listen);  // step 1
    if (socket_talk < 0) {
      fprintf(stderr, "An error occured in the server; a connection\n");
      fprintf(stderr, "failed because of ");
      perror("");
      exit(1);
    }
    dispatch(tp,&dispatchable_function,(void *)socket_talk);
    count++;
    if(count == 1050)
      {
	gettimeofday(&tval,NULL);

      }
    if(count == 1100)
      {
	gettimeofday(&t2val,NULL);
	printf("%d.%d\n",t2val.tv_sec-tval.tv_sec,t2val.tv_usec-tval.tv_usec);
      }
    //printf("Through loop %d\n",count);
  }
}


/** function to dispatch
 */
void dispatchable_function (void * arg)
{
  char *request = NULL;
  char *response = NULL;
  int socket_talk = (int)arg;
  stuff2clean temp;
  temp.char1=&request;
  temp.char2=&response;  
  temp.socketno = socket_talk;

  pthread_cleanup_push(cleanup_dispatch,&temp);
  request = read_request(socket_talk);  // step 2
  if (request != NULL) {
    int response_length;
    
    response = process_request(request, &response_length);  // step 3
    if (response != NULL) {
	send_response(socket_talk, response, response_length);  // step 4
    }
  }
  pthread_exit(NULL);
  pthread_cleanup_pop(1);
}

void cleanup_dispatch(void * arg)
{
  stuff2clean *temp = (stuff2clean *)arg;
  close(temp->socketno); 
  // clean up allocated memory, if any
  if (*(temp->char1) != NULL)
    free(*(temp->char1));
  if (*(temp->char2) != NULL)
    free(*(temp->char2));
}

/**
 * This function accepts a string of the form "5654", and opens up
 * a listening socket on the port associated with that string.  In
 * case of error, this function simply bonks out.
 */

int setup_listen(char *socketNumber) {
  int socket_listen;

  if ((socket_listen = slisten(socketNumber)) < 0) {
    perror("(SERVER): slisten");
    exit(1);
  }

  return socket_listen;
}

/**
 * This function reads a request off of the given socket.
 * This function is thread-safe.
 */

char *read_request(int fd) {
  char *request = (char *) malloc(REQUEST_SIZE*sizeof(char));
  int   ret;

  if (request == NULL) {
    fprintf(stderr, "(SERVER): out of memory!\n");
    exit(-1);
  }

  ret = correct_read(fd, request, REQUEST_SIZE);
  if (ret != REQUEST_SIZE) {
    free(request);
    request = NULL;
  }
  return request;
}

/**
 * This function crunches on a request, returning a response.
 * This is where all of the hard work happens.  
 * This function is thread-safe.
 */


char *process_request(char *request, int *response_length) {
  char *response = (char *) malloc(RESPONSE_SIZE*sizeof(char));
  int   i,j;

  // just do some mindless character munging here

  for (i=0; i<RESPONSE_SIZE; i++)
    response[i] = request[i%REQUEST_SIZE];

  for (j=0; j<num_loops; j++) {
    for (i=0; i<RESPONSE_SIZE; i++) {
      char swap;

      swap = response[((i+1)%RESPONSE_SIZE)];
      response[((i+1)%RESPONSE_SIZE)] = response[i];
      response[i] = swap;
    }
  }
  *response_length = RESPONSE_SIZE;
  return response;
}

