#include <applicat.h>
#include <form.h>
#include <automask.h>
#include <progind.h>
#include <recset.h>
#include <recarray.h>
#include <utility.h>

#include "777.h"
#include "777lib.h"
#include "777100a.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"

// Il nuovo quadro ST e' il vecchio quadro L
#define LF_QUADRO_ST LF_QUAL

///////////////////////////////////////////////////////////
// Utility
///////////////////////////////////////////////////////////

const int ANNO_DIC = 2013;

///////////////////////////////////////////////////////////
// TForm770
///////////////////////////////////////////////////////////

class TTrasferimento770;

class TForm770 : public TForm
{
  TCursor* _sortedcur;

  int _index;
  TArray _records;
  TPointer_array _positions;

protected:
  bool compatible(const TRectype& r1, const TRectype& r2);
  void add_rec(TRectype& r1, const TRectype& r2);

  long raggruppa_c();
  int first();
  int next();

  void transfer_section(TPrint_section& body, int rigo,
                        TRecord770& rec, TTrasferimento770& file);
  char log2rec(int num, int& rpm) const;

protected:

  virtual TCursor* cursor() { return _sortedcur ? _sortedcur : TForm::cursor(); }
  virtual bool validate(TForm_item &cf, TToken_string &s);

public:
  TRecnotype trasfer(long codditta, TTrasferimento770& file, char tipo, int rpm);

  TForm770(const char* name);
  virtual ~TForm770();
};

class TTrasferimento770 : public TObject
{
  TFilename _name;
  ifstream* _in_stream;
  ofstream* _out_stream;

  long _cod_ditta, _codan_dic;
  TString8 _codatt_dic;
  char _tipoa_dic;
  TString16 _codfis_dic;
  TString _ragsoc_dic;
  bool _save_headers;
  bool _centesimi; // Salva importi in centesimi invece che in euro

  TAssoc_array _form;
  
protected:
  void riepiloga_ss(const TRecord770& rec, TArray& riep_ss) const;
  void riepiloga_sx(const TRecord770& rec, TArray& riep_sx) const;
  TForm770& form(const char* quadro, char& tiporec, int& rpm);

public:
  const char* default_name() const;

  bool open(const char* path = "", char mode = 'r', int volume = 0);
  bool close();
  bool write(const TRecord770& rec);
  bool read(TRecord770& rec);
  bool eof() const { return _in_stream && _in_stream->eof(); }

  void set_cent_mode(bool cb) { _centesimi = cb; }
  bool importi_in_centesimi() const { return _centesimi; }
  bool importi_in_euro() const { return !_centesimi; }

  TTrasferimento770& operator<<(const TRecord770& rec)
  { write(rec);  return *this; }
  TTrasferimento770& operator>>(TRecord770& rec)
  { read(rec); return *this; }

  const TString& read_codfis_dic(const TRectype& rec);
  const TString& cod_fis_dic() const { return _codfis_dic; }

  TRecnotype conta_certificazioni() const;
  bool casella_prospetto_st() const;
  bool casella_prospetto_sx() const;
  bool append_record_b();
  long append_quadro(const char* quadro, long codditta, TProgress_monitor& pi);

  bool split(const char* dest_path);
  void remove();

  void save_headers(bool s) { _save_headers = s; }
  bool save_headers() const { return _save_headers; }

  TTrasferimento770(const char* name = "", char mode = 'r', int volume = 0);
  virtual ~TTrasferimento770();
};

///////////////////////////////////////////////////////////
// TForm770
///////////////////////////////////////////////////////////

bool TForm770::validate(TForm_item &cf, TToken_string &s)
{ 
  const TString code(s.get(0)); // prende il primo parametro, il codice del messaggio
  if (code == "_PERIODO_RIF")
  {
    const TRectype& curr = relation()->curr();
    const int anno = curr.get_int("QLAP");
    const int mese = curr.get_int("QLMP");
    TString8 str;
    str.format("%02d%04d", mese, anno);
    cf.set(str);
    return true;
  }
  if (code == "_PROVINCIA")
  {
    const TString& prov = cf.get();
    if (prov == "EE") // Il 770 non gradisce la provincia EE associata agli stati esteri
      cf.set(""); else
    if (prov == "FO") // Ci sono ancora vecchie province :-( 
      cf.set("FC");
    return true;
  }

  return TForm::validate(cf, s);
}

