#include <applicat.h>
#include <automask.h>
#include <progind.h>
#include <recarray.h>
#include <relation.h>
#include <utility.h>

#include "777.h"
#include "777lib.h"
#include "777200a.h"

#include "777200.h"
#include "../fe/felib.h"

#include <anagr.h>
#include <anafis.h>
#include <anagiu.h>
#include <attiv.h>
#include <comuni.h>
#include <nditte.h>
#include "base.h"
#include "quadroc.h"

///////////////////////////////////////////////////////////
// Utilities
///////////////////////////////////////////////////////////

// Forma un numero di telefono composto di sole cifre da prefisso+numero
const TString& get_telephone(const TRectype& rec, const char* pref, const char* numf)
{
  TString& num = get_tmp_string();
  num << rec.get(pref) << rec.get(numf);
  char* dst = num.get_buffer();
  for (const char* src = dst; *src; src++)
  {
    if (*src >= '0' && *src <= '9')
    {
      if (src > dst)
        *dst = *src;
      dst++;
    }
  }
  *dst = '\0';
  return num;
}

///////////////////////////////////////////////////////////
// TTracciatoCU
///////////////////////////////////////////////////////////

const char* cu_default_name() { return "MODCUR15"; }

class TTracciatoCU : public TTracciato770
{
public:
  TTracciatoCU(char tipo);
};

TTracciatoCU::TTracciatoCU(char tipo)
{
  if (strchr("ABCDHZ", tipo) == NULL)
    NFCHECK("Tipo record non valido: %c", tipo);
  set_tipo(tipo);
  
  add_field("Tipo record", AN, 1, 1);                     // 1
  
  if (tipo == 'A')
  {
    add_filler(2, 14);                                       // 2 
    add_field("Codice fornitura",              AN,  16,  5); // 3
    add_field("Tipo fornitore",                NU,  21,  2); // 4
    add_field("Codice fiscale del fornitore",  CF,  23, 16);
  } else
  if (tipo == 'B')
  {
    add_field("Codice fiscale dichiarante",           CF,  2, 16); // 2 
    add_field("Progressivo modulo",                   NU, 18,  8);
    add_filler( 26, 1);
    add_filler( 27, 8);
    add_filler( 35, 25);
    add_field("Spazio a disposizione",                AN, 60, 14); // 7
    add_field("CF del produttore del software",       CF, 74, 16); 
    add_filler( 90, 1);
    
    // Tipo di dichiarazione
    add_field("Annullamento",                         CB, 91,  1); // 10
    add_field("Sostituzione",                         CB, 92,  1); 

    // Dati del contribuente
    add_field("Cognome",                              AN, 93, 24); // 12
    add_field("Nome",                                 AN,117, 20);
    add_field("Denominazione (Alternativo a 12 e 13)",AN,137, 60);
    add_field("Indirizzo E-mail",                     AN,197,100);
    add_field("Telefono o Cellulare",                 AN,297, 12);
    add_field("FAX",                                  AN,309, 12); // 17

    add_field("Codice fiscale del rappresentante",    CF,321,16);  // 18
    add_field("Codice carica del rappresentante",     NU,337, 2);
    add_field("Cognome",                              AN,339,24);
    add_field("Nome",                                 AN,363,20);
    add_field("Codice fiscale del dichiarante",       CN,383,11);

    add_field("Numero comunicaz. lavoro dipendente",  NU, 394, 8); // 23
    add_field("Numero comunicaz. lavoro autonomo",    NU, 402, 8);
    add_field("Casella quadro CT",                    CB, 410, 1);  
    add_field("Firma del dichiarante",                CB, 411, 1);

    add_field("Codice fiscale intermediario",         CF, 412,16); // 27
    add_field("Impegno a trasmettere la dichiaraz.",  NU, 428, 1);
    add_field("Data dell'impegno",                    DT, 429, 8);
    add_field("Firma dell'intermediario",             CB, 437, 1);  
    add_filler(438, 1);
    add_field("Comune di Residenza",                  AN, 439,40); // 32
    add_field("Provincia di Residenza",               PR, 479, 2);
    add_field("CAP di Residenza",                     NU, 481, 5);
    add_field("Indirizzo di Residenza",               AN, 486,35);
    add_field("Codice Attivit�",                      NU, 521, 6);
    add_field("Codice Sede",                          NU, 527, 3);
  } else
  if (tipo == 'D')
  {
    add_field("Codice fiscale del sostituto",         CF,  2, 16);    // 2
    add_field("Progressivo modulo",                   NU, 18,  8);    // 3
    add_field("Codice fiscale percipiente",           CF, 26, 16);    // 4
    add_field("Progressivo certificazione",           NU, 42,  5);    // 5
    add_field("Identificativo dell'invio",            NU, 47, 17);    // 6
    add_field("Progressivo singola C.U.",             NU, 64,  6);    // 7
    add_field("Tipo Operazione",                      AN, 84,  1, 9); // 9
    add_filler(85, 4);
    add_field("Conferma singola certificazione",      CB, 89,  1);    // 11
  } else
  if (tipo == 'H')
  {
    add_field("Codice fiscale del sostituto",         CF,  2, 16); // 2
    add_field("Progressivo modulo",                   NU, 18,  8); // 3
    add_field("Codice fiscale percipiente",           CF, 26, 16); // 4
    add_field("Progressivo certificazione",           NU, 42,  5); // 5
  } else
  if (tipo == 'Z')
  {
    add_filler(2, 14);                                  
    add_field("Numero record di tipo 'B'", NU, 16, 9); // 3
    add_field("Numero record di tipo 'C'", NU, 25, 9);
    add_field("Numero record di tipo 'D'", NU, 34, 9);
    add_field("Numero record di tipo 'G'", NU, 43, 9); // 6
    add_field("Numero record di tipo 'H'", NU, 52, 9); 
  }
}

