#include <applicat.h>
#include <progind.h>
#include <recarray.h>
#include <relation.h>
#include <tabutil.h>

#include "in0.h"
#include "in0200a.h"
#include "inlib01.h"

#include <nditte.h>
#include <anagr.h>

///////////////////////////////////////////////////////////
// TRecord_intra
///////////////////////////////////////////////////////////

struct TIntra_context
{
  char _tipo;
  long _progr;
  char _freq;
  int _anno;
  int _periodo;
  long _righe_riep;
  real _totale_riep;
  long _righe_rett;
  real _totale_rett;
  unsigned long _written;
  unsigned long _disksize;

  TIntra_context();
};

TIntra_context::TIntra_context()
{
  _tipo = 'C'; _freq = 'T'; _anno = 1999; _periodo = 1;
  _progr = _righe_riep = _righe_rett = 0L;
  _written = _disksize = 0L;
}

class TRecord_intra : public TString
{                         
  int _ndec;
  
protected:
  virtual void print_on(ostream& o) const;

public:
  void reset(const TIntra_context& ic);
  void reset_data();
  void put(const char* str, int pos, int dim, const char* flags = "");
  void put(real num, int pos, int dim, int dec = 0);
  void put(long num, int pos, int dim);
  void put(char chr, int pos);
  void genera_testata(const TIntra_context& ic);
  void put(const TRectype& rec, TIntra_context& ic);

  TRecord_intra();
  virtual ~TRecord_intra() { }
};

// Scrive un campo generico sul record di invio
void TRecord_intra::put(const char* str, int pos, int dim, const char* flags)
{
  CHECKD(pos > 0 && pos < size(), "Invalid field position:", pos);
  CHECKD(dim > 0 && dim <= 200, "Invalid field dimension:", dim);
  TString256 val(str);
  if (val.len() < dim)
  {
    const bool rj = strchr(flags, 'R') != NULL;
    const bool zf = strchr(flags, 'Z') != NULL;
    if (rj)
      val.right_just(dim, zf ? '0' : ' ');
    else
      val.left_just(dim, zf ? '0' : ' ');
  }
  else
    val.cut(dim);

  overwrite(val, pos-1);
}

// Scrive un campo numerico sul record di invio
void TRecord_intra::put(real num, int pos, int dim, int dec)
{
  TString80 str;
  if (!num.is_zero())
  {
    num.round(dec);
    
    const bool negativo = num < ZERO;
    if (negativo) num *= -1.0;

    if (dec < 0) 
    {
      str = num.string(dim-dec, 0, '0');
      str.rtrim(-dec);
    }
    else
    {
      str = num.string(dim, dec, '0');
    }
    if (negativo)
      str[dim-1] += 64;
  }
  put(str, pos, dim, "RZ");
}

// Scrive un campo intero sul record di invio
void TRecord_intra::put(long num, int pos, int dim)
{
  TString16 str; 
  str.format("%0*ld", dim, num);
  put(str, pos, dim, "RZ");
}

// Scrive un campo carattere sul record di invio
void TRecord_intra::put(char chr, int pos)
{
  const char str[2] = { chr, '\0' };
  put(str, pos, 1);
}

// Azzera il record
void TRecord_intra::reset(const TIntra_context& ic)
{
  spaces();
  put("EUROA", 1, 5);
  const TRectype& ditta = cache().get(LF_NDITTE, main_app().get_firm());
  TString16 cod; 
  cod.format("%c|%ld", ditta.get_char(NDT_TIPOA), ditta.get_long(NDT_CODANAGR));
  const TRectype& anagr = cache().get(LF_ANAG, cod);
  put(anagr.get(ANA_PAIV), 6, 11);
  put(ic._progr, 17, 6);
}

void TRecord_intra::reset_data()
{
  const TString80 key = left(22);
  spaces();
  overwrite(key, 0);
}

