#include <applicat.h>
#include <isam.h>
#include <mask.h>
#include <prefix.h>
#include <progind.h>
#include <relation.h>
#include <tabutil.h>
#include <utility.h>

#include <mov.h>
#include <urldefid.h>

#include "cg5.h"
#include "cg5400.h"

class TRipristina_stampa : public TApplication
{
  enum TOperation { restore_reg, restore_inl };
  TOperation _op;

protected: // TApplication
  virtual bool create() ;                         
  virtual bool menu(MENU_TAG);
  
public:
  void azzera_lim(int year, int month) const;

  bool reg_restore(const TString& reg, int year, int month, int day, bool giornale);
  bool inl_restore(const TString& lbu, int year, int month);

  TRipristina_stampa() : _op(restore_reg) {}
};


// Azzera flag di stampato della liquidazione mensile dal mese dato in poi
void TRipristina_stampa::azzera_lim(int year, int month) const
{
  TTable lim("LIM");                // Azzera i flag di stampa liquidazione
  lim.put("CODTAB", format("%04d%02d", year, month));
  for (int err = lim.read(_isgteq); err == NOERR; err = lim.next())
  {                 
    if (atoi(lim.get("CODTAB").left(4)) != year) break;
    const bool stampato = lim.get_bool("B1");
    if (stampato)
    {
      lim.zero("B1");
      lim.rewrite();
    }
  }
}

bool TRipristina_stampa::reg_restore(const TString& regist, 
                                     int year, int month, int day,
                                     bool giornale)
{
  CHECKD(month > 0 && month < 13, "Mese errato ", month);

  bool ok = TRUE;
  const char * const fieldname = giornale ? MOV_STAMPATO : MOV_REGST;
  TRelation rel(LF_MOV);
  TLocalisamfile& mov = rel.lfile();
  TTable reg("REG");
  TString16 s; s.format("%04d%s", year, (const char*)regist);
  
  reg.zero();
  reg.put("CODTAB", s);
  
  TDate inizio_anno(1, 1, year);
  if (reg.read(_isequal, _lock) == NOERR)
  {
    TDate dlast(reg.get_date("D3"));

    if (giornale)
    {
      TTable esc("ESC");
      esc.zero();
      esc.put("CODTAB", format("%04d", year));
      if (esc.read() != NOERR)
        return error_box("Esercizio %s assente", year);
      
      TDate wd(esc.get_date("D0"));
      inizio_anno = wd;  

      // Determina il corretto anno solare dell'esercizio: se il giorno di inizio esercizio
      // e successivo alla data di ripristino allora mi trovo nell'anno solare successivo
      year = wd.year();
      if (wd.month() > month || (wd.month() == month && wd.day() > day))
        year++;
    }                  
    
    const int ld = TDate::last_day(month, year);
    if (day > ld) day = ld;
    TDate d(day, month, year);                   // Data di ripristino
    
    TRectype from(mov.curr()); from.zero();
    TRectype to(from);
    TString80 filter;
    
    from.put(MOV_DATAREG, d);
    to.put(MOV_DATAREG, dlast);
    if (!giornale)
      filter.format("%s==\"%s\"", MOV_REG, (const char*)regist);
    
    TCursor cursor(&rel, filter, 2, &from, &to);
    long last_num = 0L;
    
    cursor = 0L;
    const long nitems = cursor.items();
    
    TProgind p(nitems ? nitems : 1, format("Ripristino stampa del registro %s", (const char*)regist) , TRUE, TRUE, 70);
    
    if (giornale)
    {
      last_num = mov.get_long(MOV_NUMGIO);
      if (last_num > 0) last_num--;
      CHECK(last_num >= 0, "Ultimo numero stampato impossibile"); 
    }
    for (; ok && cursor.pos() < cursor.items(); ++cursor)
    {
      p.addstatus(1);
      mov.read(_isequal, _lock);
      mov.zero(fieldname);                 // Azzera flag STAMPATO o REGST
      
      ok = (mov.rewrite() == NOERR);
      if (!ok)
        error_box("Errore nell'aggiornamento del movimento %ld.\n Errore n. %d",
                  mov.get_long(MOV_NUMREG), mov.status());
    }
    
    if (d > inizio_anno)                  // Aggiorna data ultima stampa
    {            
      --d;
      reg.put("D3", d);                   // Data ultima stampa
      if (!giornale)
      {
        const int mese = d.month();
        if (reg.get_int("I4") > mese) 
          reg.put("I4", (long)mese);      // Ultimo mese di stampa liquidazione
        if (reg.get_int("I8") >= mese) 
          reg.zero("I8");                 // Mese di ultima stampa credito precedente
        azzera_lim(year, mese+1);  
      }    
    }  
    else
    {
      reg.zero("D3");
      if (!giornale)
      {
        reg.zero("I4");
        reg.zero("I8");            
        azzera_lim(year, 1);  
      }  
    }  
    
    ok = (reg.rewrite() == NOERR);
    if (!ok)
      error_box("Errore nell'aggiornamento del registro %s.\n Errore n. %d",
                (const char*)regist, mov.status());
  }
  else
    ok = error_box("Impossibile leggere il registro %s anno %s", 
                   (const char*)regist, (const char*)year);

  return ok;
}