void TForm770::transfer_section(TPrint_section& body, int rigo,
                                TRecord770& rec, TTrasferimento770& file)
{
  TString8 basecode, code, type;
  TString str;

  body.update(); 
  for (word f = 0; f < body.fields(); f++)
  {
    TForm_item& fi = body.field(f);
    str = fi.get(); str.trim();
    if (fi.shown() && str.not_empty())
    {
      basecode = fi.get_special_value("TRASFER");
      if (basecode.not_empty())
      {
        type = fi.get_special_desc("TRASFER");
        if (type.full())
        {
          if (type == "CB")  // Casella Barrata
          {
            if (str == "1" || str == "X")
            {
              str = "1";
              str.right_just(16);
            }
            else
              str.cut(0);
          } else
          if (type == "NP" || type == "NU" || type == "VP" || type == "VN")  // Numero Positivo, NUmero, Valuta positiva o libera
          {     
            if (real::is_null(str))
              str.cut(0);
            else
            {
              // Trattamento a parte degli importi cho possono essere in Euro o Eurocent
              if (xvt_str_compare_ignoring_case(fi.class_name(), "VALUTA") == 0)
              {
                real val = str;
                if (file.importi_in_centesimi())
                {
                  val *= CENTO;
                  str = val.stringa(16, 0);
                }
                else
                  str = val.stringa(16, 2);
              }
              else  
                str.right_just(16);
            }
          } else
          if (type == "DT")
          {
            str.strip("-");
            str.right_just(16);
          }
        }
        else
        {
          if (fi.fields() > 0)
          {
            const TFieldref& fr = ((TForm_string&)fi).field(0);
            const int num = fr.file();
            const char* fn = fr.name();
            const TFieldtypes ft = cursor()->curr(num).type(fn);
            switch (ft)
            {
            case _boolfld:
              str = str.full() ? "1" : "0";
              str.right_just(16);
              break;
            case _datefld:
              if (str.len() == 10 && str[2] == '-' && str[5] == '-')
              {
                str.strip("-"); 
                str.right_just(16);
              }
              break;
            case _realfld:
              if (real::is_null(str))
                str.cut(0);
              else
              {
                // Trattamento a parte degli importi cho possono essere in Euro o Eurocent
                if (xvt_str_compare_ignoring_case(fi.class_name(), "VALUTA") == 0)
                {
                  real val = str;
                  if (file.importi_in_centesimi())
                    val *= CENTO;
                  str = val.string(16, 0);
                }
                else  
                  str.right_just(16);
              }
              break;            
            default:
              break;
            }
          }
        }
        
        if (str.not_empty())
        {
          const int vero_rigo = atoi(basecode.mid(2, 3)) - 1 + rigo;
          code.format("%c%c%03d%c%c%c", 
                      basecode[0], basecode[1], vero_rigo, 
                      basecode[5], basecode[6], basecode[7]);
          if (!rec.np_put(code, str))
          {
            file << rec;
            rec.azzera_campi_non_posizionali();
            rec.np_put(code, str);
          }
        }     
      }
    }
  }
}

bool TForm770::compatible(const TRectype& r1, const TRectype& r2)
{
  if (r1.empty() || r2.empty())
    return true;

  if (r1.get_long(QUC_CODDITTA) != r2.get_long(QUC_CODDITTA))
    return false;
  if (r1.get_char(QUC_TIPOA) != r2.get_char(QUC_TIPOA))
    return false;
  if (r1.get_long(QUC_CODANAGR) != r2.get_long(QUC_CODANAGR))
    return false;
  if (r1.get_char(QUC_CAUSALE) != r2.get_char(QUC_CAUSALE))
    return false;
  if (r1.get_int(QUC_TASSAZIONE) != r2.get_int(QUC_TASSAZIONE))
    return false;

  const real p1 = r1.get_real(QUC_PERC);
  const real p2 = r2.get_real(QUC_PERC);
  bool yes = (p1 == ZERO && p2 != ZERO) || (p1 == p2);
  return yes;
}