// Scrive la testata con le informazioni della ditta
void TRecord_intra::genera_testata(const TIntra_context& ic)
{
  reset(ic);

  put('0', 23);         // Tipo record frontespizio
  put("", 24, 5, "RZ"); // Numero riga (0 per frontespizio)
 
  put(ic._tipo, 29);    // Tipo riepilogo
  put(ic._anno % 100, 30, 2);
  put(ic._freq, 32);
	const int periodo = ic._freq == 'A' ? 0 : ic._periodo;
  
	put(periodo, 33, 2);

  TString16 cod = mid(5, 11); // Ricopia la parita iva della ditta
  put(cod, 35, 11);  

  const TRectype& ditta = cache().get(LF_NDITTE, main_app().get_firm());
  put(ditta.get_bool("PRESELEN") ? '1' : '0', 46);
  put(ditta.get_bool("CESSIVA") ? '1' : '0', 47);
  
  cod.cut(0);
  cod << ditta.get_char("TIPOSOGDEL") << '|';
  cod << ditta.get("CODSOGDEL");
  if (cod.len() >= 3)
  {
    const TRectype& sogdel = cache().get(LF_ANAG, cod);
    put(sogdel.get(ANA_PAIV), 48, 11);
  }
  else
    put("", 48, 11, "Z");

  put(ic._righe_riep, 59, 5);
  put(ic._totale_riep, 64, 13, _ndec);
  put(ic._righe_rett, 77, 5);
  put(ic._totale_rett, 82, 13, _ndec);
}