///////////////////////////////////////////////////////////
// TTracciatiCU
///////////////////////////////////////////////////////////

class TTracciatiCU : public TObject
{
  TArray _trc;
public:
  const TTracciato770& tracciato(char tipo);
} _trcCU;

const TTracciato770& TTracciatiCU::tracciato(char tipo)
{
  CHECK(tipo >= 'A' && tipo <= 'Z', "Tipo record non valido");
  const int pos = tipo - 'A';
  TTracciatoCU* trc = (TTracciatoCU*)_trc.objptr(pos);
  if (trc == NULL)
  {
    trc = new TTracciatoCU(tipo);
    _trc.add(trc, pos);
  }
  return *trc;
}

class TRecordCU : public TRecord770
{
private:
  virtual TObject* dup() const { return new TRecordCU(*this); }

protected:
  virtual const TTracciato770& tracciato() const
  { return _trcCU.tracciato(tipo_record());  }

public:
  virtual bool valid() const
  {
    const char tipo = tipo_record();
    return (tipo > ' ') && (strchr("ABCDGHZ", tipo) != NULL);
  }

  virtual bool ha_campi_non_posizionali() const { return strchr("DGH", tipo_record()) != NULL; }
  bool ha_campo_posizionale(int fld) const { return tracciato().exists(fld); }

  TRecordCU() { }
  TRecordCU(const TRecordCU& rec) : TRecord770(rec) { }
  TRecordCU(char tipo) { tipo_record(tipo); }
};

///////////////////////////////////////////////////////////
// TTrasferimentoCU
///////////////////////////////////////////////////////////

const TVariant& TTrasferimentoCU::get_var(const char* name) const
{
  if (_cur_pos >= 0 && _cur_pos < items())
  {
    const TRecordCU& cur_rec = (const TRecordCU&)_data[_cur_pos];
    const TFixed_string varname(name+1);
    if (real::is_natural(varname))
    {
      const int n = atoi(varname);
      if (n > 0 && cur_rec.ha_campo_posizionale(n))
      {
        TVariant& var = get_tmp_var();
        switch (column_info(n-1)._type)
        {
        case _nullfld:
          break;
        case _boolfld:
          {
            TString4 str; cur_rec.get(n, str);
            var = str == "1" || str == "X";
          }
          break;
        case _datefld:
          {
            TString8 str; cur_rec.get(n, str);
            if (str.full() && str != "00000000")
              var = str;
          }
          break;
        case _realfld:
          {
            TString80 str; cur_rec.get(n, str);
            var = real(str);
          }
          break;
        default:
          {
            TString256 str; cur_rec.get(n, str);
            var = str;
          }
          break;
        }
        return var;
      }
    }
    else
    {
      if (cur_rec.ha_campi_non_posizionali_compilati())
      {
        TString80 str;
        TString8 key;
        TString16 val; 
        for (int p = 0; cur_rec.np_get(p, key, val); p++)
        {
          if (p == 0 && key[0] != varname[0]) // Record di tipo errato!
            return NULL_VARIANT;
          if (key == varname)
          {
            str = val;
            while (cur_rec.np_get(++p, key, val) && key == varname)
              str << val.mid(1);
            str.rtrim();
            break;
          }
          if (key > varname)
            break;
        }
        return get_tmp_var() = str;
      }
    }
  }
  return NULL_VARIANT;
}