void TForm770::add_rec(TRectype& r1, const TRectype& r2)
{
  if (r1.empty())
    r1 = r2;
  else
  {
    for (int f = r1.items()-1; f >= 0; f--)
    {
      const char* name = r1.fieldname(f);
      if (r1.type(name) == _realfld)
      {
        real val = r1.get_real(name);
        val += r2.get_real(name);
        r1.put(name, val);
      }
    }
    r1.put(QUC_PERC, r2.get(QUC_PERC));
  }
}

long TForm770::raggruppa_c()
{
  TCursor& cur = *cursor();
  TRectype& rc = cur.curr();
  _records.destroy();
  for (cur = 0; cur.ok(); ++cur)
  {
    bool append = true;
    const int last = _records.items()-1;
    if (last >= 0)
    {
      TRectype& sum = (TRectype&)_records[last];
      if (compatible(sum, rc))
      {
        add_rec(sum, rc);
        append = false;
      }
    }
    if (append)
    {
      _records.add(rc);
      _positions.add_long(cur.pos()+1);
    }
  }                
 
  return _records.items();
}

int TForm770::first()
{
  TCursor& cur = *cursor();

  cur = 0;
  bool ok = cur.ok();
  
  if (cur.file().num() == LF_QUAC)
  {
    if (ok)
    {
      raggruppa_c();
      _index = 0;
      cur.curr() = (TRectype&)_records[0];
    }
  }

  return ok ? NOERR : _iseof;
}

int TForm770::next()
{
  TCursor& cur = *cursor();

  bool ok;
  if (cur.file().num() == LF_QUAC)
  {
    _index++;
    ok = _index < _records.items();
    if (ok)
    {
      cur = _positions.get_long(_index) - 1;
      cur.curr() = (TRectype&)_records[_index];
    }
  }
  else
  {
    ++cur;
    ok = cur.ok();
  }

  return ok ? NOERR : _iseof;
}

TRecnotype TForm770::trasfer(long codditta, TTrasferimento770& file, char rectype, int rpm)
{
  TCursor& cur = *cursor();
  
  // Filtra il cursore sulla ditta corrente
  TRectype region(cur.curr());
  region.zero();
  region.put(BSE_CODDITTA, codditta);
  cur.setregion(region, region);

  if (region.num() == LF_QUADRO_ST)
  {
    TString16 filter;
    filter.format("QLAP=%d", ANNO_DIC);
    cur.setfilter(filter);
  }

  const TRecnotype items = cur.items();
  if (items > 0)         // Se c'e almeno un record ...
  {
    cur.freeze(true);    // Per efficienza congela il cursore

    // Inizializza record col primo modulo
    TRecord770 rec(rectype);
    int modulo = 1;
    int rigo = 1;
    
    rec.set(2, file.cod_fis_dic());
    rec.set(3, modulo);

    switch (rectype)
    {
    case 'E': rec.set(8, CF_PRODUTTORE); break;
    case 'H': rec.set(9, CF_PRODUTTORE); break;
    default : break;
    }

    TPrint_section& body = section('B', odd_page);

    int err = first();   // Deve sempre tornare NOERR
    for (; err == NOERR; err = next())
    {
      // Normalizza cognome e nome delle persone fisiche
      if (cur.relation()->log2ind(LF_ANAG) > 0)
      {
        TLocalisamfile& anag = cur.file(-216);  // Anagrafica percipiente
        const TString& ragsoc = anag.get(ANA_RAGSOC);
        const TString& nome = ragsoc.mid(30);
        if (nome.blank() && anag.get_char(ANA_TIPOA) == 'F')
        {
          TToken_string rs(ragsoc, ' '); rs.strip_double_spaces();
          const int pezzi = rs.items();

          int spc = 0;
          if (pezzi >= 4)
          {
            int spc = rs.find(' ', 0);
            spc = rs.find(' ', spc+1);
          }
          else
          {
            spc = rs.find(' ', 3);
            if (spc <= 5 && rs.starts_with("DE", true) && pezzi >= 3)
              spc = rs.find(' ', spc+1);
            if (spc < 0)
              spc = rs.find(' ', 0);
          }
          if (spc >= 2)
          {
            const TString80 nome = rs.mid(spc+1);
            rs.cut(spc); rs.overwrite(nome, 30, 20);
            anag.put(ANA_RAGSOC, rs);
            if (pezzi == 2) // Salvo solo quando sono sicuro!
              anag.rewrite();
          }
        }
      }

      transfer_section(body, rigo, rec, file);
      rigo++;
      if (rigo > rpm)
      {
        // Scrivi record se non vuoto
        if (rec.ha_campi_non_posizionali_compilati())
          file << rec;
        
        // Avanza al prossimo modulo ed azzera la riga
        rigo = 1;
        modulo++;
  
        rec.azzera_campi_non_posizionali();
        rec.set(3, modulo);
      }
    }

    // Ultimo record parzialmente compilato
    if (rec.ha_campi_non_posizionali_compilati())
      file << rec;
    
    cur.freeze(false);
  }

  return items;
}

