/*
Console File Splitter v 0.2

Divide i file in più parti e all'occorrenza li ricompone.
Utile per qualsiasi esigenza di spazio.

Autore:             __GiReX__
Linguaggio:         C++
Sistema operativo:  Qualsiasi
Data:               26/11/07 20.47
*/

#include <iostream>
#include <fstream>
#include <cstring>
#include <ctime>
#include <cmath>
#define SPLIT_SUCCESS 0
#define MERGE_SUCCESS 4
using namespace std;

void usage(char *prog);
int split(char *nome_file, int parti);
int merge(char *nome_file, int parti);
int name_part(int n_part, char *nome_file, char *tmp);

int main(int argc, char *argv[])
{
  int parti, error; 
  clock_t sec;
   char *nome_file = NULL;
    
    if(argc < 3 || argc > 4) {
        usage(argv[0]);
        return 1;
    }
    
    if(argc == 4 && strcmp(argv[1], "-r")) {
        cout << "\n\"" << argv[1] << "\" Parametro non riconosciuto!\n\n";  
            usage(argv[0]);
        return 2;
    }                        
  
    if(argc == 3) { 
        nome_file = argv[1];
          parti = atoi(argv[2]);   
        error = split(nome_file, parti);                 // Splitto   
   } else {
        nome_file = argv[2];
          parti = atoi(argv[3]);
        error = merge(nome_file, parti);                 // Mergo
}
   sec = clock() / CLOCKS_PER_SEC;

      switch(error) {
          case SPLIT_SUCCESS:
                cout << "Il file e' stato diviso in " << parti << " parti con successo!\n\n"
                     << "Tempo impiegato: " << sec << " sec\n";                
                break;
          case 1:
                cout << "Errore nell'apertura del file!\n\n"
                     << "Controllare che il nome o percorso siano corretti!\n";
                break;
          case 2:
                cout << "Errore: Numero di parti troppo elevato!\n";
                break;
          case 3:
                cout << "Errore nella creazione dei file part!\n";
                break;
          case -1:
                cout << "Errore nella creazione del file di destinazione!\n\n"
                     << "Controllare che il file originale non risieda nella stessa cartella!";
                break;
          case -2:
                cout << "Impossibile aprire i file part!\n\n"
                     << "Controllare che il nome del file corrisponda al nome del file originario\n"
                     << "e che il numero di parti dichiarato corrisponda!\n";
                break;
          case MERGE_SUCCESS:
                cout << "Il file e' stato ricomposto con successo!\n\n"
                     << "Tempo impiegato: " << sec << " sec\n";
                break;
          default:
                cout << "Errore sconosciuto!\n";
          }
      

      cout << "\nPremere un tasto per uscire...\n";    
    cin.ignore();
 return 0;
}
    
int name_part(int n_part, char *nome_file, char *tmp)
{  
  char buff[4] = {0};
     
   strcpy(tmp, nome_file);
   strcat(tmp, ".part_");
   strcat(tmp, itoa(n_part, buff, 10));      // Funzione per convertire int to char*
  
   return 0;
}

int split(char *nome_file, int parti) 
{ 
  ifstream *file = new ifstream(nome_file,ios::binary);// Apro file in modalità binaria
  ofstream *file_part = new ofstream;  
  
   long double size, byte_file, last_file_size;        // Byte del file, e byte per ogni file
   char nome_file_part[80] = {0};                      // Nome dei file part
    
    if(!file)   return 1;    
       
   file->seekg(0, ios::end);
      size = file->tellg();                                    // Prendo lunghezza file
        cout << "\nFile di: " << size << " byte" << endl;
          if(size < parti*2 || parti > 30) return 2;
      file->seekg(0, ios::beg);                                // Risposto il cursore all'inizio del file
     byte_file = ceil(size / parti);                          // Byte per ogni file part arrotondati per eccesso
   last_file_size = (size - (byte_file*parti)) + byte_file;   // Byte dell'ultimo file che differisce dagli altri

  cout << "Splitting in corso...\n"; 
   
   for(int i = 0; i < parti; i++) { 
   
   name_part(i, nome_file, nome_file_part);              // Prendo nome dei file part
    
      file_part->open(nome_file_part, ios::binary);       // Apro i file in modalità binaria
     
      if(!file_part) return 3;
   
     
      if( i == (parti - 1)) {                                   // Se è l'ultimo file..
           for(register int i2 = 0; i2 < last_file_size; i2++) 
                  file_part->put(file->get()); 
    } else {
           for(register int i2 = 0; i2 < byte_file; i2++) 
                  file_part->put(file->get());                    // Un byte alla volta copio n byte per file nel file part
           } 
           
     file_part->close(); 
   }
   file->close();
 return SPLIT_SUCCESS;
}

int merge(char *nome_file, int parti)
{
  char bit, nome_part[80] = {0};
   ofstream *file = new ofstream(nome_file, ios::binary);
   ifstream *file_part = new ifstream;
    
    if(!file) return -1;

    cout << "\nRicomponimento (merge) in corso...\n";    
    
     for(int i = 0; i < parti; i++) {
      name_part(i, nome_file, nome_part);                             // Prendo il nome dei file part
       file_part->open(nome_part, ios::binary);                        // Apro i file part
       
       if(!file_part)  return -2;

        while(!file_part->eof())     {                                 // Se npn è un eofbit lo copio nel file
             file_part->get(bit);
        if(!(file_part->rdstate() & ifstream::eofbit)) file->put(bit);  // Copio i file part nel file di destinazione
     }
     
         file_part->clear();
      file_part->close();                                              // Chiudo file part
}
   
    file->close();
 return MERGE_SUCCESS;
}

void usage(char *prog) 
{
     cout << "Usage:\n\n"
          << "Per dividere: " << prog << " <file_da_dividere> <numero_parti>\n\n"
          << "Per ricomporre: " << prog << " -r <file_origine> <numero_parti>\n\n"
          << "Dove <file_origine> e' il nome del file che e' stato diviso\n"
          << "e <numero_parti> le parti in cui e' stato diviso!\n";
}