bool TTrasferimentoCU::load(const char* path)
{
  if (path && *path)
  {
    _name = path;
    if (_name.find(cu_default_name()) < 0)
      _name.add(cu_default_name());
  }
  if (_name.empty())
    _name = cu_default_name();

  ifstream is(_name, ios::in | ios::binary);
  TRecordCU rec;
  while (!is.eof())
  {
    is >> rec;
    if (rec.valid())
      _data.add(rec);
  }
  _cur_pos = _data.empty() ? 0 : -1;
  return _cur_pos>=0;
}

TTrasferimentoCU& TTrasferimentoCU::operator<<(const TRecordCU& rec)
{
  _data.add(rec);
  return *this;
}

TTrasferimentoCU& TTrasferimentoCU::operator>>(TRecordCU& rec)
{
  if (_cur_pos < items())
    rec = (const TRecordCU&)_data[_cur_pos++];
  return *this;
}

unsigned int TTrasferimentoCU::columns() const 
{ 
  int f = 1;
  if (_cur_pos >= 0 && _cur_pos < items())
  {
    const TRecordCU& cur_rec = (const TRecordCU&)_data[_cur_pos];
    f = cur_rec.campi_posizionali();
    if (cur_rec.ha_campi_non_posizionali_compilati())
    {
      TString16 key, val;
      for (int i = 0; cur_rec.np_get(i, key, val); i++)
        f++;
    }
  }
  return f; 
}

const TRecordset_column_info& TTrasferimentoCU::column_info(unsigned int column) const 
{ 
  static TRecordset_column_info ci;
  if (column >= 0 && column < columns())
  {
    const TRecordCU& cur_rec = (const TRecordCU&)_data[_cur_pos];
    const TTracciato770& trc = _trcCU.tracciato(cur_rec.tipo_record());
    if (column < (unsigned int)trc.campi_posizionali())
    {
      if (trc.exists(column+1))
      {
        const TField770& fld = trc.field(column+1);
        ci._name = fld._desc;
        switch (fld._type)
        {
        case VN:
        case VP: ci._type = _realfld; break;
        case DT: ci._type = _datefld; break;
        case CB: ci._type = _boolfld; break;
        default: ci._type = (ci._name == "Filler") ? _nullfld : _alfafld; break;
        }
        ci._width = fld._len;
      }
      else
      {
        ci._name = "Filler";
        ci._type = _nullfld;
        ci._width = 0;
      }
    }
    else
    {
      TString16 key, val;
      const int f = column - trc.campi_posizionali();
      cur_rec.np_get(f, key, val);
      ci._name = key;
      ci._type = _alfafld;
      ci._width = 16;
    }
  }
  else
  {
    ci._name.cut(0);
    ci._type = _nullfld;
    ci._width = 0;
  }
  return ci;
}

const TVariant& TTrasferimentoCU::get(unsigned int column) const
{
  if (column < columns())
  {
    const TRecordCU& cur_rec = (const TRecordCU&)_data[_cur_pos];
    if (column <= 0)
    {
      const char str[2] = { cur_rec.tipo_record(), '\0' };
      return get_tmp_var() = str;
    }
    else
    {
      if (column < (unsigned int)cur_rec.campi_posizionali())
      {
        TString4 fld; fld.format("#%d", column+1);
        return get_var(fld);
      }
      else
      {
        TString16 key, val;
        const int f = column - cur_rec.campi_posizionali();
        cur_rec.np_get(f, key, val);
        return get_tmp_var() = val;
      }
    }
  }
  return NULL_VARIANT;
}