TForm770::TForm770(const char* name)
        : TForm(name), _sortedcur(NULL)
{
  const char* key = NULL;
  
  switch (TForm::cursor()->file().num())
  {
  case LF_QUALA: key = "CODDITTA|216@->RAGSOC|TIPOA|CODANAGR"; break;
  default     : break;
  }
  if (key)
    _sortedcur = new TSorted_cursor(TForm::relation(), key);
}

TForm770::~TForm770()
{
  if (_sortedcur)
    delete _sortedcur;
}

class TCache770 : public TObject
{
  TArray _files;
  TAssoc_array _tables;

public:
  const TRectype& get(int num, const char* key);
  const TRectype& get(int num, long key);
  void destroy();
} _cache770;

///////////////////////////////////////////////////////////
// TCache770
///////////////////////////////////////////////////////////

const TRectype& TCache770::get(int num, const char* key)
{
  TRecord_cache* rc = (TRecord_cache*)_files.objptr(num);
  if (rc == NULL)
  {
    rc = new TRecord_cache(num);
    _files.add(rc, num);
  }
  return rc->get(key);
}

const TRectype& TCache770::get(int num, long key)
{
  TString16 str; str << key;
  return get(num, str);
}

void TCache770::destroy()
{
  _files.destroy();
  _tables.destroy();
}

///////////////////////////////////////////////////////////
// TTrasferimento770
///////////////////////////////////////////////////////////

const char* TTrasferimento770::default_name() const 
{ return "MOD77014"; }

bool TTrasferimento770::open(const char* path, char mode, int volume)
{
  CHECK(mode == 'r' || mode == 'w', "Invalid open mode");
  close();

  if (path && *path)
  {
    _name = path;
    _name.add(default_name());
    if (volume > 0)
      _name << '_' << volume;
  }
  if (_name.empty())
  {
    _name = default_name();
    if (volume > 0)
      _name << '_' << volume;
  }
  if (mode == 'r')
    _in_stream = new ifstream(_name, ios::in | ios::binary);
  else
    _out_stream = new ofstream(_name, ios::out | ios::binary);

  return true;
}

bool TTrasferimento770::close()
{
  if (_in_stream)
  {
    delete _in_stream;
    _in_stream = NULL;
  }
  if (_out_stream)
  {
    delete _out_stream;
    _out_stream = NULL;
  }
  return true;
}

bool TTrasferimento770::write(const TRecord770& rec)
{
  bool ok = _out_stream != NULL;
  if (ok)
    (*_out_stream) << rec;
  return ok;
}

bool TTrasferimento770::read(TRecord770& rec)
{
  bool ok = _in_stream != NULL && !_in_stream->eof(); 
  if (ok)
  {
    (*_in_stream) >> rec;
    ok = rec.valid();
  }
  return ok;
}

