//--------------------------------------------------------------------------------------------------------------
// MICHAL SCHORM - xschor02@stud.fit.vutbr.cz
// IPK - 2. projekt - server
// Klient server přenos souborů
//--------------------------------------------------------------------------------------------------------------

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <limits.h>
#include <netdb.h>
#include <arpa/inet.h> //inet_addr

//--------------------------------------------------------------------------------------------------------------
//        MAKRA A HLAVIČKY FCÍ

#define DEBUG 1             // 1=true, 0=false
#define DEBUG_DATA 0 
#define ASCII_0 48 

#define CHYBA_ARGC           -1
#define CHYBA_SOCKET         -2
#define CHYBA_CONNECT        -3
#define CHYBA_SEND           -4
#define CHYBA_RECEIVE        -5
#define CHYBA_GETHOSTBYNAME  -6
#define CHYBA_ARGV           -7
#define CHYBA_BAD_HEAD       -8
#define CHYBA_UNKNOWN_DEMAND -9
#define CHYBA_NO_FILE        -10
#define CHYBA_IO_FILE_ERR    -11

#define UPLOAD     100
#define DOWNLOAD   101

void error_processing(int error_code);
void comm_service(int socket_fd);

//--------------------------------------------------------------------------------------------------------------
//        MAIN

int main (int argc, char *argv[])
{
 int port;
 // ------ arguments check ------------------ 
 if(argc != 3) error_processing(CHYBA_ARGC);
 if( strcmp(argv[1], "-p")==0) port = atoi(argv[2]);
 else error_processing(CHYBA_ARGV);
 // -----------------------------------------

 struct sockaddr_in serv_addr, cli_addr;
 int socket_fd = 0;
 int newsockfd = 0;
 socklen_t clilen;

 if(DEBUG) printf("\nStarting server ...\n");

 socket_fd = socket(AF_INET, SOCK_STREAM, 0);
    
 memset((char *) &serv_addr, 0, sizeof(serv_addr));
 serv_addr.sin_family = AF_INET;
 serv_addr.sin_addr.s_addr = INADDR_ANY;
 serv_addr.sin_port = htons(port);

 bind(socket_fd, (struct sockaddr *) &serv_addr, sizeof(serv_addr));
    
 //----------------------------

 while(1)
   {
    listen(socket_fd, 50);     // může postupně obsloužit až 50 klientů
      if(DEBUG) printf("Listenning ... \n");
      
    clilen = sizeof(cli_addr);
    newsockfd = accept(socket_fd, (struct sockaddr *) &cli_addr, &clilen);
      if(DEBUG) printf("Connection accepted ... \n");

    comm_service(newsockfd);
    close(newsockfd);
   }
 
 if(DEBUG) puts("Socket closed");
 close(socket_fd);
 return 0;
}

//--------------------------------------------------------------------------------------------------------------
//--------------------------------------------------------------------------------------------------------------
//--------------------------------------------------------------------------------------------------------------

//--------------------------------------------------------------------------------------------------------------
//        FCE ERROR PROCESSING

void error_processing(int error_code)
{
 fprintf(stderr, "\nChyba: ");
 switch(error_code)
   {
    case CHYBA_ARGC :
     fprintf(stderr, "\n\tNeplatný počet argumentů! \n\n");
     break;
   case CHYBA_SOCKET :
     fprintf(stderr, "\n\tNelze vytvořit socket! \n\n");
     break;
   case CHYBA_CONNECT :
     fprintf(stderr, "\n\tNelze navázat nebo udržet spojení s klientem! \n\n");
     break;
   case CHYBA_SEND :
     fprintf(stderr, "\n\tNelze odeslat data serveru! \n\n");
     break;
   case CHYBA_RECEIVE :
     fprintf(stderr, "\n\tNepodařilo se získat data ze serveru! \n\n");
     break;     
   case CHYBA_GETHOSTBYNAME :
     fprintf(stderr, "\n\tNepodařilo se převést URL na IP adresu! \n\n");
     break;            
   case CHYBA_ARGV :
     fprintf(stderr, "\n\tChybně zadané parametry! \n\n");
     break; 
   case CHYBA_BAD_HEAD :
     fprintf(stderr, "\n\tChybná hlavička! \n\n");
     break; 
   case CHYBA_UNKNOWN_DEMAND :
     fprintf(stderr, "\n\tChybný požadavek! \n\n");
     break;      
   case CHYBA_NO_FILE :
     fprintf(stderr, "\n\tPožadovaný soubor neexistuje nebo je prázdný! \n\n");  // a prázdné soubory nemá smysl přenášet
     break;      
   case CHYBA_IO_FILE_ERR :
     fprintf(stderr, "\n\tChyba při prci se souborem! \n\n");
     break;      


   default :
     fprintf(stderr, "UNKNOWN ERROR\n\n");
  }
 exit(1); 
}

//--------------------------------------------------------------------------------------------------------------
//        FCE COMM SERVICE