bool TTrasferimentoCU::set_field(int n, const TVariant& var)
{
  bool done = _data.objptr(_cur_pos) != NULL;
  if (done)
  {
    TRecordCU& cur_rec = (TRecordCU&)_data[_cur_pos];
    done = cur_rec.ha_campo_posizionale(n);
    if (done)
      cur_rec.set(n, var.as_string());
  }
  return done;
}

const TString& TTrasferimentoCU::read_codfis_dic(const TRectype& rec)
{
  TToken_string key;  // Stringa multiuso

  key = rec.get(BSE_CODDIC);
  if (key.empty())
    key = rec.get(BSE_CODDITTA);

  const TRectype& rec_nditte = cache().get(LF_NDITTE, key);
  if (rec_nditte.empty())
  {
    error_box(FR("Non esiste la ditta %s"), (const char*)key);
	  return EMPTY_STRING;
  }
  _cod_ditta = atol(key);
  _tipoa_dic = rec_nditte.get_char(NDT_TIPOA);
  _codan_dic = rec_nditte.get_long(NDT_CODANAGR);

  _codatt_dic = rec_nditte.get(NDT_CODATTPREV); // Codice attivita' prevalente
  key.add(_codatt_dic);                         // key = CODDITTA|CODATTPREV
  const TRectype& attiv = cache().get(LF_ATTIV, key);
  if (attiv.exist(ATT_CODATECO))                // Non e' detto che il campo esista sempre
  {
    const TString& codateco = attiv.get(ATT_CODATECO);
    if (codateco.full())
      _codatt_dic = codateco;
  }

  key.cut(0) << _tipoa_dic;
  key.add(rec_nditte.get(NDT_CODANAGR));
  const TRectype& rec_anagr = cache().get(LF_ANAG, key);
  if (rec_anagr.empty())
  {
    error_box(FR("Non esiste la persona %s"), (const char*)key);
	  return EMPTY_STRING;
  }
  _codfis_dic = rec_anagr.get(ANA_COFI);   //  Codice fiscale del dichiarante
  if (_codfis_dic.len() > 11)
    _codfis_dic = rec_anagr.get(ANA_PAIV);
  _ragsoc_dic = rec_nditte.get(NDT_RAGSOC); //  Denominazione del dichiarante
  
  return _codfis_dic;
}

void TTrasferimentoCU::set_operazione(char t, const TString& iden) 
{ 
  _tipo_operazione = (t == 'A' || t == 'S') ? t : ' '; 
  _certificazione = 0;
  if (_tipo_operazione >= 'A')
    _identificativo = iden;
  else
    _identificativo.cut(0);
}


TRecnotype TTrasferimentoCU::conta_certificazioni() const
{
  int anno = _anno;
  if (anno < 2014)
    anno = 2014;

  TString query;
  query.format("USE %d SELECT ANNO=%d\nFROM CODDITTA=%ld\nTO CODDITTA=%ld",
                LF_QUALA, anno, _cod_ditta, _cod_ditta);

  TRecnotype nc = 0;
  TISAM_recordset recset(query);
  for (bool ok = recset.move_first(); ok; ok = recset.move_next())
  {
    const char tipoanag = recset.get("TIPO").as_string()[0];
    const long codanagr = recset.get("CODANAGR").as_int();
    if (tipoanag >= 'F' && codanagr > 0)
      nc++;
  }
  CHECKD(nc <= recset.items(), "Numero certificazioni errato:", nc);
  return nc;
}