const TString& TTrasferimento770::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 = _cache770.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 = _cache770.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 = _cache770.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
  _ragsoc_dic = rec_anagr.get(ANA_RAGSOC); //  Denominazione del dichiarante
  
  return _codfis_dic;
}

TRecnotype TTrasferimento770::conta_certificazioni() const
{
  TString query;
  query.format("USE %d\nFROM CODDITTA=%ld\nTO CODDITTA=%ld",
               LF_QUALA, _cod_ditta, _cod_ditta);
  TISAM_recordset recset(query);
  const TRecnotype nc = recset.items();
  return nc;
}

bool TTrasferimento770::casella_prospetto_st() const
{
  // Il quadro ST e' il "vecchio" quadro L
  TString query;
  query.format("USE %d\nSELECT QLAP=%d\nFROM CODDITTA=%ld\nTO CODDITTA=%ld",
               LF_QUADRO_ST, ANNO_DIC, _cod_ditta, _cod_ditta);
  TISAM_recordset recset(query);
  const TRecnotype nr = recset.items();
  return nr > 0;
}

bool TTrasferimento770::casella_prospetto_sx() const
{
  // Il quadro SX sono le ritenute versate in eccesso
  TString query;
  query.format("USE %d\nSELECT QLAP=%d\nFROM CODDITTA=%ld\nTO CODDITTA=%ld",
               LF_QUADRO_ST, ANNO_DIC, _cod_ditta, _cod_ditta);
  TISAM_recordset recset(query);
  bool found = false;
  for (bool ok = recset.move_first(); ok && !found; ok = recset.move_next())
  {
    const real verecc = recset.get("QLRITVEREC").as_real();
    found = !verecc.is_zero();
  }
  return found;
}


bool TTrasferimento770::append_record_b()
{
  bool ok = false;
  if (save_headers())
  {
    TRecord770 rec('B');       // Compila record di testata B
    rec.set(2, cod_fis_dic());
    rec.set(3, 1);             // Modulo (deve essere sempre 1)
    rec.set(7, CF_PRODUTTORE); // CF del produttore Sirio

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

    TToken_string key;
    key << _tipoa_dic << '|' << _codan_dic;
    const TRectype& rec_anagr = _cache770.get(LF_ANAG, key);

    key = rec_anagr.get(ANA_STATORES);
    key.add(rec_anagr.get(ANA_COMRES), 1);
    const TRectype& rec_comres = _cache770.get(LF_COMUNI, key);
    
    if (_tipoa_dic == 'G')
    {
      const TRectype& rec_anagiu = _cache770.get(LF_ANAGGIU, _codan_dic);
      rec.set(33, rec_anagiu.get(ANG_NATGIU));
      
      rec.set(35, rec_comres.get(COM_DENCOM));
      rec.set(36, rec_comres.get(COM_PROVCOM));
      TString indirizzo;
      indirizzo << rec_anagr.get(ANA_INDRES) << ' ' << rec_anagr.get(ANA_CIVRES);
      indirizzo.strip_double_spaces(); indirizzo.cut(35);
      rec.set(37, rec_anagr.get(ANA_CAPRES));
      rec.set(38, rec_anagr.get(ANA_COMRES));
      rec.set(39, indirizzo);

      rec.set(46, rec_anagiu.get(ANG_STATOSOC));
      rec.set(47, rec_anagiu.get(ANG_SITSOC));
    }

    rec.set(75, 1); // Redazione della dichiarazione (sezione I)
    rec.set(76, 0); // Comunicazioni relative a certificazioni lavoro dipendente
    const int autonomi = conta_certificazioni();
    const bool prosp_st = casella_prospetto_st();
    const bool prosp_sx = casella_prospetto_sx();
    rec.set(77, autonomi);
    rec.set(78, autonomi > 0); // casella prospetto SS
    rec.set(79, prosp_st);     // casella prospetto ST
    rec.set(80, false);        // casella prospetto SV
    rec.set(81, prosp_sx);     // casella prospetto SX
    rec.set(82, false);        // casella prospetto SY
    
    rec.set(94, 1);            // Firma del dichiarante

    rec.set(100, 0);           // situazioni particolari: vale sempre 0

    const TRectype& rec_nditte = _cache770.get(LF_NDITTE, _cod_ditta);

    key.cut(0) << "F|" << rec_nditte.get(NDT_RAPPR);
    const TRectype& rec_rap = _cache770.get(LF_ANAG, key);
    const TRectype& rec_rap_fis = _cache770.get(LF_ANAGFIS, key.get(1));
    if (!rec_rap_fis.empty())
    {
      key.cut(0) << "|" << rec_rap_fis.get(ANF_COMNASC);
      const TRectype& rec_com_nas = _cache770.get(LF_COMUNI, key);
      key.cut(0) << "|" << rec_rap.get(ANA_COMRES);
      const TRectype& rec_com_res = _cache770.get(LF_COMUNI, key);
      rec.set(126, rec_rap.get(ANA_COFI));
      rec.set(127, rec_nditte.get(NDT_CARRAPP));
      rec.set(131, rec_rap.get(ANA_RAGSOC).left(24));
      rec.set(132, rec_rap.get(ANA_RAGSOC).mid(30, 20));
      rec.set(133, rec_rap_fis.get(ANF_SESSO));
      rec.set(134, rec_rap_fis.get_date(ANF_DATANASC));
      rec.set(135, rec_com_nas.get(COM_DENCOM));
      rec.set(136, rec_com_nas.get(COM_PROVCOM));
    }
    else
      cantread_box("dati del legale rappresentante");

    // Ignoro la parte "estera"

    ok = write(rec);
  }
  return ok;
}