void comm_service(int socket_fd)
{
   // -1 Socket closed
   // -2 Socket read error

   char bufferIn[1025];
   bufferIn[0] = '\0';
   char bufferOut[1025];
   bufferOut[0] = '\0';
   char filename[1025];
   filename[0] = '\0';

   int bytesread, i, size, operation;



    // READ PROTOCOL HEAD
    bytesread = read(socket_fd, bufferIn, strlen("PROTOCOL XSCHOR02-IPK-FIT-2016 CLIENT\n"));
    if(bytesread <= 0) error_processing(CHYBA_CONNECT);
    bufferIn[bytesread] = '\0';
    if(strcmp(bufferIn,"PROTOCOL XSCHOR02-IPK-FIT-2016 CLIENT\n")!=0) error_processing(CHYBA_BAD_HEAD);
    if(DEBUG) printf("   > Comm with client established\n");
    // READ DEMAND
    bytesread = read(socket_fd, bufferIn, strlen("DW_LOAD\n"));
    if(bytesread <= 0) error_processing(CHYBA_CONNECT);
    bufferIn[bytesread] = '\0';
    if(strcmp(bufferIn,"DW_LOAD\n")==0) {operation=DOWNLOAD; if(DEBUG) printf("   > Client want to DOWN-load file\n"); }
    else if(strcmp(bufferIn,"UP_LOAD\n")==0) {operation=UPLOAD; if(DEBUG)  printf("   > Client want to UP-load file\n"); }
    else error_processing(CHYBA_UNKNOWN_DEMAND);
    // READ FILE NAME    
    for(bytesread=0, i=0 ; 1 ; i++)
      {
       bytesread = read(socket_fd, (char *)(filename+i*sizeof(char)), sizeof(char));
       if(bytesread <= 0) error_processing(CHYBA_CONNECT);
       else if( *(filename+i*sizeof(char))=='\n' ){filename[i] = '\0'; break;}
      }
    if(DEBUG) printf("   > Filename: \"%s\"\n", filename);  
    // READ FILE SIZE    
    for(bytesread=0, i=0 ; 1 ; i++)
      {
       bytesread = read(socket_fd, (char *)(bufferIn+i*sizeof(char)), sizeof(char));
       if(bytesread <= 0) error_processing(CHYBA_CONNECT);
       else if( *(bufferIn+i*sizeof(char))=='\n' ){bufferIn[i] = '\0'; break;}
      }
    if(bufferIn[0]=='?') size = 0;
    else { size=atoi(bufferIn); if(size==0){error_processing(CHYBA_NO_FILE);} }
    if(DEBUG) printf("   > Size: \"%d\"\n", size);      
    // --------------------------
    
    
    
    int znak, result;
    FILE * fp;
    // -------------------- DOWNLOAD ----------------------------
    if(operation==DOWNLOAD)
      {
       fp = fopen(filename, "r");
       if(fp==NULL){error_processing(CHYBA_IO_FILE_ERR);}
       fseek(fp, 0L, SEEK_END);
       size = ftell(fp);
       rewind(fp);
       
       for(i=0; (unsigned int)i<strlen(bufferIn); i++){ bufferIn[i]='\0'; }
       sprintf(bufferIn, "%d", size);
        
       strcpy(bufferOut, "PROTOCOL XSCHOR02-IPK-FIT-2016 SERVER\n");    // PROTOCOL
       strcat(bufferOut, "DW_LOAD");                                    // OPERATION
       strcat(bufferOut, "\n"); 
       strcat(bufferOut, filename);                                     // FILENAME
       strcat(bufferOut, "\n");
       strcat(bufferOut, bufferIn);                                     // FILE SIZE
       strcat(bufferOut, "\n\0");
       
       if( send(socket_fd , bufferOut , strlen(bufferOut) , 0) < 0) error_processing(CHYBA_SEND);
       
       for(i=0; i<size; i++)
         {
          znak = fgetc(fp);
          if( send(socket_fd , (char *)(& znak) , sizeof(int) , 0) < 0) error_processing(CHYBA_SEND);
         }
       fclose(fp);
      }
    // -------------------- UPLOAD ----------------------------
    else if(operation==UPLOAD)  
      {
       strcpy(bufferOut, "PROTOCOL XSCHOR02-IPK-FIT-2016 SERVER\n");    // PROTOCOL
       strcat(bufferOut, "UP_LOAD");                                    // OPERATION
       strcat(bufferOut, "\n"); 
       strcat(bufferOut, filename);                                     // FILENAME
       strcat(bufferOut, "\n");
       strcat(bufferOut, "?");                                          // FILE SIZE
       strcat(bufferOut, "\n\0");
       if( send(socket_fd , bufferOut , strlen(bufferOut) , 0) < 0) error_processing(CHYBA_SEND);
       
       
       fp = fopen("temorary-file-for-server-content.txt", "w");

       while(1)
         {
          result = recv(socket_fd , (int *)(& znak) , sizeof(int) , 0);
          if( result < 0) error_processing(CHYBA_RECEIVE);
          if( result == 0) break;
          fputc(znak, fp);
         }
       fclose(fp);
        
       if( access( filename, F_OK ) != -1 ) remove(filename);
       rename("temorary-file-for-server-content.txt", filename);
      }
    else {error_processing(CHYBA_UNKNOWN_DEMAND);}
      

   if(DEBUG) printf("\n\n");
}