bool TTrasferimentoCU::append_record_b()
{
  if (!save_headers())
    return false;
  
  TRecordCU rec('B');        // Compila record di testata B
  rec.set(2, cod_fis_dic());
  rec.set(3, 1);             // Modulo (deve essere sempre 1)
  rec.set(8, CF_PRODUTTORE); // CF del produttore Sirio
  rec.set(10, _tipo_operazione == 'A');
  rec.set(11, _tipo_operazione == 'S');

  if (_tipoa_dic == 'F')
  {
    rec.set(12, _ragsoc_dic.left(24));
    rec.set(13, _ragsoc_dic.mid(30,20));
  }
  else
    rec.set(14, _ragsoc_dic);

  const TRectype& rec_nditte = cache().get(LF_NDITTE, _cod_ditta);
  rec.set(15, rec_nditte.get(NDT_MAIL));

  TString80 numtel = get_telephone(rec_nditte, NDT_PTEL, NDT_TEL);
  if (numtel.blank())
    numtel = get_telephone(rec_nditte, NDT_PFAX, NDT_FAX);
  rec.set(16, numtel);

  const TAnagrafica rapp(LF_ANAG, 'F', rec_nditte.get_long(NDT_RAPPR));
  if (rapp.ok())
  {
    _codfis_rap = rapp.codice_fiscale();
    rec.set(18, _codfis_rap);
    rec.set(19, rec_nditte.get(NDT_CARRAPP));
    rec.set(20, rapp.cognome().left(24));
    rec.set(21, rapp.nome());
  }
  else
  {
    _codfis_rap = _codfis_dic;
    cantread_box("legale rappresentante");
  }

  rec.set(23, 0);                      // Dichiarazioni relative a dipendenti
  rec.set(24, conta_certificazioni()); // Certificazioni per autonomi e provvigioni
  rec.set(26, true);                   // Firma

  if (_codfis_int.full())
  {
    rec.set(27, _codfis_int);
    rec.set(28, 1);                      // Impegno alla trasmissione (1 o 2)  
    rec.set(29, _data_imp);              // Data impegno alla trasmissione
    rec.set(30, true);                   // Firma intermediario
  }

  _data.add(rec);
  return true;
}

bool TTrasferimentoCU::append_record_d(const TRectype& qla, TLog_report& log)
{
  const char tipoanag = qla.get_char("TIPOA");
  const long codanagr = qla.get_long("CODANAGR");
  if (tipoanag < 'F'  || codanagr <= 0)
  {
    TString msg; msg.format(FR("Scartato percipiente non valido %c/%ld"), tipoanag, codanagr);
    log.log(2, msg);
    return false;
  }

  const TAnagrafica perc(LF_ANAG, tipoanag, codanagr);
  if (!perc.ok())
  {
    TString msg; msg.format(FR("Scartato percipiente non valido %c/%ld"), tipoanag, codanagr);
    log.log(2, msg);
    return false;
  }

  if (perc.codice_fiscale().blank())
  {
    TString msg; msg.format(FR("Scartato percipiente %c/%ld senza codice fiscale"), tipoanag, codanagr);
    log.log(2, msg);
    return false;
  }
  
  TRecordCU rec('D');         // Compila record percipiente D
  rec.set(2, cod_fis_dic());
  rec.set(3, 1);              // Modulo (deve essere sempre 1)
  rec.set(4, perc.codice_fiscale());
  rec.set(5, ++_certificazione); // Progressivo certificazione
  rec.set(9, _tipo_operazione); 
  if (_tipo_operazione >= 'A')
  {
    rec.set(6, _identificativo); 
    rec.set(7, _certificazione); 
  }
  rec.set(11, false); // Conferma singola certificazione

  const TAnagrafica sost(LF_NDITTE, _cod_ditta);
  rec.np_put("DA001001", cod_fis_dic());
  if (sost.fisica())
  {
    rec.np_put("DA001002", sost.cognome());
    rec.np_put("DA001003", sost.nome());
  }
  else
    rec.np_put("DA001002", sost.ragione_sociale());

  rec.np_put("DA001004", sost.comune_residenza());
  rec.np_put("DA001005", sost.provincia_residenza());
  rec.np_put("DA001006", sost.CAP_residenza());
  rec.np_put("DA001007", sost.indirizzo_residenza());

  const TRectype& rec_nditte = cache().get(LF_NDITTE, _cod_ditta);
  rec.np_put("DA001008", get_telephone(rec_nditte, NDT_PTEL, NDT_TEL));
  rec.np_put("DA001009", rec_nditte.get(NDT_MAIL));
  rec.np_put("DA001010", _codatt_dic);
  // rec.np_put("DA001011", "1"); // Codice Sede ???

  rec.np_put("DA002001", perc.codice_fiscale());
  if (perc.fisica())
  {
    rec.np_put("DA002002", perc.cognome());
    rec.np_put("DA002003", perc.nome());
    rec.np_put("DA002004", perc.sesso());
    rec.np_put("DA002005", perc.data_nascita());
    rec.np_put("DA002006", perc.comune_nascita());
    rec.np_put("DA002007", perc.provincia_nascita());
  }
  else
  {
    rec.np_put("DA002002", perc.ragione_sociale());
  }

  const TRectype& anag = cache().get_rec(LF_ANAG, qla.get("TIPOA"), qla.get("CODANAGR"));
  rec.np_put("DA002008", anag.get(ANA_CATPAR));
  rec.np_put("DA002009", anag.get_long(ANA_EVECC));
  rec.np_put("DA002010", anag.get_long(ANA_ESCPRECOMP));

  const TString& causale = qla.get("CAUSALE");
  if (causale == "N")
  {
    rec.np_put("DA002020", perc.comune_residenza());
    rec.np_put("DA002021", perc.provincia_residenza());
    rec.np_put("DA002022", perc.codice_comune_residenza());
  }

  // rec.np_put("DA002030", "");  // Codice fiscale del rappresentante un incapace

  if (perc.estero())
  {
    rec.np_put("DA002040", perc.codice_fiscale());
    rec.np_put("DA002041", perc.localita_residenza());
    rec.np_put("DA002042", perc.indirizzo_residenza());
    rec.np_put("DA002043", perc.stato_estero_UNICO());
  }

  rec.np_put("DA003001", _data_tra);
  rec.np_put("DA003002", true);

  _data.add(rec);
  return true;
}