// Scrive un intero record del file riepiloghi/rettifiche
void TRecord_intra::put(const TRectype& rec, TIntra_context& ic)
{
  reset_data();

  put(rec.get_long("NUMRIG"), 24, 5);
  const char tipo = rec.get_char("TIPO");
  switch (tipo)
  {
  case 'A':
    put('1', 23);
    put(rec.get("STATO"), 29, 2);
    put(rec.get("PIVA"), 31, 12);
    put(rec.get_real("AMMLIRE"), 43, 13, _ndec);
    put(rec.get_real("AMMVALUTA"), 56, 13);
    put(rec.get_char("NATURA"), 69);
    put(rec.get("NOMENCL").mid(0,4), 70, 4, "Z");  //il campo viene spezzato in 3 pezzi
    put(rec.get("NOMENCL").mid(4,2), 74, 2, "Z");
    put(rec.get("NOMENCL").mid(6,2), 76, 2, "Z");
    if (ic._freq == 'M')
    {
      put(rec.get_real("MASSAKG"), 78, 10, 0);
      put(rec.get_real("MASSAUMS"), 88, 10, 0);
      put(rec.get_real("VALSTAT"), 98, 13, _ndec);
      put(rec.get_char("CONSEGNA"), 111);
      put(rec.get_char("TRASPORTO"), 112);
      put(rec.get("PAESE"), 113, 2);
      put(rec.get("PAESEORIG"), 115, 2);
      put(rec.get("PROV"), 117, 2);
    }
    else
      put("", 78, 100);
    break;
  case 'B':
    {
      put('2', 23);
      if (ic._freq == 'M')
        put(rec.get("PERETT"), 29, 2, "RZ");
      else
        put("", 29, 2, "RZ");
      if (ic._freq == 'T')
        put(rec.get("PERETT")[1], 31);
      else
        put('0', 31);
      put(rec.get("ANNORETT").right(2), 32, 2, "RZ");
      put(rec.get("STATO"), 34, 2);
      put(rec.get("PIVA"), 36, 12);
      put(rec.get("SEGNORETT"), 48, 1);
      put(rec.get_real("AMMLIRE"), 49, 13, _ndec);
      put(rec.get_real("AMMVALUTA"), 62, 13);
      put(rec.get_char("NATURA"), 75);
      put(rec.get("NOMENCL").mid(0,4), 76, 4, "Z");  //il campo viene spezzato in 3 pezzi
      put(rec.get("NOMENCL").mid(4,2), 80, 2, "Z");
      put(rec.get("NOMENCL").mid(6,2), 82, 2, "Z");
      if (ic._freq == 'M')
        put(rec.get_real("VALSTAT"), 84, 13, _ndec);
      else
        put("", 84, 13);
    }
    break;
  case 'C':
    put('1', 23);
    put(rec.get("STATO"), 29, 2);
    put(rec.get("PIVA"), 31, 12);
    put(rec.get_real("AMMLIRE"), 43, 13, _ndec);
    put(rec.get_char("NATURA"), 56);
    put(rec.get("NOMENCL").mid(0,4), 57, 4, "Z");  //il campo viene spezzato in 3 pezzi
    put(rec.get("NOMENCL").mid(4,2), 61, 2, "Z");
    put(rec.get("NOMENCL").mid(6,2), 63, 2, "Z");
    if (ic._freq == 'M')
    {
      put(rec.get_real("MASSAKG"), 65, 10);
      put(rec.get_real("MASSAUMS"), 75, 10);
      put(rec.get_real("VALSTAT"), 85, 13, _ndec);
      put(rec.get_char("CONSEGNA"), 98);
      put(rec.get_char("TRASPORTO"), 99);
      put(rec.get("PAESE"), 100, 2);
      put(rec.get("PROV"), 102, 2);
    }
    else
      put("", 65, 100);
    break;
  case 'D':
    {
      put('2', 23);
      if (ic._freq == 'M')
        put(rec.get("PERETT"), 29, 2, "RZ");
      else
        put("", 29, 2, "RZ");
      if (ic._freq == 'T')
        put(rec.get("PERETT")[1], 31);
      else
        put('0', 31);
      put(rec.get("ANNORETT").right(2), 32, 2, "RZ");
      put(rec.get("STATO"), 34, 2);
      put(rec.get("PIVA"), 36, 12);
      put(rec.get("SEGNORETT"), 48, 1);
      put(rec.get_real("AMMLIRE"), 49, 13, _ndec);
      put(rec.get("NATURA"), 62, 1);
      put(rec.get("NOMENCL").mid(0,4), 63, 4, "Z");  //il campo viene spezzato in 3 pezzi
      put(rec.get("NOMENCL").mid(4,2), 67, 2, "Z");
      put(rec.get("NOMENCL").mid(6,2), 69, 2, "Z");
      if (ic._freq == 'M')
        put(rec.get_real("VALSTAT"), 71, 13, _ndec);
      else
        put("", 71, 13);
    }
    break;
  default:
    NFCHECK("Record di tipo sconosciuto: %c", tipo);
    break;
  }
}

// Scrive su file il record
void TRecord_intra::print_on(ostream& o) const
{
  ((TRecord_intra*)this)->rtrim();
  TString::print_on(o);
  o << endl;
}

TRecord_intra::TRecord_intra() : TString(132)
{ 
  _ndec = TCurrency::get_firm_dec() > 0 ? 0 : -3;
}


///////////////////////////////////////////////////////////
// TDischetto_mask
///////////////////////////////////////////////////////////

class TDischetto_mask : public TIntra_mask
{
protected:
  virtual bool on_field_event(TOperable_field& o, TField_event e, long jolly);
  virtual short type_field() const { return F_TIPO; }
  virtual short period_field() const { return F_PERIODO_M; }
  virtual int anno() const { return get_int(F_ANNO); }

  long calcola_totale(TCursor& cur, real& tot) const;
  bool write_record(ofstream& out, const TRecord_intra& rec, TIntra_context& ic);
  
  void proponi_numero();

public:
  void genera_dischetto(char tip, int mode = 0);
  
  TDischetto_mask();
};

