#include <applicat.h>
#include <mask.h>
#include <progind.h>
#include <tabutil.h>
#include <urldefid.h>

#include "cg2200.h"
#include "cg2101.h"
#include "cglib.h"

#include <causali.h>
#include <mov.h>
#include <rmov.h>
#include <rmoviva.h>

TString& add_plural(TString& s, long num, const char* name)
{   
  const TFixed_string n(name);
  const char last = n[n.len()-1];                   

  if (num < 1)
  {
    s << "nessun";
    if (toupper(last) == 'A' || toupper(n[0]) == 'Z' ||
        toupper(n[0]) == 'S' && strchr("aeiouAEIOU", n[1]) == NULL) 
      s << tolower(last);
    s << ' ' << name;
  } 
  else
  {
    s << num << ' ' << name;
    if (num > 1)
      s[s.len()-1] = (last == 'a') ? 'e' : 'i';
  }
  
  return s;
}

class TProvvisori_app : public TApplication
{    
  TLocalisamfile* _sal;
  TLocalisamfile* _cau;
  TTable* _reg;
  
  TString16 _from_caus, _to_caus;
  
  TSaldo_agg _saldi;

protected:                   // TApplication
  virtual bool create();
  virtual bool destroy();
  virtual bool menu(MENU_TAG m);  
  
protected:
  static bool date_handler(TMask_field& f, KEY k);

public:
  void inizia_saldi(const TRectype& mov);
  void aggiungi_saldi(const TRectype& rmov, bool lettura);
  void aggiorna_saldi();

  bool confirm_provv(TCursor& cur, TProgind& pi);
  bool delete_provv(TCursor& cur, TProgind& pi);
  static bool filter(const TRelation* rel);

  TProvvisori_app() {};  
};

inline TProvvisori_app& app() 
{ return (TProvvisori_app&)main_app(); }


bool TProvvisori_app::create()
{
  TApplication::create();      
  
  _cau = new TLocalisamfile(LF_CAUSALI);
  _sal = new TLocalisamfile(LF_SALDI);
  _reg = new TTable("REG");

  dispatch_e_menu(BAR_ITEM(1));
  return TRUE;
}

bool TProvvisori_app::destroy()
{
  delete _reg;
  delete _sal;
  delete _cau;
  
  return TApplication::destroy();
}

void TProvvisori_app::inizia_saldi(const TRectype& r)
{
  CHECK(r.num() == LF_MOV, "Voglio un movimento");
  
  _saldi.reset();
  
  bool apertura = FALSE;
  const TString& c = r.get(MOV_CODCAUS);
  if (c.not_empty())
  {
    _cau->put(CAU_CODCAUS, c);
    if (_cau->read() == NOERR)
      apertura = _cau->get_bool(CAU_MOVAP);
  }    
  _saldi.set_movap(apertura);                      

  _saldi.set_anno_es(r.get_int(MOV_ANNOES));
  _saldi.set_num_ulmov(r.get_long(MOV_NUMREG));
  _saldi.set_data_ulmov(r.get_date(MOV_DATAREG));
}  

void TProvvisori_app::aggiungi_saldi(const TRectype& r, bool lettura)
{
  CHECK(r.num() == LF_RMOV, "Voglio la riga di un movimento");
  
  TBill conto; conto.get(r);
  TImporto importo(r.get_char(RMV_SEZIONE), r.get_real(RMV_IMPORTO));
  
  _saldi.set_movprovv(lettura);               // In lettura sono tutti provvisori
  _saldi.aggiorna(conto, importo, !lettura);  // In lettura devo sottrarre l'importo
}

void TProvvisori_app::aggiorna_saldi()
{                       
  _saldi.registra();
}

bool TProvvisori_app::confirm_provv(TCursor& cur, TProgind& pi)
{
  TLocalisamfile& mov = cur.file(LF_MOV);
  TLocalisamfile rmov(LF_RMOV);

  for (cur = 0; cur.pos() < cur.items(); ++cur)
  {                    
    const long numreg = mov.get_long(MOV_NUMREG);
    inizia_saldi(mov.curr());
    
    int err = cur.lock();
    for (int rig = 1; err == NOERR; rig++)
    {
      rmov.put(RMV_NUMREG, numreg);
      rmov.put(RMV_NUMRIG, rig);
      if (rmov.read(_isequal, _lock) != NOERR) break;
      aggiungi_saldi(rmov.curr(), TRUE);
      aggiungi_saldi(rmov.curr(), FALSE);
    }  
    
    if (err == NOERR)
    {
      mov.zero(MOV_PROVVIS);
      err = mov.rewrite();
      if (err == NOERR)
      {
        aggiorna_saldi();
        pi.addstatus(1);
      }  
    }
    
    if (err != NOERR)
      return error_box("Errore nella conferma del movimento %ld", numreg);
  }  
  
  return TRUE;
}