bool TTrasferimentoCU::append_record_h(const TRectype& qla, TLog_report& log)
{
  const char tipoanag = qla.get_char("TIPOA");
  const long codanagr = qla.get_long("CODANAGR");
  if (tipoanag < 'F' && codanagr <= 0)
  {
    TString msg; msg.format(FR("Scartato percipiente non valido %c/%ld"), tipoanag, codanagr);
    log.log(2, msg);
    return false;
  }

  TAnagrafica perc(LF_ANAG, tipoanag, codanagr);
  if (!perc.ok())
  {
    TString msg; msg.format(FR("Scartato percipiente non valido %c/%ld"), tipoanag, codanagr);
    log.log(2, msg);
    return false;
  }

  TRecordCU rec('H');                // Compila record H
  rec.set(2, cod_fis_dic());         // Codice fiscale sostituto
  rec.set(3, 1L);                // Progressivo modulo
  rec.set(4, perc.codice_fiscale()); // Codice fiscale del percipiente
  rec.set(5, _certificazione);       // Progressivo certificazione

  const TString4 causale = qla.get("CAUSALE");
  rec.np_put("AU001001", causale);
  if (causale >= "G" && causale <= "I")
    rec.np_put("AU001002", qla.get_long("ANNO")); // Potrebbe essere anche l'anno precedente

  //  rec.np_put("AU001003", "");
  rec.np_put("AU001004", qla.get_real("TOTALE"));
  
  if (perc.estero())
    rec.np_put("AU001005", qla.get_real("SOMREGCONV"));
  
  const real altre_somme = qla.get_real("SOMME");
  if (!altre_somme.is_zero())
  {
    rec.np_put("AU001006", 3L);  // 1, 2, 3
    rec.np_put("AU001007", altre_somme);
  }
  rec.np_put("AU001008", qla.get_real("IMPONIBILE")); // 004 - 005 - 007
  rec.np_put("AU001009", qla.get_real("IMPORTO"));    // ritenute a titolo di acconto
  //rec.np_put("AU001010", ZERO);                     // ritenute a titolo di imposta
  rec.np_put("AU001011", qla.get_real("RITSOSPESE")); // ritenute sospese
  if (causale == "N")
    rec.np_put("AU001012", qla.get_real("ADDREG"));   // addizionale regionale

  rec.np_put("AU001018", qla.get_real("IMPANNIPRE")); // imponibile anni precedenti
  rec.np_put("AU001019", qla.get_real("RITANNIPRE")); // ritenute anni precedenti
  rec.np_put("AU001020", qla.get_real("CTINPSEROG")); // contributi a carico erogante
  rec.np_put("AU001021", qla.get_real("CTINPSPERC")); // contributi a carico percipiente
  //rec.np_put("AU001022", qla.get_real("SPESERIMB"));  // spese rimborsate
  //rec.np_put("AU001023", qla.get_rea;("RITRIMB"));    // ritenute rimborsate

  _data.add(rec);
  return true;
}