void TDischetto_mask::proponi_numero()
{
  const char tip = tipo();
  int a = anno(), p = periodo(); 
  long d = 0;
  
  TTable ird("IRD");    
  int err = ird.last();
  while (err == NOERR)
  {
    const TString16 str = ird.get("CODTAB");
    a = atoi(str.mid(1,4));
    p = atoi(str.mid(5,2));
    d = ird.get_long("I0");
    if (tip == 'T' || tip == str[0])
      break;
    err = ird.prev();  
  }

  if (anno() > a || periodo() > p)
    set(F_NUMERO, d+1);
  else  
    set(F_NUMERO, d == 0 ? 1L : d);
  set(F_LAST, d);
}

bool TDischetto_mask::on_field_event(TOperable_field& o, TField_event e, long jolly)
{
  switch (o.dlg())
  {
  case F_ANNO:
    if (e == fe_init || e == fe_modify)
    {
      const int anno = atoi(o.get());
      const char fa = frequenza(anno, 'A');
      const char fc = frequenza(anno, 'C');
      TList_field& list = (TList_field&)field(type_field());
      TToken_string codes = "A|C";
      TToken_string descr;
      descr.add(TR("Acquisti"));
			descr.add(TR("Cessioni"));
      if (fa == fc)
      {
        codes.add("T");
        descr.add(TR("Tutti"));
      }
      list.replace_items(codes, descr);

      proponi_numero();                
    }
    break;
  case F_TIPO:
  case F_PERIODO_M:
  case F_PERIODO_T:
  case F_PERIODO_A:
    if (e == fe_modify)
      proponi_numero();
    break;
  case F_RIEPILOGHI:
    if (e == fe_button)         
    {
      const char tip = tipo();
      if (tip == 'T')
      {
        ::genera_riepiloghi('A', anno(), periodo());
        ::genera_riepiloghi('C', anno(), periodo());
      }
      else
        ::genera_riepiloghi(tip, anno(), periodo());
    }
    break;
  default:break;
  }
  return TIntra_mask::on_field_event(o, e, jolly);
}

long TDischetto_mask::calcola_totale(TCursor& cur, real& tot) const
{
  TWait_cursor arrow;
  const long items = cur.items();
  cur.freeze();

  tot = ZERO;

  const TRectype& rec = cur.curr();
  for (cur = 0L; cur.pos() < items; ++cur)
  {
    const char tipo = rec.get_char("TIPO");
    const real val = rec.get_real("AMMLIRE");
// Da chiarire: come sommare le rettifiche negative!
    if ((tipo == 'B' || tipo == 'D') && rec.get_char("SEGNORETT") == '-')
      tot -= val;       // Rettifiche negative
    else
      tot += val;
  }
  return items;
}

bool TDischetto_mask::write_record(ofstream& out, const TRecord_intra& rec, TIntra_context& ic)
{
  out << rec;

  bool good = out.good() != 0;
  if (good)
    ic._written += rec.len()+2;

  if (ic._written >= ic._disksize)
  {
    out.close();
    
    TFilename name; 
    name = get(F_DISCO); 
    name.add(get(F_PATH));
    name.add("scambi.cee");
    message_box(TR("Inserire un nuovo dischetto prima di continuare."));
    
    out.open(name);
    if (out)
    {
      ic._written = 0;
      good = TRUE;
    }
  }
  if (!good)
    error_box(TR("Errore di scrittura su disco: ripetere l'operazione."));
  return good;
}