// Determina a partire dal codice quadro (LA o ST) il tipo record (H o E) ed il numero di record per modulo (1 o 12)
// Ritorna il form da cui prelevare i codici (come AUXXX010) da inserire nei tracciati
TForm770& TTrasferimento770::form(const char* quadro, char& tiporec, int& rpm)
{
  CHECK(quadro && *quadro != '\0' && quadro[1] != '\0', 
        "Codice quadro non valido");

  const char* name = NULL;
  switch(quadro[1])
  {
  case 'A': name = "77qla"; tiporec = 'H'; rpm = 1; break;
  case 'T': name = "77qst"; tiporec = 'E'; rpm = 12; break;
  default : name = NULL; break;
  }

  TForm770* frm = (TForm770*)_form.objptr(name);
  if (frm == NULL)
  {
    frm = new TForm770(name);
    _form.add(name, frm);
  }

  return *frm;
}

long TTrasferimento770::append_quadro(const char* quadro, long codditta, TProgress_monitor& pi)
{
  TString str;
  str << TR("Trasferimento quadro ") << quadro << TR(" ditta ") << codditta;
  pi.set_text(str);
  
  char tipo;
  int rpm;
  TForm770& frm = form(quadro, tipo, rpm);
  long items = frm.trasfer(codditta, *this, tipo, rpm);
  return items;
}

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;
}

void TTrasferimento770::riepiloga_ss(const TRecord770& rec, TArray& riep_ss) const
{
  TString8 code;
  real val;
  for (int i = 0; rec.np_get_real(i, code, val); i++) if (code.starts_with("AU") && !val.is_zero())
  {   
    const TString& key = code.right(3);
    const int num = atoi(key);
    if (num >= 27 && num <= 35) 
    {
      real& r = real_at(riep_ss, num-27);  // AU_27-AU_47 -> SS003001
      r += val;
    } else
    if (num >= 47 && num <= 55) 
    {
      real& r = real_at(riep_ss, num-47);
      r -= val;
    }
  }
}

void TTrasferimento770::riepiloga_sx(const TRecord770& rec, TArray& riep_sx) const
{
  TString8 code;
  real val;
  for (int i = 0; rec.np_get_real(i, code, val); i++) if (code.starts_with("ST") && !val.is_zero())
  {   
    const int rig = atoi(code.mid(2,3));
    const int col = atoi(code.mid(5,3));
    if (rig >= 2 && rig <= 25 && col >= 4 && col <= 5) 
    {
      real& r = real_at(riep_sx, 5); // SX004005
      r += val;
    }
  }
}