static real& real_at(TArray& riep, int idx)
{
  real* r = (real*)riep.objptr(idx);
  if (r == NULL)
    riep.add(r = new real, idx);
  return *r;
}

bool TTrasferimentoCU::save(const char* path)
{
  ofstream of(path && *path ? path : _name, ios::binary);
  for (_cur_pos = 0; _cur_pos < items(); _cur_pos++)
    of << cur_rec();
  return of.good();
}

bool TTrasferimentoCU::split(const char* path)
{
  TRecnotype totale[26]; memset(totale, 0, sizeof(totale));

  bool good = move_first();

  const unsigned long disk_size = 15*1024*1024;   // Max 15 Mb per fornitura
  const long records = items();
  const long records_per_disk = long(disk_size / TOTAL_SIZE) - 3;  // Tolgo A,B,Z
  const int volumes = int((records-1)/records_per_disk)+1;

  TProgress_monitor pi(records, TR("Generazione file trasferimento"));
  for (int volume = 1; good && volume <= volumes; volume++)
  {
    // Specifica volume solo se necessario
    TFilename fn = path; fn.add(cu_default_name());
    if (volumes > 1) fn << '_' << volume;
    TTrasferimentoCU outfile(fn, 'w');
  
    if (save_headers())
    {      
      TRecordCU rec;
      rec.tipo_record('A');      // Compila record di testata A
      rec.set(3, "CUR15");       // Codice fornitura
      rec.set(4, 1);             // Tipo fornitore: 01=Soggetto che invia la propria dichiarazione; 10=CAF
      rec.set(5, _codfis_int.full() ? _codfis_int : cod_fis_dic()); // Codice fiscale del fornitore  
      outfile << rec;            // Scrive record testata
    }
    
    long written = 0;
  
    for (; good; good = move_next())
    {
      const TRecordCU& rec = cur_rec();
      const char tipo_rec = rec.tipo_record();
      if (tipo_rec <= 'A' || tipo_rec >= 'Z')
        continue;

      outfile << rec;
      written++;

      totale[tipo_rec-'A']++;
  
      if (written >= records_per_disk)
        break;

      if (!pi.add_status())
      {
        good = false;
        break;
      }
    }

    if (save_headers())
    {
      // Compi la record di coda
      TRecordCU rec; 
      rec.tipo_record('Z');
      rec.set(3, totale['B'-'A']);  // Totale B
      rec.set(4, totale['C'-'A']);  // Totale C
      rec.set(5, totale['D'-'A']);  // Totale D
      rec.set(6, totale['G'-'A']);  // Totale G
      rec.set(7, totale['H'-'A']);  // Totale H
  
      // Scrive record di coda
      outfile << rec;
    }
    outfile.save();
  }  
  return true;
}

// Cancella il file
void TTrasferimentoCU::remove()
{
  ::remove(_name);
}

TTrasferimentoCU::TTrasferimentoCU(const char* path, char  mode)
                : _save_headers(true), _data_imp(TODAY), _data_tra(TODAY), _tipo_operazione(' ')
{
  if (mode == 'r')
    load(path);
  else
  {
    _name = path;

    while (_data_tra.month() > 2 || _data_tra.wday() > 5 || _data_tra.is_holiday())
      --_data_tra;
  }
}

TTrasferimentoCU::~TTrasferimentoCU()
{ }

///////////////////////////////////////////////////////////
// TCU_mask
///////////////////////////////////////////////////////////

class TCU_mask : public TAutomask
{
protected:
  virtual bool on_field_event(TOperable_field& o, TField_event e, long jolly);

public:
  TCU_mask() : TAutomask("777200a") {}
};