// mode 0 = ask; 1 = append; 2 = remove
void TDischetto_mask::genera_dischetto(char tip, int mode)
{
  TIntra_context ic;
  ic._tipo = tip;
  ic._anno = anno();
  ic._freq = frequenza(ic._anno);
  ic._periodo = periodo();
  ic._progr = get_long(F_NUMERO);

  TString16 codtab; codtab.format("%c%04d%02d", ic._tipo, ic._anno, ic._periodo);

  TTable ird("IRD");
  ird.put("CODTAB", codtab);
  const bool exist = ird.read() == NOERR;

  if (exist)
  {   
    const char* ac = tip == 'A' ? TR("agli acquisti")  : TR("alle cessioni");
    if (!yesno_box(FR("Il dischetto relativo %s del periodo indicato � gi� stato generato:\n"
                   "Si desidera proseguire ugualmente?"), ac))
      return;
  }
    
  TRelation rel(LF_RIEPRETT);
  TRectype filter(LF_RIEPRETT);
  filter.put("TIPO", ic._tipo);
  filter.put("ANNO", ic._anno);
  filter.put("PERIODO", periodo_str());
  TCursor riep(&rel, "", 1, &filter, &filter);

  ic._righe_riep = calcola_totale(riep, ic._totale_riep);

  filter.put("TIPO", char(ic._tipo+1));
  TCursor rett(&rel, "", 1, &filter, &filter);

  ic._righe_rett = calcola_totale(rett, ic._totale_rett);
  
  TFilename name; 
  name = get(F_DISCO); 
  name.add(get(F_PATH));
  name.add("scambi.cee");

  if (name.exist())
  {
    bool do_remove = (mode == 2);
    if (mode == 0)
    {
      do_remove = yesno_box(FR("Il file %s, esiste gia': si desiderla eliminarlo?\n"          
                  "Rispondendo SI i dati sul dischetto verranno azzerati.\n"
                  "Rispondendo NO i dati verranno accodati a quelli gi� presenti"), 
                  (const char*)name);
    }
    if (do_remove)              
      ::remove(name);
  }

  ofstream out(name, ios::out | ios::app);
  if (!out)
  {
    error_box(FR("Impossibile creare il file %s\n"
              "Assicurarsi di avere inserito un dischetto\n"
              "formattato e ripetere nuovamente l'operazione."),
              (const char*)name);
    return;
  }
  ic._disksize = xvt_fsys_get_disk_size(name, 'b') - (64L*1024L);
  ic._written = 0;

  const long total = ic._righe_riep + ic._righe_rett;

  TProgind pi(total, TR("Generazione scambi.cee"), FALSE, TRUE);
  TRecord_intra rec;
  rec.genera_testata(ic);
  out << rec;
  
  rec.reset_data();
  for (riep = 0L; riep.pos() < ic._righe_riep; ++riep)
  {
    pi.addstatus(1);
    rec.put(riep.curr(), ic);
    if (!write_record(out, rec, ic))
      return;
  }
  rec.reset_data();
  for (rett = 0L; rett.pos() < ic._righe_rett; ++rett)
  {
    pi.addstatus(1);
    rec.put(riep.curr(), ic);
    if (!write_record(out, rec, ic))
      return;
  }

  codtab.format("%c%04d%02d", ic._tipo, ic._anno, ic._periodo);
  
  ird.put("CODTAB", codtab);
  ird.put("I0", ic._progr); // Numero progressivo dischetto
//  ird.put("I1", ??? );
  ird.put("I2", ic._righe_riep);
  ird.put("I3", ic._righe_rett);
  
  ird.put("R0", ic._totale_riep);
  ird.put("R1", ic._totale_rett);
  if (exist)
    ird.rewrite();
  else
    ird.write();
	set(F_NUMERO, ic._progr + 1);

}

TDischetto_mask::TDischetto_mask()
: TIntra_mask("in0200a")
{ }

///////////////////////////////////////////////////////////
// TDischetto_app 
///////////////////////////////////////////////////////////

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

void TDischetto_app::main_loop()
{
  open_files(LF_TABCOM, LF_TAB, LF_CLIFO, 
             LF_INTRA, LF_RINTRA, LF_RIEPRETT, 0);
  TDischetto_mask m;
  while (m.run() == K_ENTER)
  {                 
    const char tip = m.tipo();
    if (tip == 'T')
    {
      m.genera_dischetto('A', 2);
      m.genera_dischetto('C', 1);
    }
    else
      m.genera_dischetto(tip);
  }
}

int in0200(int argc, char* argv[])
{
  TDischetto_app a;
  a.run(argc, argv, TR("Generazione dischetti INTRA"));
  return 0;
}