bool TTrasferimento770::split(const char* path)
{
  close();

  const long records = fsize(_name) / TOTAL_SIZE;
  TRecnotype totale[26]; memset(totale, 0, sizeof(totale));

  TRecord770 rec;

  unsigned long disk_size = 15*1024*1024;   // Max 15 Mb per fornitura
  const bool magnetic = ::xvt_fsys_is_removable_drive(path) !=0;
  if (magnetic)
  {
    if (!yesno_box(FR("Inserire il primo disco del trasferimento nell'unita' %s\n"
                   "Tutti i dischi devono essere vuoti ed avere la stesso formato.\n"
                   "Si desidera iniziare il trasferimento?"), path))
      return false;

    disk_size = ::xvt_fsys_get_disk_size(path, 'b');
  }
  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;

  TProgind pi(records, TR("Generazione file trasferimento"), false, true);
  
  // Read from start
  open("", 'r');
  
  for (int volume = 1; volume <= volumes; volume++)
  {
    if (magnetic && volume > 1)
    {
      if (!yesno_box(FR("Inserire il disco %d di %d:\nSi desidera proseguire il trasferimento?"),
                     volume, volumes))
        break;
    }

    // Specifica volume solo se necessario
    const int v = (volumes > 1 && !magnetic) ? volume : 0; 
    TTrasferimento770 outfile(path, 'w', v);
  
    if (_save_headers)
    {      
      rec.tipo_record('A');      // Compila record di testata A
      rec.set(3, "77S14");       // Codice fornitura
      rec.set(4, 1);             // Tipo fornitore 01 = Soggetto che invia la propria dichiarazione
      rec.set(5, cod_fis_dic()); // Codice fiscale del fornitore
      if (volumes > 1)
      {
        rec.set(7, volume);
        rec.set(8, volumes);
      }
      else
      {
        rec.set(7, 0);
        rec.set(8, 0);
      }
  
      outfile << rec;  // Scrive record testata
    }
    
    long written = 0;
  
    TArray riep_ss, riep_sx;
    
    if (records > 0)
    {
      while (read(rec))
      {
        pi.addstatus(1);
  
        const char tipo_rec = rec.tipo_record();
        if (tipo_rec <= 'A' || tipo_rec == 'F' || tipo_rec == 'J' || tipo_rec >= 'Z')
          continue;

        if (_save_headers && tipo_rec > 'E' && riep_sx.items() > 0)
        {
          TRecord770 recf('F');
          recf.set(2, cod_fis_dic());
          recf.set(3, 1);
          recf.set(8, CF_PRODUTTORE);
          FOR_EACH_ARRAY_ITEM(riep_sx, i, obj)
          {
            const real* val = (const real*)obj;
            if (val != NULL)
            {
              TString8 code;
              code.format("SX004%03d", i);
              recf.np_put(code, *val);
            }
          }
          outfile << recf;
          totale['F'-'A']++; // Praticamente vale sempre 1
          riep_sx.destroy(); // La prossima volta non deve salvare nulla
        }
  
        outfile << rec;
        written++;
  
        // Tengo traccia degli eventuali riepiloghi
        if (_save_headers)
        {
          totale[tipo_rec - 'A']++;
          switch (tipo_rec)
          {
          case 'E': riepiloga_sx(rec, riep_sx); break;
          case 'H': riepiloga_ss(rec, riep_ss); break;
          default : break;
          }
        }
  
        if (written >= records_per_disk)
          break;
      }
    }  

    if (_save_headers)
    {
      // Compila record di riepilogo SS
      if (!riep_ss.empty())
      {
        rec.tipo_record('J');
        rec.set(2, cod_fis_dic());
        rec.set(3, 1);
        rec.set(8, CF_PRODUTTORE);
        FOR_EACH_ARRAY_ITEM(riep_ss, i, obj)
        {
          const real* val = (const real*)obj;
          if (val != NULL && !val->is_zero())
          {
            TString8 code;
            code.format("SS003%03d", i+1);   // i=0 -> SS0003001 -> AUXXX027-AUXXX047
            rec.np_put(code, *val);
          }
        }
        outfile << rec;
        totale['J'-'A']++; // Praticamente vale sempre 1
      }

      // Compila record di coda
      rec.tipo_record('Z');
      rec.set(3, totale['B'-'A']);  // Totale B
      rec.set(4, totale['D'-'A']);  // Totale D
      rec.set(5, totale['E'-'A']);  // Totale E
      rec.set(6, totale['F'-'A']);  // Totale F
      rec.set(7, totale['G'-'A']);  // Totale G
      rec.set(8, totale['H'-'A']);  // Totale H
      rec.set(9, totale['I'-'A']);  // Totale I
      rec.set(10,totale['J'-'A']);  // Totale J
  
      // Scrive record di coda
      outfile << rec;
    }
  }  
  return true;
}

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

