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

static void bail(const char *on_what) {
  if ( errno != 0 ) {
    fputs(strerror(errno),stderr);
    fputs(": ",stderr);
  }
  fputs(on_what,stderr);
  fputc('\n',stderr);
  exit(1);
}



/*
  openSocket() retourne
    l'identifiant d'un socket associé à l'adresse IP srvr_addr:srvr_port
    -1 si impossibilit? de créer le socket
*/

int openSocket(char* srvr_addr,char* srvr_port) {

  int socketid = 0;
  struct sockaddr_in socketAdresse;
  int taille;
  int test;

  // On met les parametres par defaut si y a besoin
  if(srvr_addr == "")
    srvr_addr = "127.0.0.1";
  if (srvr_port == "")
    srvr_port = "8080";
  
  // On cree le socket reseau, TCP
  socketid = socket(PF_INET,SOCK_STREAM,0);
  if ( socketid == -1)
    bail ("socket()");
  
  // On reserve l'espace memoire pour les parametres du socket
  memset(&socketAdresse,0,sizeof socketAdresse);
     
  // On renseigne les differents champs
  socketAdresse.sin_family = AF_INET;
  // L'adresse IP
  socketAdresse.sin_addr.s_addr = inet_addr(srvr_addr);
  // Le port TCP
  socketAdresse.sin_port = htons (atoi(srvr_port));
  // Taille de socketAdresse
  taille = sizeof socketAdresse;

  // On specifie les parametres au socket
  test = bind (socketid,(struct sockaddr *) &socketAdresse,taille);
  if ( test == -1 )
    bail("bind()");

  // On retourne l'identifiant du socket
  return socketid;
}



/*
  Sachant que input_stream contient une chaine du type   GET /[nom_du_fichier] HTTP/1.0
  getFileName() retourne :
     nom_du_fichier si celui ci est precisé
     index.html sinon
*/

char* getFileName(FILE* input_stream) {

  int i;
  char* nomfichier;

  /* initialisation d'une variable temporaire à 0 pour
     éviter les erreurs de fin de chaînes de caractère */
  char* temp = malloc(150*(sizeof(char)));

  for(i=0;i<150;i++)
    temp[i]=0;
  
  // On recupere la chaine de caractere
  fgets(temp,150,input_stream);
  
  // On enleve le debut : "GET /" soit 5 caracteres
  temp = strdup(temp+5);

  // On recupere le nom de la ressource : la chaine avant le premier espace
  nomfichier = strsep (&temp," ");
  
  // On teste si la chaine est vide
  if (strcmp(nomfichier,"")==0)
    strcpy(nomfichier,"index.html\0");
  
  // on affiche du côté serveur le nom du fichier qui sera retourné
  printf ("nomfichier = %s\n",nomfichier);

  // On retourne le nom du fichier
  return nomfichier;
  
}




/*
  sendFile() copie les donn?es contenues dans le fichier r?f?renc? par file_name (si ce dernier existe)
  sinon ?crit un message d'erreur 404  dans output_stream
*/

void sendFile(FILE* output_stream, char* file_name) {
  FILE* descripteurFichier;
  char caractereLu;
  int test;
  
  // Variable ne servant qu'a la copie ligne a ligne
  // char* chaineLu;

  // Ouverture du fichier
  descripteurFichier = fopen(file_name,"rb");
  
  // Si le fichier n'existe pas, on retourne une erreur 404
  if (access(file_name, F_OK)<0)){
    fputc('4',output_stream);
    fputc('0',output_stream);
    fputc('4',output_stream);
  }

  // Si le fichier existe 
  else {
    do {
      // on lit dans le fichier
      caractereLu = fgetc(descripteurFichier);
      
      // on copie caractere a caractere dans output_stream
      fputc(caractereLu,output_stream);
      
      /* Code pour la copie ligne a ligne(ne marche pas)
	 fgets(chaineLu,200,descripteurFichier);
	 gputs(output_stream,chaineLu);
	while(chaineLu != NULL);
      */
    }
    // jusqu'a ce qu'on rencontre le caractere de fin de fichier
    while(caractereLu != EOF);
  }
  // On ferme le fichier
  fclose(descripteurFichier);
}

void do_process(int client_socket) {

  FILE* output_stream;
  FILE* input_stream;
  char* file_name;

  input_stream=fdopen(client_socket,"r");
  if (input_stream!=NULL)  {
    output_stream=fdopen(dup(client_socket),"w");
    if (output_stream!=NULL)   {
      file_name=getFileName(input_stream) ;
      if (file_name!=NULL) {
        sendFile(output_stream,file_name);
        free(file_name);
      }
      fclose(input_stream);
      fclose(output_stream);
    } else {
      fclose(input_stream);
    }
  }
}

int main(int argc,char **argv) {
  int socket = -1;
  int client_socket = -1;
  pid_t PID;
  int len_inet;
  struct sockaddr_in adr_clnt;
  
  socket=openSocket("127.0.0.1","8080");
  if ( socket == -1 )
    bail("openSocket()");

  if ( listen(socket,10)==-1 )
    bail("listen()");

  for (;;) {
    len_inet = sizeof adr_clnt;
    client_socket = accept(socket,(struct sockaddr *) &adr_clnt, &len_inet);
    if ( client_socket == -1 )
      bail("accept()");
    if ( (PID = fork()) == -1 ) {
      close(client_socket);
      continue;
    } else if ( PID > 0 ) {
      close(client_socket);
      continue;
    }
    do_process(client_socket);
    exit(0);
  }
  close(socket);
  return 0;
}