bool TProvvisori_app::delete_provv(TCursor& cur, TProgind& pi)
{
  TLocalisamfile& mov = cur.file(LF_MOV);
  TLocalisamfile rmov(LF_RMOV);
  TLocalisamfile rmoviva(LF_RMOVIVA); 
  
  TString256 error;

  for (cur = 0; cur.pos() < cur.items(); ++cur)
  {
    const long numreg = mov.get_long(MOV_NUMREG);
    
    mov.setkey(1);         // Isam bug on remove with key != 1
    mov.put(MOV_NUMREG, numreg);
    int err = mov.read(_isequal, _lock);
    if (err != NOERR)
      return error_box("Errore %d nel bloccare il record %ld", err, numreg);
    
    inizia_saldi(mov.curr());
    
    for (int rig = 1; err == NOERR; rig++)
    {
      rmov.put(RMV_NUMREG, numreg);
      rmov.put(RMV_NUMRIG, rig);
      if (rmov.read(_isequal, _lock) != NOERR) break;
      
      aggiungi_saldi(rmov.curr(), TRUE);
      err = rmov.remove();
      if (err != NOERR)
        error.format("riga contabile %d", rig);
    }  
    for (rig = 1; err == NOERR; rig++)
    {
      rmoviva.put(RMI_NUMREG, numreg);
      rmoviva.put(RMI_NUMRIG, rig);
      if (rmoviva.read(_isequal, _lock) != NOERR) break;
      err = rmov.remove();
      if (err != NOERR)
        error.format("riga IVA %d", rig);
    }  
    
    if (err == NOERR)
    {            
      err = mov.remove(); 
      if (err != NOERR)
        error = "testata";
    }  

    if (err == NOERR)
    {
      aggiorna_saldi();
      pi.addstatus(1);
    }  
    else
      return error_box("Errore %d nella cancellazione della %s del movimento %ld", 
                       err, (const char*)error, numreg);
  }
  
  return TRUE;
}


bool TProvvisori_app::filter(const TRelation* rel)
{          
  bool ok = FALSE;
  
  const TRectype& mov = rel->curr();
  if (mov.get_char(MOV_PROVVIS) > ' ') 
  {
    const char* caus = mov.get(MOV_CODCAUS);
    ok = app()._from_caus <= caus && app()._to_caus >= caus;
  }
  
  return ok;
} 

// Deve essere specificata almeno una data (inizio o fine)
bool TProvvisori_app::date_handler(TMask_field& f, KEY k)
{ 
  bool ok = TRUE;
  if (k == K_ENTER && f.get().empty())
  {
    const TMask& m = f.mask();
    if (m.get(F_TODATE).empty())
      ok = error_box("E' necessario specificare almeno una data");
  }
  return ok;
}

bool TProvvisori_app::menu(MENU_TAG)
{
  TMask m("cg2200a");
  m.set_handler(F_FROMDATE, date_handler);
  
  TCursor& cur = *m.efield(F_FROMDATE).browse()->cursor();
  
  KEY key;
  while ((key = m.run()) != K_QUIT) 
  {    
    TRectype from(LF_MOV);
    TRectype to(LF_MOV);
    
    const char* s = m.get(F_FROMDATE);    

    if (key == K_ENTER)
    {            
      const TDate da(s);
      const TLibro_giornale lg(da.year());
      const TDate lp(lg.last_print());
      if (da < lp)
      {         
        s = lp.string();
        const bool ok = yesno_box("Il libro giornale e stato stampato il %s:\n", 
                                  "Si desidera modificare la data iniziale?", s);
        if (!ok) continue;
      }
    }

    if (*s) from.put(MOV_DATAREG, s);
    s = m.get(F_FROMREG);
    if (*s) from.put(MOV_NUMREG, s);
    _from_caus = m.get(F_FROMCAUS);

    s = m.get(F_TODATE);    
    if (*s) to.put(MOV_DATAREG, s);
    s = m.get(F_TOREG);
    if (*s) to.put(MOV_NUMREG, s);
    _to_caus = m.get(F_TOCAUS);
    
    cur.setregion(from, to);
    cur.set_filterfunction(filter);
    
    const TRecnotype total = cur.items();
    
    TString80 action(key == K_ENTER ? "conferma" : "cancellazione");
    action << " di ";  add_plural(action, total, "movimento");

    TString256 caption("E' stata richiesta la ");
    caption << action << '.';
    
    if (total > 0)
    {
      caption << "\nSi desidera continuare?";
      if (!yesno_box(caption)) continue;
    }
    else 
    {
      warning_box(caption);
      continue;
    }  
    
    action[0] = toupper(action[0]);                         
    TProgind pi(total, action, FALSE, TRUE, 24);
    
    cur.freeze(TRUE);                    
    
    if (key == K_ENTER)
      confirm_provv(cur, pi);
    else  
      delete_provv(cur, pi);
    
    cur.freeze(FALSE);                    
    from.zero(); to.zero();
    cur.setregion(from, to);
    cur.set_filterfunction(NULL);
    m.reset();
  }
  
  return FALSE;
}


int cg2200(int argc, char** argv)
{
  TProvvisori_app a;
  a.run(argc, argv, "Gestione provvisori");
  return 0;
}