TTrasferimento770::TTrasferimento770(const char* path, char  mode, int volume)
: _in_stream(NULL), _out_stream(NULL), _save_headers(false), _centesimi(false)
{
  open(path, mode, volume);
}

TTrasferimento770::~TTrasferimento770()
{
  close();
}

///////////////////////////////////////////////////////////
// main
///////////////////////////////////////////////////////////

class TTransfer770_msk : public TAutomask
{
protected:
  virtual bool on_field_event(TOperable_field& of, TField_event fe, long jolly);

public:         
  TTransfer770_msk();
};

bool TTransfer770_msk::on_field_event(TOperable_field& of, TField_event fe, long jolly)
{
  switch (of.dlg())
  {
  case F_PATH:
    if (fe == fe_modify || fe == fe_close)
    {
      TFilename name = of.get();
      if (!name.exist())
        return cantread_box(name);
    }
    break;
  default:
    break;
  }
  return true;
}

TTransfer770_msk::TTransfer770_msk() : TAutomask("777100a") 
{ 
  /*
  int anno = ini_get_int(CONFIG_STUDIO, "77", ANNO_SEL);
  if (anno <= 0)
  {
    const TDate oggi(TODAY);
    anno = oggi.year()-1;
  }
  set(F_ANNO, anno);
  */
  set(F_ANNO, ANNO_DIC);
}

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

void TTransfer770_app::main_loop()
{
  TTransfer770_msk m;

  while (m.run() == K_ENTER)
  {
    TFilename tmp; tmp.tempdir();
    TTrasferimento770 t(tmp, 'w');
    t.save_headers(m.get_bool(F_HEADERS));
    t.set_cent_mode(m.get_bool(F_CENT));

    TRelation rel_base(LF_BASE);
    TRectype da_rec(LF_BASE), a_rec(LF_BASE);
    da_rec.put(BSE_CODDITTA, m.get(F_DADITTA));
    a_rec.put(BSE_CODDITTA, m.get(F_ADITTA));

    TString16 filter;
/*
    filter << '(' << BSE_ANNODIC << '=' << m.get(F_ANNO) << ")||"
           << '(' << BSE_ANNODIC << "=0)"; 
*/
    TCursor cur_base(&rel_base, filter, 1, &da_rec, &a_rec);
    const TRecnotype items = cur_base.items();
    cur_base.freeze();
    
    if (items > 0)
    {
      TProgress_monitor pi(items, TR("Generazione file di trasferimento"));
      for (cur_base = 0; cur_base.ok(); ++cur_base)
      {
        const TRectype& base = cur_base.curr();
        const long codditta = base.get_long(BSE_CODDITTA);
        pi.addstatus(1);
        t.read_codfis_dic(base);

        t.append_record_b();
        t.append_quadro("ST", codditta, pi);
        t.append_quadro("LA", codditta, pi);
      }
    }

    tmp = m.get(F_PATH);
    if (t.split(tmp))
    {
      tmp.add(t.default_name());
      TString msg; msg << TR("E' stato generato il file ") << tmp;
      xvt_dm_popup_message(msg);
    }
    else
    {
      tmp.add(t.default_name());
      cantwrite_box(tmp);
    }
    t.remove();
  }
}

int m777100(int argc, char* argv[])
{
  TTransfer770_app app;
  app.run(argc, argv, TR("Invio 770"));
  return 0;
}