//--------------------------------------------------------------------------------------------------------------
// MICHAL SCHORM - xschor02@stud.fit.vutbr.cz
// IPK - 2. projekt - klient
// 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 SERVER_ERR           -7
#define CHYBA_ARGV           -8
#define CHYBA_BAD_HEAD       -9
#define CHYBA_UNKNOWN_DEMAND -10
#define CHYBA_NO_FILE        -11
#define CHYBA_IO_FILE_ERR    -12

#define UPLOAD 100
#define DOWNLOAD 101

void error_processing(int error_code);
int get_ip_servername_file_from_hostname(char url[128], char * ip_adresa);
int check_connection(int socket_fd, struct sockaddr_in server, int index_operation, char * requested_file);
//--------------------------------------------------------------------------------------------------------------
//        MAIN

int main (int argc, char *argv[])
{
 // --------------- arguments check
 if(argc != 7) error_processing(CHYBA_ARGC);
 int port = -1;
 int index_host = -1;
 int index_operation = -1;
 char requested_file[128];
 char save_file_name[128];
 
 if( strcmp(argv[1], "-p")==0) port = atoi(argv[2]);
 else if( strcmp(argv[3], "-p")==0) port = atoi(argv[4]);
 else if( strcmp(argv[5], "-p")==0) port = atoi(argv[6]);
 else error_processing(CHYBA_ARGV);
 if( strcmp(argv[1], "-h")==0) index_host=2;
 else if( strcmp(argv[3], "-h")==0) index_host=4;
 else if( strcmp(argv[5], "-h")==0) index_host=6;
 else error_processing(CHYBA_ARGV);
 if( strcmp(argv[1], "-d")==0 ){ index_operation=DOWNLOAD; strcpy(requested_file, argv[2]); strcpy(save_file_name, argv[2]);}
 else if( strcmp(argv[3], "-d")==0 ){ index_operation=DOWNLOAD; strcpy(requested_file, argv[4]); strcpy(save_file_name, argv[4]);}
 else if( strcmp(argv[5], "-d")==0 ){ index_operation=DOWNLOAD; strcpy(requested_file, argv[6]); strcpy(save_file_name, argv[6]);}
 else if( strcmp(argv[1], "-u")==0 ){ index_operation=UPLOAD; strcpy(requested_file, argv[2]); strcpy(save_file_name, argv[2]);}
 else if( strcmp(argv[3], "-u")==0 ){ index_operation=UPLOAD; strcpy(requested_file, argv[4]); strcpy(save_file_name, argv[4]);}
 else if( strcmp(argv[5], "-u")==0 ){ index_operation=UPLOAD; strcpy(requested_file, argv[6]); strcpy(save_file_name, argv[6]);}
 else error_processing(CHYBA_ARGV); 
 
 
 // --------------- arguments check
 
 int socket_fd;
 struct sockaddr_in server;
 char server_address[128];
// char http_head_buffer[1024];
 
 get_ip_servername_file_from_hostname(argv[index_host], server_address);
 
 //if(DEBUG) printf("\n\nserver:'%s'\nfile:'%s'\n\n", server_address, requested_file);
 
 // --------------- příprava socketu
 socket_fd = socket(AF_INET , SOCK_STREAM , 0);
 if(socket_fd == -1) error_processing(CHYBA_SOCKET);
 if(DEBUG) puts("Socket created");
 
 server.sin_addr.s_addr = inet_addr(server_address);
 server.sin_family = AF_INET;
 server.sin_port = htons(port);

 if(server.sin_port == htons(port)){}  //compiler

 // --------------- test spojení, protokolu, atd
 int protocol_error = check_connection(socket_fd, server, index_operation, requested_file);
 
 // --------------- 
 if(protocol_error == SERVER_ERR) error_processing(SERVER_ERR);
 
 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 spojení se serverem! \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 SERVER_ERR :
     fprintf(stderr, "\n\tChyba serveru \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 GET_IP_SERVERNAME_FILE_FROM_HOSTNAME
 
int get_ip_servername_file_from_hostname(char url[128], char * ip_adresa)
{
 
 struct hostent *he;
 struct in_addr **addr_list;
 
 // check for IP
 int count_dots = 0;
 int chars = 0;
 int last_num = 1;
 unsigned int i;
 
 for(i=0; i<strlen(url); i++)
   {
    if( url[i]=='.' && last_num==1)
      {
       count_dots++;
       last_num=0;
      }
    else if( url[i]=='.' && last_num==0) break;
    else if( isdigit(url[i])==0 )
      {
       chars++;
       break;
      }
    else last_num = 1;
   }
   
 if( last_num==1 && chars==0 && (count_dots==3 || count_dots==5) )
   {
    strcpy(ip_adresa, url);
    return 0;
   }  
 // ------------------------------------
 
          
 if ( (he = gethostbyname( url )) == NULL ) error_processing(CHYBA_GETHOSTBYNAME); 
 addr_list = (struct in_addr **) he->h_addr_list;
 strcpy(ip_adresa, inet_ntoa(*addr_list[0]) );

 return 0;
}     


//--------------------------------------------------------------------------------------------------------------
//        FCE CHECK_CONNECTION

//int check_connection(int socket_fd, struct sockaddr_in server, char * server_reply, char * http_head_buffer, char * method, char * requested_file, char * protocol, char * server_name)
int check_connection(int socket_fd, struct sockaddr_in server, int index_operation, char * requested_file)
{
 FILE * fp;
 
 char server_state[30];
 server_state[0] = '\0';
 char bufferIn[1025];
 bufferIn[0] = '\0';
 char filename[1025];
 filename[0] = '\0';
 int bytesread, i, size, size2;
 char bufferOut[1024];
 for(i=0; (unsigned int)i<strlen(bufferOut); i++){ bufferOut[i]='\0'; }
 
 if (connect(socket_fd , (struct sockaddr *)&server , sizeof(server)) < 0) error_processing(CHYBA_CONNECT);
 if(DEBUG) puts("Connected"); 

 // Comm via protocol ----------------------------------------------------- 
 strcpy(bufferOut, "PROTOCOL XSCHOR02-IPK-FIT-2016 CLIENT\n");    // PROTOCOL
 if(index_operation==DOWNLOAD){strcat(bufferOut, "DW_LOAD");}
 else {strcat(bufferOut, "UP_LOAD");}                             // OPERATION
 strcat(bufferOut, "\n"); 
 strcat(bufferOut, requested_file);                               // FILENAME
 strcat(bufferOut, "\n");
 if(index_operation==DOWNLOAD){strcat(bufferOut, "?");}                 // FILE SIZE
 else
   {
    fp = fopen(requested_file, "r");
    if(fp==NULL){error_processing(CHYBA_NO_FILE);}
    fseek(fp, 0L, SEEK_END);
    size = size2 = ftell(fp);
    for(i=0; (unsigned int)i<strlen(bufferIn); i++){ bufferIn[i]='\0'; }
    sprintf(bufferIn, "%d", size);
    strcat(bufferOut, bufferIn);
    bufferIn[0]='\0';
    fclose(fp);
   }                             
 strcat(bufferOut, "\n");
 if( send(socket_fd , bufferOut , strlen(bufferOut) , 0) < 0) error_processing(CHYBA_SEND);


 int result, znak;

  // READ PROTOCOL HEAD
  bytesread = read(socket_fd, bufferIn, strlen("PROTOCOL XSCHOR02-IPK-FIT-2016 SERVER\n"));
  if(bytesread <= 0) error_processing(CHYBA_CONNECT);
  bufferIn[bytesread] = '\0';
  if(strcmp(bufferIn,"PROTOCOL XSCHOR02-IPK-FIT-2016 SERVER\n")!=0) error_processing(CHYBA_BAD_HEAD);
  if(DEBUG) printf("   > Comm with server established\n");
  // READ DEMAND
  bytesread = read(socket_fd, server_state, strlen("DW_LOAD\n"));
  if(bytesread <= 0) error_processing(CHYBA_CONNECT);
  server_state[bytesread] = '\0';
  if(strcmp(server_state,"DW_LOAD\n")==0) {if(DEBUG)  printf("   > Server want to DOWN-load file\n"); }
  else if(strcmp(server_state,"UP_LOAD\n")==0) {if(DEBUG)  printf("   > Server want to UP-load file\n"); }
  else error_processing(CHYBA_UNKNOWN_DEMAND);
  server_state[bytesread-1] = '\0';
  // 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);      
  // --------------------------
 
 

 if( strcmp(server_state, "DW_LOAD")==0 )
   {
     fp = fopen("temorary-file-for-client-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( requested_file, F_OK ) != -1 ) remove(requested_file);
    rename("temorary-file-for-client-content.txt", requested_file);   
   }
 else if( strcmp(server_state, "UP_LOAD")==0 )
   {
     fp = fopen(filename, "r");
     for(i=0; i<size2; i++)
       {
        znak = fgetc(fp);
        if( send(socket_fd , (char *)(& znak) , sizeof(int) , 0) < 0) error_processing(CHYBA_SEND);
       }
     fclose(fp);
   }
 else error_processing(SERVER_ERR);
 

 return 0;
}