bool TRipristina_stampa::inl_restore(const TString& lib, int year, int month)
{ 
  TLocalisamfile inl(LF_INDLIB);
  
  inl.zero();
  inl.put("ANNO", year);
  inl.put("CODLIB", lib);
  const TRectype filter(inl.curr());

  bool ok = FALSE;
  
  // Azzera il flag di stampato sugli indici con mese >= month
  for (inl.read(_isgteq); inl.good() && inl.curr() == filter; inl.next())
    if (inl.get_int("MESEREG") >= month && inl.get_bool("STAMPATO"))
    {
      inl.zero("STAMPATO");
      ok = inl.rewrite() == NOERR;
      if (!ok)
      {
        error_box("Errore di ripristino dell'indice %ld", inl.get_long("NUMREG"));
        break;
      }
    }  
  
  if (ok) // Se e' stato ripristinato almeno un indice e non ci sono stati errori
  {
    TTable lbu("%LBU");
    lbu.put("CODTAB", format("%4d%s", year, (const char*)lib));
    ok = lbu.read() == NOERR;
    if (ok)
    {
      TDate d(1, month, year); // Riporta la data di ultima stampa
      lbu.put("D0", d);
      ok = lbu.rewrite() == NOERR;
    }
    if (!ok) 
      error_box("Impossibile aggiornare il libro unico %s per l'anno %d", (const char*)lib, year);
  }
  
  return ok;  
}

bool TRipristina_stampa::create()
{
  TApplication::create();
  
  if (argc() > 2 && stricmp(argv(2), "INL") == 0)
    _op = restore_inl;
  
  dispatch_e_menu(BAR_ITEM(1));
  return TRUE;
}


bool TRipristina_stampa::menu(MENU_TAG)
{
  TMask msk(_op == restore_inl ? "cg5400b" : "cg5400a") ;
  
  while (msk.run() == K_ENTER)
  {
    long firm = 0L;
    const TString16 reg  = msk.get(F_REG);
    bool giornale = FALSE;                            
    
    if (_op == restore_reg)
    {
      firm = msk.get_long(F_FIRM);
      giornale = msk.get_int(F_TIPO) == 5;
    }
    
    const int year = msk.get_int(giornale ? F_ESER : F_YEAR);
    const int month = msk.get_int(F_MESE);
    int day = giornale ? msk.get_int(F_DAY) : 1;
    if (day < 1) day = 1; 
    
    if (prefix().exist(firm))
    {
      TString256 mess("Attenzione ripristino della stampa de");
      if (_op == restore_reg) 
        mess << (giornale ? "l giornale" : "l registro");
      else 
        mess << "gli indici del libro ";
      mess << ' ' << reg << "\ndell'" << (giornale ? "esercizio " : "anno ") << year;
      if (_op == restore_reg) mess <<  " della ditta " << firm;
      mess << " dal " << day << '-' << month << '-' << year;

      if (yesno_box(mess))
      {
        mess << "\nSi desidera veramente continuare?";
        if (yesno_box(mess))
        {                                         
          switch (_op)
          {               
          case restore_inl:
            inl_restore(reg, year, month); break;
          default:
            reg_restore(reg, year, month, day, giornale); break;
          }  
        }  
      }
    }
    else error_box("Gli archivi della ditta %ld non sono stati ancora generati", firm);
    msk.reset();
  }
  return FALSE;
}

int cg5400(int argc, char* argv[])
{
  TRipristina_stampa a ;
  a.run(argc, argv, "Ripristino stampe");
  return 0;
}
