#include "filetext.h"
//////////////////////////////////////// TTracciato_Campo ////////////////////////////////////////
TTracciato_campo::TTracciato_campo(int position, int length, int decimal, const char align, const char filler)
 :_position(position), _length(length), _decimal(decimal), _align(align), _filler(filler) {}

TObject* TTracciato_campo::dup() const
{            
  TTracciato_campo* t = new TTracciato_campo(*this);
  return t;
}

const TTracciato_campo& TTracciato_campo::operator =(const TTracciato_campo& tc)
{
  copy(tc);
  return *this;
}

void TTracciato_campo::copy(const TTracciato_campo& tc)
{                                         
  set_name(tc._name);
  set_type(tc._type);    
  set_field(tc._field);
  set_position(tc._position);
  set_length(tc._length);
  set_decimal(tc._decimal); 
  set_align(tc._align);
  set_filler(tc._filler); 
  set_picture(tc._picture); 
  set_message(tc._message);
}                                     
//////////////////////////////////////// TTracciato_record ////////////////////////////////////////
TObject* TTracciato_record::dup() const
{ 
  TTracciato_record* t = new TTracciato_record(*this);
   return t; 
}

TTracciato_record::TTracciato_record(const  TTracciato_record& tr)
{
  set_type(tr.type());
  _tracciati_campo = tr._tracciati_campo;
}                       

void TTracciato_record::add(const TTracciato_campo& tc, int pos)
{
  _tracciati_campo.add(tc,pos);
}

TTracciato_campo& TTracciato_record::get(int n)
{
  if (TTracciato_record::ptr(n) == NULL)//se non esiste lo creo
  {
    TTracciato_campo* t = new TTracciato_campo();
    TTracciato_record::add(*t, n);
  }  
  return (TTracciato_campo&)_tracciati_campo[n];  
}
//////////////////////////////////////// TRecord_text ////////////////////////////////////////
const TString& TRecord_text::row(int pos) const         
{                                                              
  return _array.row(pos);
}

void TRecord_text::add(const TString& c, int pos)
{
  _array.add(c, pos);
}                                                               
//////////////////////////////////////// TFile_text ////////////////////////////////////////
TTracciato_record& TFile_text::t_rec(const TString& type)
{
  return (TTracciato_record&)_tracciati_record[type];
} 

void TFile_text::set_gen_parm(TConfig& config, TString& section)
{
  _decsep = config.get_char("DECSEP",section);
  _recordsize = config.get_int("RECORDSIZE",section);
  _fieldsep = config.get_char("FIELDSEP",section);// solo se a lung. variabile
  _recordsep = config.get("RECORDSEP",section);// solo se a lung. variabile
  _typefield =  config.get_int("TYPEFIELD",section);
  _fixedlen = _fieldsep == ' ' && _recordsep.blank();
  _typepos = -1; 
  _typelen = -1;
}        

void TFile_text::set_type_parm(TConfig& config, TString& section)
{
  TString lavoro = section;
  lavoro.ltrim(4);//elimino la parola 'TYPE' e gli spazi vuoti
  lavoro.trim();//per avere solo il nome del tipo predefinito
  TTracciato_campo tipo;  
  tipo.set_type(lavoro);
  tipo.set_length(config.get_int("LENGTH", section));
  tipo.set_decimal(config.get_int("DECIMAL", section));
  tipo.set_align(config.get_char("ALIGN", section));
  TString s  = config.get("FILLER", section); 
  tipo.set_filler(s[1]);
  tipo.set_picture(config.get("PICTURE", section));
  _tipi.add(lavoro, tipo);// aggiungo il tracciato campo all'assoc_array dei tipi predefiniti
}
        