bool TCU_mask::on_field_event(TOperable_field& o, TField_event e, long jolly)
{
  switch (o.dlg())
  {
  case F_PATH:
    if (e == fe_init || e == fe_modify)
    {
      TFilename f = o.get();
      f.add(cu_default_name());
      enable(DLG_PREVIEW, f.exist());
    }
    break;
  case DLG_PREVIEW:
    if (e == fe_button)
    {
      TFilename tmp = get(F_PATH);
      tmp.add(cu_default_name());
      if (tmp.exist())
      {
        const int quality = get_bool(F_QUALITY) ? 2 : 1;
        print_cu(tmp, quality);
      }
      else
        cantread_box(tmp);
      return false; // don't close mask
    }
    break;
  case DLG_EDIT:
    if (e == fe_button)
    {
      TFilename tmp = get(F_PATH);
      tmp.add(cu_default_name());
      if (tmp.input() && tmp.exist())
      {
        const int quality = get_bool(F_QUALITY) ? 2 : 1;
        edit_cu(tmp, quality);
      }
      return false; // don't close mask
    }
    break;
  default: break;
  }
  return true;
}


///////////////////////////////////////////////////////////
// TCU_app
///////////////////////////////////////////////////////////

class TCU_app : public TSkeleton_application 
{
public:
  virtual void main_loop();
};

void TCU_app::main_loop()
{
  const int anno = ini_get_int(CONFIG_STUDIO, "77", "AnnoDic", 2014);
  TCU_mask m;
  m.set(F_ANNO, anno);

  while (m.run() == K_ENTER)
  {
    TFilename tmp; tmp.tempdir();
    TTrasferimentoCU t(tmp, 'w');
    t.set_inter(m.get(F_INTER_CODFIS), m.get_date(F_INTER_DATA), anno);
    t.set_operazione(m.get(F_INVIO)[0], m.get(F_IDENTIFICATIVO));

    TString query;
    query.format("USE %d\nFROM CODDITTA=%ld\nTO CODDITTA=%ld",
                 LF_BASE, m.get_long(F_DADITTA), m.get_long(F_ADITTA));
    TISAM_recordset cur_base(query);
    const TRecnotype ditte = cur_base.items();
    if (ditte > 0)
    {
      TLog_report log; log.kill_duplicates();

      const TRectype& base = cur_base.cursor()->curr();
      for (bool ok = cur_base.move_first(); ok; ok = cur_base.move_next())
      {
        const long codditta = base.get_long(BSE_CODDITTA);
        TString msg; msg.format(FR("Generazione C.U. ditta %ld"), codditta);
        log.log(0, "");
        log.log(0, msg);

        t.read_codfis_dic(base);
        t.append_record_b();
      
        query.format("USE %d SELECT (ANNO=%d)\nBY TIPOA,CODANAGR,CAUSALE\nFROM CODDITTA=%ld\nTO CODDITTA=%ld",
                      LF_QUALA, anno, codditta, codditta);

        TISAM_recordset perc(query);
        const TRecnotype nperc = perc.items();
        if (nperc > 0)
        {
          TProgress_monitor pp(nperc, TR("Generazione record percipienti"));
          const TRectype& rec = perc.cursor()->curr();
          long last_perc = 0;
          TString4 last_caus;
          const int modulo = 1;
          for (bool ok = perc.move_first(); ok; ok = perc.move_next())
          {
            const long codanagr = rec.get_long("CODANAGR");
            const TString4 codcaus = rec.get_long("CAUSALE");
            if (codanagr != last_perc || codcaus != last_caus)
            {
              last_perc = codanagr;
              last_caus = codcaus;
              t.append_record_d(rec, log);
            }
            t.append_record_h(rec, log);
            pp.add_status();
          }
        }
      }
      if (log.errors() || log.warnings())
        log.preview();
    }

    tmp = m.get(F_PATH);
    if (t.split(tmp))
    {
      tmp.add(cu_default_name());
      if (yesno_box(FR("E' stato generato il file %s\nSi desidera visualizzarlo in anteprima?"), (const char*) tmp))
        m.send_key(K_SPACE, DLG_PREVIEW);
    }
    else
    {
      tmp.add(cu_default_name());
      cantwrite_box(tmp);
    }
    t.remove();
  }
}

int m777200(int argc, char* argv[])
{
  TCU_app app;
  app.run(argc, argv, "Invio C.U.");
  return 0;
}