void TFile_text::set_rec_parm(TConfig& config, TString& section)
{
  TString comodo = section;
  comodo.ltrim(6);//elimino la parola 'RECORD' o 'HEADER' o 'FOOTER' e gli spazi vuoti
  comodo.trim();//per avere solo il nome del tipo del record
  TTracciato_record tr(comodo);//istanzio un tracciato_record del tipo corrente
  //setto tutti i dati relatvi ai tipi predefini nei tracciati campo per i diversi campi del record 
  config.set_paragraph(section);
  TString lavoro;
  TString_array variables;
  const int numvar = config.list_variables(variables, TRUE);//scarico tutte le variabili della sezione 
  TBit_array indici(numvar);
  indici.reset();
  for (int j = 0; j < numvar; j++)//scandisco tutte le variabili della sezione                                       
  {                          
    const TString key = (variables.row(j)).get(0);//estraggo nome 
    const TString& obj = (variables.row(j)).get(1);//estraggo valore 
    lavoro = key.left(3);    
    int pos = key.find('(');
    int n  = atoi(key.mid(pos+1, -1));//estraggo l'indice              
    if (!indici[n])    
    {
      if (config.exist("TYPE",  n))
      {
        TString tipo = config.get("TYPE", section, n);
         if (!tipo.empty())
         {
           TTracciato_campo& tc = (TTracciato_campo&)_tipi[tipo];
            tr.add(tc, n);   
         }
       }  
       indici.set(n);
    }
    TTracciato_campo& tc = tr.get(n);//prendo il tracciato campo con indice <n>
    if (lavoro == "NAM")
    {                            
       tc.set_name(obj);
       continue;
    }
    if (lavoro == "FIE")
    {                 
       TFieldref field;
       field = obj;
       tc.set_field(field);
       continue;
    }          
    if (lavoro == "POS")
    {             
       int pos = atoi(obj);
       tc.set_position(pos);
       if (_fixedlen && _typepos < 0 && n == _typefield)
          _typepos = pos;
       continue;
    }
    if (lavoro == "LEN")
    {             
       int len = atoi(obj);                  
       if (tc.length() <= 0 && len >= 0 && tc.length() != len)
          tc.set_length(len);
       if (_fixedlen && _typelen < 0 && n == _typefield)
         _typelen = len;
       continue;
    }                  
    if (lavoro == "DEC")
    {             
        int dec = atoi(obj);  
        if (tc.decimal() <= 0 && dec >= 0 && tc.decimal() != dec)
          tc.set_decimal(dec);
        continue;
     }                          
     if (lavoro == "ALI")
    {                  
       bool condition = tc.align() != obj[0];
       if (condition)
        tc.set_align(obj[0]);
       continue;
    } 
    if (lavoro == "FIL")
    {             
       if (!obj.blank())
       {
         bool condition = tc.filler() != obj[1];
         if (condition)
           tc.set_filler(obj[0]);
        } 
       continue;
     }
    if (lavoro == "PIC")
    {             
       if (tc.picture().blank() && (!obj.blank()))
        tc.set_picture(obj);
       continue;
    }
    if (lavoro == "MES")
    {                      
       tc.set_message(obj);
       continue;
    } 
  }
   //aggiungo il tracciato record all'assoc_array dei tracciati record
  _tracciati_record.add(comodo, tr);
}

TFile_text::TFile_text(const TString& file_name, const TString& config_name) : _name(file_name)
{
  _read_file = NULL;
  _write_file = NULL;
  TConfig config(config_name);
  TString_array paragraphs;
  config.list_paragraphs(paragraphs);//scarico la lista dei paragrafi
  int items = paragraphs.items();//quanti paragrafi ho scaricato
  TString section, sec;
  for (int i = 0; i < items; i++)//scandisco tutti i paragrafi
  {                                             
    section = paragraphs.row(i) ;//prendo il nome del paragrafo
    sec = section.left(3);//prendo le prime tre lettere del nome
    if (sec == "MAI") //inizializzo i parametri generali del file
    {
       set_gen_parm(config,section);
       continue;
    }        
    if (sec == "TYP")                                 
    {          
       set_type_parm(config, section);
       continue;
    }
    if ((sec == "REC")||(sec == "HEA")||(sec == "FOO"))
    {   
      set_rec_parm(config, section);
      continue;
    }  
  }  
}                                                    
                          
//Scrive su file il  record_text  (valido anche per header e footer)
int TFile_text::write(TRecord_text& rec)
{
  TString buffer;  
  TString campo;
  const TString& type = rec.type();
  TTracciato_record& tr = t_rec(type);
  TArray& a_tc = tr.tracciati_campo();
  int items = rec.items();              
  if (_fixedlen)
  {                        
      for (int i = 0; i < items; i++)
      {
           campo = rec.row(i);   
           TTracciato_campo& tc = tr.get(i); 
           //buffer.insert(campo, tc.position());                        
           buffer << campo; 
           campo.cut(0);
      }
     CHECK(_write_file, "Impossibile scrivere su un file chiuso.");
      *_write_file <<  buffer;                                  
      if (!ok_w()) return 1;
      buffer.cut(0);
  }    
  else
  {
    TToken_string ts(buffer, _fieldsep);
    for (int i = 0; i < items; i++)
      {
           campo = rec.row(i);   
           ts.add(campo, i);
           campo.cut(0);
      }
      CHECK(_write_file, "Impossibile scrivere su un file chiuso.");
      *_write_file <<  ts;
      *_write_file <<  _recordsep;
      if (!ok_w()) return 1;   
      ts.cut(0);
  }  
  return 0;
}

//Carica tutti i dati nel tracciato record (valido anche per header e footer) nel record_text
void TFile_text::autoload(TRecord_text& rec, TCursor& cur , const TString* tipo)
{                             
  TString campo; 
  TRelation& rel = *cur.relation();
  const TString& type = rec.type(); 
  if (tipo == NULL) tipo = &type;
  TTracciato_record& tr = t_rec(*tipo);
  TArray& a_tc = tr.tracciati_campo();
  int items =  a_tc.items();
  for (int i = 0; i < items; i++)
  {
    TTracciato_campo& tc = tr.get(i);
    const TFieldref& field = tc.field(); 
    if (!field.name().empty())
      campo = field.read(rel);
    TString message = tc.message(); 
    if (!message.empty())
    {
        TToken_string msg (message, ',');                               
        if (!msg.blank())
           validate(cur, rec, msg, campo); 
     }   
    const TRectype& rel_rec = rel.curr(field.file());         
    TFieldtypes tipo_campo = rel_rec.type(field.name());
    if (tipo_campo != _datefld && tipo_campo != _realfld && tipo_campo != _intfld && tipo_campo != _longfld && message != "_IMPORTO,!TOT")
        campo = format_field(tc, campo);//formatta il campo secondo le specifiche del tracciato 
    else
    { 
        if (tipo_campo == _datefld)
        {
            TDate data(campo);  
            TString s;
            format_date(data, tc.picture(), s);//formatta la data secondo le specifiche del tracciato    
            campo = s ; 
        }                
        if (tipo_campo == _realfld || tipo_campo == _intfld || tipo_campo == _longfld)
        {
           real numero(campo);
           campo = numero.string(tc.picture());//formatta il numero secondo le specifiche del tracciato                 
           int length = tc.length();
           if (tc.align() == 'R')
             campo.right_just(length, tc.filler());
           else
             campo.left_just(length, tc.filler());     
           int j = campo.replace('.', _decsep);
           CHECK(j >= 0 && j <= 1 , "Impossibile scrivere pił separatori decimali.");
        }              
    }
    rec.add(campo, i);       
    campo.cut(0);
  }                                                          
} 

//Legge da file il record text
int TFile_text::read(TRecord_text& rec)
{
  TString buffer(_recordsize), lavoro;
  CHECK(_read_file, "Impossibile leggere su un file chiuso.");
  if (_fixedlen)
  {        
    buffer.cut(0);
    _read_file -> read(buffer.get_buffer(),buffer.size()); 
    if (!ok_r()) return 1; //non ritorna errore se fine file ed il record e' completo!
    const TString tipo = buffer.mid(_typepos, _typelen);
    rec.set_type(tipo);//istanzio il tipo del record text
   TTracciato_record& tr = t_rec(tipo); 
    //ora che ho il tracciato record devo scandire i tracciati campo e caricare il record text
    TArray& a_tc = tr.tracciati_campo();
    int items =  a_tc.items();
    for (int i = 0; i < items; i++)
    {
      TTracciato_campo& tc = tr.get(i);
      int pos = tc.position();
      int len = tc.length();
      lavoro = buffer.mid(pos, len); 
      rec.add(lavoro, i);
    }
  }
  else
  {       
    //legge carattere per carattere fino a quando non si trova il separatore di record 
    char  c = _read_file-> get ();
    while(c != _recordsep[0]&&ok_r())
    {  
         buffer << c;
         c = _read_file-> get ();
         if (!ok_r()) return 1;//non ritorna errore se fine file ed il record e' completo! 
    }     
    // prendo il resto del separatore  
    int l = _recordsep.len()-1; 
    for (int j = 0; j < l;c = _read_file-> get (), j++);
    TToken_string ts(buffer, _fieldsep);
    TString tipo = ts.get(_typefield); 
    TTracciato_record& tr = t_rec(tipo); 
    rec.set_type(tipo);//istanzio il tipo del record text
    TArray& a_tc = tr.tracciati_campo();
    int items =  a_tc.items();
    ts.restart();
    for (int i = 0; i < items; i++)
    {
      lavoro = ts.get();
      rec.add(lavoro, i);
    }
  }                                   
  return 0;
}  

//Carico la relazione con i dati del record text
int TFile_text::autosave(TRelation& rel, const TRecord_text& rec)
{           
  const TString& type = rec.type();//prendo il tracciato record del tipo del record_text 
  TTracciato_record& tr = t_rec(type); 
  TArray& a_tc = tr.tracciati_campo();
  int items =  a_tc.items();                 
  TString valore;
  for (int i = 0; i < items; i++)
  {
    TTracciato_campo& tc = tr.get(i);
    const TFieldref& field = tc.field();
    valore = rec.row(i);                     
    if (!field.name().empty())
        field.write(valore, rel);//faccio una write sulla relazione del fieldref
    }
  int err = rel.write();
  if (err != NOERR)   
     if (err == _isdupkey || err ==_isreinsert)
         err = rel.rewrite();     
  return err;       
} 

//Scarica dal record_text il campo alla posizione <ncampo>
const TString& TFile_text::get_field(TRecord_text& rec, int ncampo) 
{                                         
  const TString& type = rec.type();//prendo il tracciato record del tipo del record_text 
  TTracciato_record tr(type);
  TTracciato_campo& tc = tr.get(ncampo);
   return rec.row(ncampo);
}

//Carica nel record_text il campo alla posizione <ncampo> con il valore <val> gią formattato  
void TFile_text::add_field(TRecord_text& rec, const int ncampo, const char* val)
{
   const TString& type = rec.type(); 
   TTracciato_record& tr = t_rec(type);
   TTracciato_campo& tc = tr.get(ncampo);
   TString valore = val; 
   valore = format_field(tc, valore);
   rec.add(valore, ncampo);    
}    

//Formatta la data in base al tracciato 
void TFile_text::format_date(const TDate& data, const TString& form, TString& data_str)
{                                                             
  int i = 1, cnt = 0;
  char x = form[0];
  do
  {  
      char k = x;
      if (k != 'a' && k != 'm'&& k != 'g')
          data_str << k;
      else
          cnt++;
      x = form[i];
      i++;  
      if (k != x)
          switch (k)
          {
            case 'g':  CHECKD(cnt >= 1 && cnt <= 4, "Formato per giorno non valido ", cnt);
                          if (cnt == 1) data_str << data.day();
                          if (cnt == 2) data_str << format("%02d", data.day());
                          if (cnt == 3) {TString s = itow(data.wday());s.cut(3);data_str << s;}
                          if (cnt == 4) data_str << itow(data.wday());  
                          cnt = 0;
                          break;
            case 'm': CHECKD(cnt >= 1 && cnt <= 4, "Formato per mese non valido ", cnt);
                          if (cnt == 1) data_str << data.month();
                          if (cnt == 2) data_str << format("%02d", data.month());
                          if (cnt == 3) {TString s = itom(data.month());s.cut(3);data_str << s;}
                          if (cnt == 4) data_str << itom(data.month());
                          cnt = 0;
                          break;
            case 'a':  CHECKD(cnt >= 2 && cnt <= 4, "Formato per anno non valido ", cnt);
                          if (cnt == 2)  {TString s;s << data.year();s.ltrim(2);data_str << s;}
                          if (cnt == 3)  {TString s;s << data.year();s.ltrim(1);data_str << s;}                      
                          if (cnt == 4)  data_str << data.year();
                          cnt = 0;
                          break;
          }
  }        
  while (i <= form.len());  
}

//Formatta la stringa in base al tracciato 
TString& TFile_text::format_field(TTracciato_campo& tc, TString& campo)
{
    int pos = tc.position();
    int length = tc.length();
    if (!tc.picture().blank())
        campo.picture(tc.picture(), campo);     
    if (length > campo.len())
    {
      if (tc.align() == 'R')
        campo.right_just(length, tc.filler());
      else
        campo.left_just(length, tc.filler());            
    } 
    else
      campo.cut(length); 
    return campo;    
}    

int TFile_text::open(TFilename name, char mode)
{                               
  set_name(name);             
  if (mode == 'r')//apertura in lettura
  {          
    _read_file = new ifstream(name, ios::binary);
     if (!ok_r())
      error_box("Impossibile aprire il file %s in lettura", (const char *)name);
  }
  if (mode == 'w')//apertura in scrittura
    _write_file = new ofstream(name,ios::binary|ios::app);  
  return 0;  
}                 

int TFile_text::close()
{
  _name = "";
  _current = NULL;
  _tipi.destroy();
  _tracciati_record.destroy();
  if (_read_file) _read_file->close();
  if (_write_file) _write_file->close();
  return 0;
}

