#include <automask.h>
#include <defmask.h> 
#include <recarray.h> 
#include <relapp.h> 
#include <sheet.h> 
#include <utility.h> 

#include "ce1.h"
#include "ce1500a.h" 
#include "ce2101.h" 
#include "celib.h" 
#include "../cg/cglib01.h" 

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

#include "ammce.h"
#include "ammmv.h"
#include "cespi.h"
#include "movce.h"
#include "salce.h"


//--------------------------------------------------
// DICHIARAZIONE APPLICAZIONE
//--------------------------------------------------
class TMov_qmask;
class TMov_emask;

class TMovicespi : public TRelation_application
{         
  TRelation* _rel;
  TMov_qmask* _qmask;
  TMov_emask* _emask;
  TFilename _prima_nota;

private:
  void kill_mov(const TString& idcespite, const TString& idmov, int lfile);
  void kill_rett(const TString& id, const TString& idmov);

  void set_prompt(TMask_field& fld, bool plus);
  void show_plus_minus(TMask& m);

protected:
  virtual bool changing_mask(int mode) { return true; }
  virtual TMask* get_mask(int mode);
  virtual const char* get_next_key();
  virtual bool protected_record(TRelation &r);
  virtual bool user_create();
  virtual bool user_destroy();
  virtual void init_query_mode(TMask& m);
  virtual void init_insert_mode(TMask& m);
  virtual void init_modify_mode(TMask& m);
  virtual int write(const TMask& m);
  virtual int rewrite(const TMask& m);
  virtual bool remove();

public:  
  virtual TRelation* get_relation() const { return _rel; }

  void calc_residuo(long numreg);
  void cg_mode();
  int init_mask(TMask& m);
  
  void save_if_dirty();
};

TMovicespi& app() { return (TMovicespi&)main_app(); }


//---------------------------------------------------------
// MASCHERA DI RICERCA (ce1500a)
//---------------------------------------------------------
class TMov_qmask : public TAutomask
{      
  int _staat;

protected:
  virtual bool can_be_closed() const;
  virtual bool on_field_event(TOperable_field& o, TField_event e, long jolly);
  void on_user_search(TOperable_field& o);
  virtual void on_firm_change();
  int calcola_stato_attivita();
  int create_fields(int x, int y, short key_id);

public:
  int stato_attivita() const { return _staat; } 
  TMov_qmask();
};

void TMov_qmask::on_user_search(TOperable_field& o)
{
  TToken_string order, fields, header;
  const TFieldref* campo = o.field();
  //ordinare per campo personalizzato/idcespite/descrizione
  order.format("%d->%s", LF_CESPI, (const char*)campo->name());
  TString prompt = o.prompt();
  prompt << "@" << o.size();  //lunghezza del prompt del campo
  header.add(prompt);
  order.add(MOVCE_IDMOV); 
  header.add(TR("Movimento@11"));

  fields = order;
  fields.add(MOVCE_DESC);
  header.add(FR("Descrizione@50"));

  fields.add(MOVCE_IDCESPITE); 
  header.add(TR("Cespite@10"));

  TString desc_cesp;
  desc_cesp.format("%d->%s", LF_CESPI, "DESC");
  fields.add(desc_cesp);
  header.add(FR("Descrizione cespite@50"));

  TRelation rel(LF_MOVCE);
  rel.add(LF_CESPI, "IDCESPITE==IDCESPITE");

  TSorted_cursor cur(&rel, order);
  TCursor_sheet sht(&cur, fields, TR("Movimenti"), header, 0, 1);
  if (sht.run() == K_ENTER)
  { 
    TToken_string& row = sht.row();
    set(F_IDMOV, row.get(1), true);
    stop_run(K_AUTO_ENTER);
  }
}

int TMov_qmask::calcola_stato_attivita()
{
  const int ese = get_int(F_ESERCIZIO);
  const int gru = get_int(F_GRUPPO);
  const char* spe = get(F_SPECIE);

  TString16 str;  
  str.format("%04d%02d%-4s", ese, gru, spe);

  const TRectype& curr_ccb = cache().get("CCB", str);
  if (curr_ccb.get_bool("B1")) // Bollato stampato
    _staat = 3;
  else
  {  
    TEsercizi_contabili esc;
    str.format("%04d%02d%-4s", esc.pred(ese), gru, spe);
    const TRectype& prev_ccb = cache().get("CCB", str);
    _staat = prev_ccb.get_bool("B1") ? 2 : 1;
  }
  
  ditta_cespiti().set_attivita(ese, gru, spe);
  return _staat;
}

bool TMov_qmask::on_field_event(TOperable_field& o, TField_event e, long jolly)
{
  switch (o.dlg())
  {
  case F_ESERCIZIO:
    if ((e == fe_close || e == fe_modify) && efield(F_INIZIO_ES).empty())
      return error_box("Inserire un esercizio valido");
  case F_GRUPPO:
  case F_SPECIE:
    calcola_stato_attivita();
    enable(DLG_NEWREC, _staat != 3);
    break;
  case F_IMPIANTO:
  case F_IDCESPITE: 
    if (e == fe_modify)
    {
      const TString& ces = get(F_IDCESPITE);
      const TString& imp = get(F_IMPIANTO);
      const bool cesok = !real::is_null(ces);
      const bool impok = !real::is_null(imp);
      TString80 filter;
      if (cesok)
        filter << MOVCE_IDCESPITE << "==\"" << ces << '"';
      if (impok)
      {
        if (cesok)
          filter << ")&&(";
        filter << LF_CESPI << "->" << CESPI_CODIMP << "==\"" << imp << '"';
        if (cesok)
        {
          filter << ')';
          filter.insert("(", 0);
        }
      }
      TEdit_field& m = efield(F_IDMOV);
      m.browse()->set_filter(filter);
    }
    break;
  case F_NUMREG: 
    if (e == fe_modify)
    {      
      TEdit_field& m = efield(F_IDMOV);
      if (!m.empty())
        m.on_key(K_TAB);
    }
    break;
  case F_CGROWS:
    if (e == se_query_add || e == se_query_del)
      return false;
    break;
  case DLG_QUIT:
    if (e == fe_button)
    {
      TMask_field& f = efield(F_TOTRES);
      if (f.shown() && !f.empty())
        return yesno_box(TR("Attenzione: la registrazione contabile non � ancora del tutto evasa:\n"
                         "Si desidera uscire ugualmente?"));
    }
    break;
  case F_USER:
  case F_USER+1:
  case F_USER+2:
  case F_USER+3:
  case F_USER+4:
  case F_USER+5:
    if (e == fe_button)
      on_user_search(o);
    break;
  default: 
    break;
  }
  return true;
}

bool TMov_qmask::can_be_closed() const
{
  TButton_field& f = (TButton_field&)field(DLG_QUIT);
  return ((TMov_qmask*)this)->on_field_event(f, fe_button, 0);
}

void TMov_qmask::on_firm_change()
{
  TAutomask::on_firm_change();
  ditta_cespiti().init_mask(*this);
}

TMov_qmask::TMov_qmask() : TAutomask("ce1500a") 
{
  first_focus(F_IDMOV);
  create_fields(1, 9, F_USER);
}

//metodo per la generazione dei campi di ricerca personalizzati
int TMov_qmask::create_fields(int x, int y, short key_id)
{
  TRectype rec_cespi(LF_CESPI);
  //cicla sui campi user (se ci sono) nel ditta.ini facendoli apparire sulla pagine Personalizz.
  TConfig config (CONFIG_DITTA, "ce");

  int i = 0;
  //valuta il valore massimo in orizzontale per il prompt, in modo da allineare i campi in verticale
  int maxprompt = 0;
  for (i = 0; config.exist("USER", i); i++)
  {
    TToken_string riga = config.get("USER", NULL, i);
    const TString80 prompt = riga.get(1);
    const int len = prompt.len();
    if (len > maxprompt)
      maxprompt = len;
  }
  maxprompt++;

  for (i = 0; config.exist("USER", i); i++)
  {
    TToken_string riga = config.get("USER", NULL, i);

    //siamo in pagina di ricerca! se il campo non e' di ricerca lo salta!
    const bool search = riga.get_char(3) == 'X';
    if (!search)
      continue;
    //tutto il resto lo fa comunque,settandondolo nella page corretta
    const short kid = key_id+i; //numero del campo come definito nel .h della maschera
    const TString16 name = riga.get(0); //nome campo
    TString80 prompt = riga.get(1); prompt.left_just(maxprompt);  //prompt sulla maschera    
    const TString80 picture = riga.get(2);  //picture del campo(se c'e')
    //se la lunghezza del campo non e' definita nella picture la prende dal tracciato
    const int len = picture.blank() ? rec_cespi.length(name) : picture.len();

    //chiede al record di che tipo e' il campo chiamato name...
    const TFieldtypes tipo_campo = rec_cespi.type(name);
    //..quanto e' lungo...(maniaca!)
    const int length_campo = rec_cespi.length(name);
    //...e quanti decimali ha nel caso sia un real
    int ndec_campo = 0;
    if (tipo_campo == _realfld)
      ndec_campo = rec_cespi.ndec(name);

    //crea finalmente i nuovi campi sulla pagina 1 (Personalizzazioni)
    switch(tipo_campo)
    {
    case _wordfld:      add_number (kid, 0, prompt, x, i+y, len, search ? "BU" : "U");  break;

    case _intfld :
    case _longfld:      add_number (kid, 0, prompt, x, i+y, len, search ? "B" : "");  break;

    case _intzerofld:
    case _longzerofld:  add_number (kid, 0, prompt, x, i+y, len, search ? "BZ" : "Z");  break;

    case _realfld:      add_number (kid, 0, prompt, x, i+y, len, "", ndec_campo);  break;

    case _datefld:      add_date (kid, 0, prompt, x, i+y, search ? "B" : "");  break;

    case _memofld:      add_zoom(kid, 0, prompt, x, i+y, 50);  break;
      
    default      :      add_string (kid, 0, prompt, x, i+y, len, search ? "BU" : "");  break;
    }

    //deve poter salvare il contenuto nei campi!! (e' il FIELD delle maschere)
    TEdit_field& efld = efield(kid);
    efld.set_field(name); //la set_field lo fa
  }
  //deve accendere gli handler di questa maschera perche' funzionino
  set_handlers();
  
  return i;
}


//---------------------------------------------------------
// MASCHERA DI MODIFICA (ce1500b)
//---------------------------------------------------------
class TMov_emask : public TAutomask
{      
  int _staat;
  TCespite _cespite;
  TString _s5;

private:
  void set_inputability(short id, char flag);
  void set_inputability(const short* id, char flag);
  bool test_inputability(const short* id, char flag);
  void set_fondi_inputability();

  real calc_riv(const TRectype& salpro, int tipo) const;
  bool calc_amm(int tipo);
  void calc_plus_minus(int tipo, real& plus, real& minus) const;
  
  bool cespite_nuovo() const;
  TCurrency sum_fields(const short* f) const;
  void super_polish();

protected:
  virtual bool on_field_event(TOperable_field& o, TField_event e, long jolly);
  virtual bool on_key(KEY k);

public:
  void set_stato_attivita(int s) { _staat = s; }
  void recalc_amm();

  TMov_emask();
};

// Determina se siamo in presenza di un cespite nuovo
bool TMov_emask::cespite_nuovo() const
{
  const TDate dtacq = _cespite.get_date(CESPI_DTCOMP);
  const TDate dtmov(get(F_DTMOV));
  return !dtacq.ok() || dtacq >= dtmov;
}

TCurrency TMov_emask::sum_fields(const short* f) const
{
  TCurrency sum, val;
  for (int i = 0; f[i] != 0; i++)
  {
    get_currency(abs(f[i]), val);
    if (f[i] > 0)
      sum += val;
    else
      sum -= val;  
  }
  return sum;
}

// Toglie tutti i dirty
void TMov_emask::super_polish()
{
  for (int f = fields()-1; f >= 0; f--)
  {
    TMask_field& c = fld(f);
    c.set_dirty(false);
  }
}

void TMov_emask::set_inputability(short id, char flag)
{
  TMask_field& f = field(id);
  if  (flag=='S' || flag=='O')
    f.enable();
  else
  {
    f.reset();
    f.disable();  
  }
  f.check_type(flag=='O' ? CHECK_REQUIRED : CHECK_NORMAL);
}

void TMov_emask::set_inputability(const short* id, char flag)
{
  if (flag == 'O') 
    flag = 'S';
  for (; *id > 0; id++)
    set_inputability(*id, flag);
}

bool TMov_emask::test_inputability(const short* id, char flag)
{
  bool ok = flag != 'O';
  if (!ok)
  {
    for (int i = 0; id[i] > 0; i++)
    {
      if (!field(id[i]).empty())  
      {
        ok = true;
        break;
      }
    }
  }
  if (!ok)
    error_box(TR("E' necessario specificare almeno uno dei valori previsti dal tipo movimento"));
  return ok;
}

HIDDEN const short doc_ids[] = { F_TPDOC, F_NDOC, F_DTDOC, 0 };
HIDDEN const short riv_ids[] = { F_RIV75, F_RIV83, F_RIV90, F_RIV91, 0 };
HIDDEN const short fis_ids[] = { F_NORMALE, F_ACCELERATO, F_ANTICIPATO, 0 };
HIDDEN const short civ_ids[] = { F_NORMALE2, F_ACCELERATO2, F_ANTICIPATO2, 0 };
HIDDEN const short prv_ids[] = { F_FPRIVATO, F_QPERSEPRIV, 0 };

bool TMov_emask::on_field_event(TOperable_field& o, TField_event e, long jolly)
{
  switch (o.dlg())
  {
  case F_CODMOV:
    if ((e == fe_modify || e == fe_init))
    {
      const TRectype& tpmov = cache().get("%TMC", o.get());
      const bool insert_rett = tpmov.get_int("I0") == 2 && insert_mode();
      enable(F_IDRET, insert_rett);
      if (insert_rett)
      {
        TString filter; // Filtro su movimenti rettificabili
        filter << '(' << MOVCE_CODMOV << "=\"" << tpmov.get("S8") << "\")";
        filter << "&&(ANSI(" << MOVCE_DTMOV << ")>=" << get_date(F_INIZIO_ES).string(ANSI) << ')';
        filter << "&&(ANSI(" << MOVCE_DTMOV << ")<=" << get_date(F_FINE_ES).string(ANSI) << ')';
        efield(F_IDRET).browse()->set_filter(filter);
      }
      _s5 = tpmov.get("S5");
      if (_s5.empty())
        _s5.spaces(16);
      set_inputability(doc_ids,    _s5[0]);
      set_inputability(F_IMPVEN,   _s5[2]);
      set_inputability(F_ELEMENTI, _s5[3]);
      set_inputability(F_COSTO,    _s5[4]);
      set_inputability(F_VNONAMM,  _s5[5]);
      set_inputability(F_PLUSREIN, _s5[6]);
      set_inputability(riv_ids,    _s5[7]);
      set_inputability(F_RIVGF,    _s5[8]);
      set_inputability(F_RIVGC,    _s5[9]);
      // Determina segni possibili
      switch (tpmov.get_char("S7"))
      {        
      case '+': disable(F_SEGNO); set(F_SEGNO, "+"); break;
      case '-': disable(F_SEGNO); set(F_SEGNO, "-"); break;
      default : enable(F_SEGNO); break;
      }
    }
    break;
  case F_IDRET:
    if (e == fe_modify)
    {
      TString16 ces;
      if (!o.empty())
      {
        TEdit_field& e = (TEdit_field&)o;
        ces = e.browse()->cursor()->curr().get(MOVCE_IDCESPITE);
        TEdit_field& c = efield(F_IDCESPITE);
        c.set(ces); 
        c.check();
      }
      enable(F_IDCESPITE, ces.empty()); // Dis/abilita codice cespite
      enable(F_DESC_CES, ces.empty());  // Dis/abilita descrizione cespite
    }
    break;
  case F_IDCESPITE:

    if (e == fe_init || e == fe_modify || e == fe_close)
    {
      const TString& id = o.get();
      if (_cespite.get(CESPI_IDCESPITE) != id)
        _cespite.read(id);                    
      enable_page(3, id.not_empty()); 
      if (id.not_empty())
      {
        const TRectype& cat = _cespite.categoria();
        set(F_CATEGORIA, _cespite.get(CESPI_CODCAT), true);
        set(F_DESC_CAT, cat.get("S0"), true);
        if (o.enabled())
        {
          const TRectype& tpmov = cache().get("%TMC", get(F_CODMOV));

          switch (_cespite.tipo())
          {
          case tc_immateriale:
            if (!tpmov.get_bool("B1")) 
              return error_box(TR("Movimento non applicabile a beni immateriali"));
            break;
          case tc_pluriennale:
            if (!tpmov.get_bool("B2")) 
              return error_box(TR("Movimento non applicabile a costi pluriennali"));
            break;
          default:
            if (!tpmov.get_bool("B0")) 
              return error_box(TR("Movimento non applicabile a beni materiali"));
            break;    
          }
        }
      }
      else
      {
        reset(F_CATEGORIA);
        reset(F_DESC_CAT);
      }
      const TDate dtcomp = _cespite.get_date(CESPI_DTCOMP);
      if (dtcomp.ok())
      {
        const TDate dtmov(get(F_DTMOV));
        if (dtcomp > dtmov)
          return error_box(TR("La data di acquisizione del cespite non pu� precedere quella del movimento"));
      }
    }
    break;  
  case F_DTMOV:
    if (e == fe_init || e == fe_modify || e == fe_close)
    {
      TEdit_field& dd = efield(F_DTDOC);
      if (dd.required() && dd.empty())
        dd.set(o.get());

// Inserimento nuovo cespite da gestione movimenti cespiti
// Se il campo cespite e' vuoto e non sono in uscita di programma (sto quindi inserendo un
// cespite nuovo..) prendo la data movimento dal file di configurazione ditta..
      if (field(F_IDCESPITE).empty() && e != fe_close)
      {
        TConfig ini(CONFIG_DITTA,"ce");
        ini.set("DataMovimento", o.get());
      }
// ..se invece sono in uscita di programma (avro' gia' inserito il cespite) la data movimento
// sul file di configurazione deve essere svuotata 
      if (e == fe_close)
      {
        TConfig ini(CONFIG_DITTA,"ce");
        ini.set("DataMovimento", "");
      }
        
      const TDate dtret(get(F_DTMOV_RET));
      if (dtret.ok())
      {
        const TDate dtmov(o.get());
        if (dtmov < dtret)
          return error_box(TR("La data del movimento di rettifica deve seguire la data del movimento rettificato"));
      }
    }
    break;  
  case F_TPDOC:
    if (e == fe_button)
    {
      TArray_sheet sci(3, 3, -3, -3, o.prompt(), HR("Tipo|Descrizione@20"));
      sci.add(TR("FA|Fattura d'acquisto"));
      sci.add(TR("FV|Fattura di vendita"));
      sci.add(TR("NC|Nota di credito"));
      sci.add(TR("ND|Nota di debito"));
      if (sci.run() == K_ENTER)
      {       
        TToken_string& str = sci.row(-1);
        o.set(str.get(0));
      }
    }
    if (e == fe_close)
      return test_inputability(doc_ids, _s5[0]);
    break;
  case F_RIV75:
    if (e == fe_close)
      return test_inputability(riv_ids, _s5[7]);
    break;
  case F_ELEMENTI:
    if (e == fe_modify || e == fe_close)
    {
      if (o.empty() && cespite_nuovo())
        return error_box(TR("� necessario inserire il numero di elementi del nuovo cespite"));
    }
    break;
  case F_VNONAMM:
    if (e == fe_modify || e == fe_close)
    { 
      TCurrency csto, vnon;
      get_currency(F_COSTO, csto);
      get_currency(F_VNONAMM, vnon);
      if (vnon > csto)
        return error_box(FR("Il valore non ammortizzabile non puo' superare %s"), csto.string(true));
    }
    break;
  case F_PLUSREIN:
    if (e == fe_modify || e == fe_close)
    { 
      TCurrency csto, vnon, plus;
      get_currency(F_COSTO, csto);
      get_currency(F_VNONAMM, vnon);
      get_currency(F_PLUSREIN, plus);
      const TCurrency val = csto-vnon;
      if (plus > val)
        return error_box(FR("La plusvalenza reinvestita non puo' superare %s"), val.string(true));
    }
    break;
  case F_NORMALE:
    if (e == fe_close)
    {        
      if (!test_inputability(fis_ids, _s5[10]))
        return false;
      const short fv[] = { F_COSTO, -F_VNONAMM, F_RIV75, F_RIV83, F_RIV90, F_RIV91, F_RIVGF, 0 };
      const short fa[] = { F_NORMALE, F_ACCELERATO, F_ANTICIPATO, F_QPERSE, F_FPRIVATO, F_QPERSEPRIV, 0 };
      const TCurrency val_amm = sum_fields(fv);
      const TCurrency fon_amm = sum_fields(fa);
      if (fon_amm > val_amm)
      {
        TString msg;
        msg << TR("Il fondo ammortamento fiscale (") << fon_amm.string(true) << ')';
        msg << TR("non puo' superare il valore da ammortizzare (") << val_amm.string(true) << ')';
        return error_box(msg);
      }
    }
    break;
  case F_NORMALE2:
    if (e == fe_close)
    { 
      if (!test_inputability(civ_ids, _s5[11]))
        return false;
      const short fv[] = { F_COSTO, F_RIV75, F_RIV83, F_RIV90, F_RIV91, F_RIVGC, 0 };
      const short fa[] = { F_NORMALE2, F_ACCELERATO2, F_ANTICIPATO2, 0 };
      const TCurrency val_amm = sum_fields(fv);
      const TCurrency fon_amm = sum_fields(fa);
      if (fon_amm > val_amm)
      {
        TString msg;
        msg << TR("Il fondo ammortamento civilistico (") << fon_amm.string(true) << ')';
        msg << TR("non puo' superare il valore da ammortizzare (") << val_amm.string(true) << ')';
        return error_box(msg);
      }
    }
    break;
  case S_TIPO:
    if (e == fe_init || e == fe_modify)
      calc_amm(atoi(o.get()));
    break;
  default:
    break;  
  }
  return true;
}

real TMov_emask::calc_riv(const TRectype& salpro, int tipo) const
{
  const char* riv_ids[] = { SALCE_RIV75, SALCE_RIV83, SALCE_RIV90, SALCE_RIV91, NULL };
  real riv;
  for (int i = 0; riv_ids[i]; i++)
    riv += salpro.get_real(riv_ids[i]);
  if (tipo == 1)
    riv += salpro.get_real(SALCE_RIVGF);
  if (tipo == 2)
    riv += salpro.get_real(SALCE_RIVGC);
  return riv;  
}

void TMov_emask::calc_plus_minus(int tipo, real& plus, real& minus) const
{
  TDitta_cespiti& cce = ditta_cespiti();
  TDate inies, fines;
  cce.esercizio_corrente(inies, fines);
  const TDate dtlim(get(F_DTMOV));
    
  TRelation rel(LF_MOVCE);
  TRectype& movsem = rel.curr();
  const TString& idcespite = get(F_IDCESPITE);
  movsem.put(MOVCE_IDCESPITE, idcespite);
  TString filtro;
  filtro << "(ANSI(DTMOV)>=\"" << inies.string(ANSI) << "\")";
  filtro << "&&(ANSI(DTMOV)<=\"" << dtlim.string(ANSI) << "\")";
  TCursor cur(&rel, filtro, 2, &movsem, &movsem);
  const long items = cur.items();
  plus = minus = ZERO;
  if (items > 0)  // Continua solo se esistono movimenti
  {
    cur.freeze();
    
    // Astutamente aggiungo solo ora il file collegato
    TString expr; expr << "IDCESPITE==IDCESPITE|IDMOV==IDMOV|TPAMM==\"" << tipo << '"';
    rel.add(LF_AMMMV, expr);
    const TRectype& ammmv = rel.curr(LF_AMMMV);
    
    for (cur = 0L; cur.pos() < items; ++cur)
    {
      plus += ammmv.get_real(AMMMV_PLUS);
      minus += ammmv.get_real(AMMMV_MINUS);
    }
  }
} 

bool TMov_emask::calc_amm(int tipo)
{   
  const TString& idcespite = _cespite.get(CESPI_IDCESPITE);
  if (idcespite.empty())
    return false;

  const TDate dtlim(get(F_DTMOV));
  _cespite.calc_amm(tipo, dtlim);
  set(S_DATAMOV, dtlim);                 
  
  const TRectype& s = _cespite.sal_pro();
  set(S_ELEMENTI, s.get(SALCE_NUMELE));  
  set(S_COSTO, s.get_real(SALCE_CSTO));
  set(S_VNONAMM, s.get(SALCE_VNONAMM));
  set(S_PLUSREIN, s.get(SALCE_PLUSREIN));
  set(S_TOTRIV, calc_riv(s, tipo));
  real tot_val;
  tot_val += get_real(S_COSTO); tot_val -= get_real(S_VNONAMM);
  tot_val -= get_real(S_PLUSREIN); tot_val += get_real(S_TOTRIV); 
  set(S_TOTVAL, tot_val);
  
  const TRectype& a = _cespite.amm_pro();
  set(S_NORMALE,    a.get(AMMCE_QNORP));
  set(S_ACCELERATO, a.get(AMMCE_QACCP));
  set(S_ANTICIPATO, a.get(AMMCE_QANTP));
  set(S_QPERSE,     a.get(AMMCE_QPERSEP));
  set(S_FPRIVATO,   a.get(AMMCE_FPRIVATOP));
  set(S_QPERSEP,    a.get(AMMCE_QPPRIVATEP));
  real tot_fon;
  tot_fon += get_real(S_NORMALE); tot_fon += get_real(S_ACCELERATO);
  tot_fon += get_real(S_ANTICIPATO); tot_fon += get_real(S_QPERSE);
  tot_fon += get_real(S_FPRIVATO); tot_fon += get_real(S_QPERSEP);
  set(S_TOTFON, tot_fon);
  
  const real tot_res = tot_val-tot_fon;
  set(S_RESIDUO, tot_res);
  
  real plus, minus;
  calc_plus_minus(tipo, plus, minus);
  set(S_PLUS, plus);
  set(S_MINUS, minus);
  
  return true;
}

void TMov_emask::set_fondi_inputability()
{          
  set_inputability(fis_ids,  _s5[10]);
  set_inputability(civ_ids,  _s5[11]);
  set_inputability(F_QPERSE, _s5[12]);

  bool should_be_on = _s5[10] == 'S' || _s5[10] == 'O';     // Test preliminare basato su %TMC
  if (should_be_on)
    should_be_on = _cespite.get_int(CESPI_USOPROM) > 1;     // Test aggiuntivo su uso promiscuo
  set_inputability(prv_ids, should_be_on ? _s5[10] : ' ');  // Dis/abilita fondo privato e quote perse private
}

void TMov_emask::recalc_amm()
{
  const TDate dtlim(get(F_FINE_ES));
  for (int tipo = 1; tipo <= 3; tipo++)
    _cespite.calc_amm(tipo, dtlim);
}

bool TMov_emask::on_key(KEY k)
{ 
  // Try to predict next page!
  const int old_page = curr_page()+1;
  int new_page = old_page;
  switch (k)
  {
  case K_CTRL+K_F1: new_page = 1; break;
  case K_CTRL+K_F2: new_page = 2; break;
  case K_CTRL+K_F3: new_page = 3; break;
  case K_CTRL+K_F4: new_page = 4; break;
  case K_PREV     : new_page--; break;
  case K_NEXT     : new_page++; break;
  default: break;
  }
  
  // If page will change ...
  if (old_page != new_page) 
  {
    switch(new_page)
    {
    case 3: 
      set_fondi_inputability();
      break;
    case 4:
      if (dirty() < S_TIPO)
      {
        app().save_if_dirty();
        super_polish();
      }
      set(S_TIPO, 1, true); 
      break;
    default: break;
    }
    if (old_page == 4)
    {
      recalc_amm();
      TRelation& rel = *app().get_relation();
      if (rel.read() == NOERR)
      {
        autoload(rel);
        super_polish();
      }
    }
  }
  return TAutomask::on_key(k);
}

TMov_emask::TMov_emask() : TAutomask("ce1500b") 
{
}

///////////////////////////////////////////////////////////
// Applicazione principale
///////////////////////////////////////////////////////////

TMask* TMovicespi::get_mask(int mode)
{
  return mode == MODE_QUERY ? (TMask*)_qmask : (TMask*)_emask;
}

bool TMovicespi::user_create()
{                   
  open_files(LF_TAB, LF_TABCOM, LF_PCON, LF_CLIFO, 0);
  open_files(LF_CESPI, LF_AMMCE, LF_SALCE, LF_MOVCE, LF_MOVAM, LF_AMMMV, 0);

  _rel = new TRelation(LF_MOVCE);
  _rel->add(LF_MOVAM, "IDCESPITE==IDCESPITE|IDMOV==IDMOV|TPAMM==1");
  _rel->add(LF_MOVAM, "IDCESPITE==IDCESPITE|IDMOV==IDMOV|TPAMM==2", 1, 0, 2);
  _rel->add(LF_MOVAM, "IDCESPITE==IDCESPITE|IDMOV==IDMOV|TPAMM==3", 1, 0, 3);
  _rel->write_enable(); 
  
  _qmask = new TMov_qmask;
  _emask = new TMov_emask;
  
  // Collegamento da prima nota
  if (argc() > 2 && strncmp(argv(2), "/c", 2) == 0)
  {
    _prima_nota = argv(2); 
    _prima_nota.ltrim(2);
  }
  else
  {
    _prima_nota.cut(0);
  }
  
  return true;
}

bool TMovicespi::user_destroy()
{
  delete _emask;
  delete _qmask;
  delete _rel;
  return true;
}

const char* TMovicespi::get_next_key()
{
  real num = 1;
  TLocalisamfile cespi(LF_MOVCE);
  if (cespi.last() == NOERR)
    num += cespi.get_real(MOVCE_IDMOV);
  return format("%d|%s", F_IDMOV, num.string());
}

bool TMovicespi::protected_record(TRelation &r)
{
  bool stampato = r.curr().get_bool(MOVCE_STAMPATO);
  return stampato;
}

void TMovicespi::calc_residuo(long numreg)
{                                        
  TSheet_field& s = _qmask->sfield(F_CGROWS);

  // Mette tutti i residui uguali importi
  real tot_imp = ZERO;
  for (int r = s.items()-1; r >= 0; r--)
  {
    TToken_string& row = s.row(r);
    real imp = row.get(0);
    row.add(imp.string(), 1);
    tot_imp += imp;
  }
  real tot_res = tot_imp;

  TRelation rel(LF_MOVCE);
  TString filter; filter << MOV_NUMREG << "==" << numreg;
  TCursor cur(&rel, filter, 3);
  const long items = cur.items();
  if (items > 0)
  {
    cur.freeze();
    TRectype& movce = cur.curr();
    
    TAssoc_array ignore;
    // Prima cerca movimenti con importo identico ...
    for (cur = 0L; cur.pos() < items; ++cur)
    {            
  //    const char segno = movce.get_char(MOVCE_SEGNO);
      real imp = movce.get_real(MOVCE_IMPVEN);
      if (imp.is_zero())
        imp = movce.get_real(MOVCE_CSTO);
      if (imp > ZERO)   // Cerca una riga con lo stesso importo
      {
        for (int r = 0; r < s.items(); r++)
        {
          TToken_string& row = s.row(r);
          const real res = row.get(1);
          if (imp == res)
          {
            imp = ZERO;
            row.add(" ", 1);
            tot_res -= res;
            ignore.add(movce.get(MOVCE_IDMOV));
            break;
          }
        }
      }
    }
    // ... poi scala dalle varie righe l'importo fino ad esaurimento
    for (cur = 0L; cur.pos() < items && tot_res > ZERO; ++cur)
    {            
      if (ignore.is_key(movce.get(MOVCE_IDMOV)))
        continue;
//      const char segno = movce.get_char(MOVCE_SEGNO);
      real imp = movce.get_real(MOVCE_IMPVEN);
      if (imp.is_zero())
        imp = movce.get_real(MOVCE_CSTO);
      for (int r = 0; r < s.items() && imp > ZERO; r++)
      {
        TToken_string& row = s.row(r);
        real res = row.get(1);
        if (res > ZERO)
        {
          const real quota = (res > imp) ? imp : res;
          res -= quota;
          imp -= quota;
          row.add(res.string(), 1);
          tot_res -= quota;
        }
      }
    }
  }
  // s.force_update();
  _qmask->set(F_TOTIMP, tot_imp);
  _qmask->set(F_TOTRES, tot_res);
}

void TMovicespi::set_prompt(TMask_field& fld, bool plus)
{
  TString str = fld.prompt();
  if (plus)
  {
    if (str[0] != 'P')
    {
      str.ltrim(3);
      str.insert("Pl");
    }
  }
  else
  {
    if (str[0] != 'M')
    {
      str.ltrim(2);
      str.insert("Min");
    }
  }
  fld.set_prompt(str);
}        

void TMovicespi::show_plus_minus(TMask& m)
{   
  TToken_string key;          
  short id_txt = F_PLUSMIN_FIS_TXT;
  short id_fld = F_PLUSMIN_FIS;
  for (int tipo = 1; tipo <= 3; tipo++, id_txt+= 2, id_fld+=2)
  {
    key = m.get(F_IDCESPITE);
    key.add(m.get(F_IDMOV));
    key.add(tipo);
    const TRectype& ammmv = cache().get(LF_AMMMV, key);
    const real plus = ammmv.get_real(AMMMV_PLUS);
    const real minus = ammmv.get_real(AMMMV_MINUS);
    const bool on = !plus.is_zero() || !minus.is_zero();
    if (on)
    {
      if (!plus.is_zero())
      {
        set_prompt(m.field(id_txt), true);
        m.set(id_fld, plus);
      }
      else
      {
        set_prompt(m.field(id_txt), false);
        m.set(id_fld, minus);
      }
    }
    m.show(id_txt, on);
    m.show(id_fld, on);
  }
}

void TMovicespi::cg_mode()
{
  TConfig ini(_prima_nota, "Transaction");
  const char action = ini.get("Action")[0];
  
  TString8 para;
  para.format("%d", LF_MOV);
  ini.set_paragraph(para); 
  const long numreg = ini.get_long(MOV_NUMREG);
  _qmask->set(F_NUMREG, numreg);
  
  // Al primo inserimento devo impostare anche l'anno
  _qmask->set(F_ESERCIZIO, ini.get(MOV_ANNOES), true);

  if (action != 'I' && action != 'M')
  {       
    _qmask->hide(-8);
    return;
  }
  else
    _qmask->show(-8);

  TSheet_field& s = _qmask->sfield(F_CGROWS);
  for (int i = 1; ; i++)
  {
    para.format("%d,%d", LF_RMOV, i);
    if (ini.set_paragraph(para))
    {          
      const int gruppo = ini.get_int(RMV_GRUPPO);
      const int conto = ini.get_int(RMV_CONTO);
      const long sottoconto = ini.get_long(RMV_SOTTOCONTO);
      const TBill zio(gruppo, conto, sottoconto);
      const int tipo = zio.tipo_cr();
      if (tipo == 2 || tipo == 3 || tipo == 4 || tipo == 8)
      {
        TToken_string& row = s.row(-1);
        row = ini.get(RMV_IMPORTO);
        row.add(""); 
        row.add(zio.string(0x2));
      }
    }
    else
      break;
  }
  calc_residuo(numreg);
}

void TMovicespi::init_query_mode(TMask& m)
{
  TDitta_cespiti& dc = ditta_cespiti();
  dc.init_mask(m);
  
  // Collegamento da prima nota
  if (_prima_nota.not_empty())
    cg_mode();
  else
    m.hide(-8);  // Nascondi campi collegamento prima nota
}

int TMovicespi::init_mask(TMask& m)
{
  TDitta_cespiti& dc = ditta_cespiti();
  dc.init_mask(m);
  
  const int staat = _qmask->stato_attivita();
  _emask->set_stato_attivita(staat);
  m.enable(DLG_NEWREC, staat != 3);
  
  return staat;
}

void TMovicespi::init_insert_mode(TMask& m)
{ 
  const int staat = init_mask(m);
  m.enable(F_IDCESPITE);
  m.enable(F_DESC_CES);
  m.enable(DLG_SAVEREC, staat != 3);
  
  if (_prima_nota.not_empty())
  {
    m.disable(DLG_QUIT); // Altrimenti non riesco a controllare il residuo
  
    TString16 para;
    para.format("%d", LF_MOV);
    TConfig ini(_prima_nota, para);  
    
    const TRectype& cau = cache().get(LF_CAUSALI, ini.get(MOV_CODCAUS));
    m.set(F_CODMOV, cau.get(CAU_COLLCESP), true);
    if (m.field(F_TPDOC).active())
      m.set(F_TPDOC, ini.get(MOV_TIPODOC), true);
    if (m.field(F_NDOC).active())
      m.set(F_NDOC, ini.get(MOV_NUMDOC), true);
    if (m.field(F_DTDOC).active())
      m.set(F_DTDOC, ini.get(MOV_DATADOC), true);

    TString desc = ini.get(MOV_DESCR);
    desc.strip("\"");
    if (desc.empty())
      desc = cau.get(CAU_DESCR);
    m.set(F_DESC_MOV, desc, true);

    TString16 codreg = ini.get(MOV_REG);
    if (codreg.not_empty())
    {
      codreg.insert(format("%04d", ini.get_int(MOV_ANNOIVA)));
      const int iva = atoi(cache().get("REG", codreg, "I0"));
      TSheet_field& s = _qmask->sfield(F_CGROWS);
      for (int r = 0; r < s.items(); r++)
      {
        const real res = s.row(r).get(1);
        if (res > ZERO)
        {
          m.set(iva==1 ? F_IMPVEN : F_COSTO, res);
          break;
        }
      }
    }
  }
  show_plus_minus(m);
}

void TMovicespi::init_modify_mode(TMask& m)
{
  const int staat = init_mask(m);
  TEsercizi_contabili esc;
  
  m.disable(F_IDCESPITE);
  m.disable(F_DESC_CES);
  
  const TRectype& rec = get_relation()->curr();
  const TDate dtmov = rec.get_date(MOVCE_DTMOV);
  bool bollato = rec.get_bool(MOVCE_STAMPATO);
  if (!bollato) // Se proprio non ci fidiamo del flag
  {
    const TEsercizio& e = esc[m.get_int(F_ESERCIZIO)];
    const TDate dtini = e.inizio();
    const TDate dtfin = e.fine();
    bollato = (staat == 3 && dtmov <= dtfin) || (staat == 2 && dtmov < dtini);
  }  
  if (bollato)
  {
    m.disable(DLG_SAVEREC);
    m.disable(DLG_DELREC);
    TString msg = TR("Movimento gi� stampato sul Bollato dell'esercizio ");
    msg << esc.date2esc(dtmov);
    xvtil_statbar_set(msg);
  }
  if (_prima_nota.not_empty())
    m.disable(DLG_QUIT); // Altrimenti non riesco a controllare il residuo
  
  show_plus_minus(m);
}

void TMovicespi::kill_mov(const TString& idcespite, const TString& idmov, int lfile)
{
  CHECKD(lfile == LF_MOVAM || lfile == LF_AMMMV, "Invalid file ", lfile);
  TRelation rel(lfile);
  TRectype filter(rel.curr()); 
  filter.zero();
  filter.put("IDCESPITE", idcespite);
  filter.put("IDMOV", idmov);
  TCursor cur(&rel, "", 1, &filter, &filter);
  const long items = cur.items();
  if (items > 0)
  {
    cur.freeze();
    for (cur = 0L; cur.pos() < items; ++cur)
    { 
      int err = rel.remove();
      if (err != NOERR)
      {
        const int tpamm = rel.curr().get_int("TPAMM");
        error_box(FR("Errore %d nella cancellazione dell'ammortamento (%d) del movimento %s"), err, tpamm, (const char*)idmov);
      }
    }
  }
}

void TMovicespi::kill_rett(const TString& id, const TString& idmov)
{
  TRelation rel(LF_MOVCE);
  
  // Il cursore delle rettifiche usa la chiave 2: IDCESPITE+IDMOV
  // Per cui parto a selezionare le possibili rettifiche dal movimento da rettificare ...
  TRectype fromret(LF_MOVCE);
  fromret.put(MOVCE_IDCESPITE, id);
  fromret.put(MOVCE_IDMOV, idmov); 
  
  // ... in poi
  TRectype toret(LF_MOVCE);
  toret.put(MOVCE_IDCESPITE, id);
  
  TString filtro; 
  filtro << MOVCE_IDRET << "==" << idmov;   // Confronto numerico!
  TCursor movcur(&rel, filtro, 2, &fromret, &toret);
  const long items = movcur.items();
  if (items > 0)  // Se ci sono rettifiche
  {
    const TRectype& curr = movcur.curr();
    movcur.freeze();
    for (movcur = 0L; movcur.pos() < items; ++movcur)
    {
      const TString16 idmov = curr.get(MOVCE_IDMOV);
      if (rel.remove() == NOERR)
      {
        kill_mov(id, idmov, LF_MOVAM);
        kill_mov(id, idmov, LF_AMMMV);
      }
    }
  }
}

int TMovicespi::write(const TMask& m)
{
  int err = TRelation_application::write(m);
  if (err == NOERR)
    _emask->recalc_amm();
  return err;
}

int TMovicespi::rewrite(const TMask& m)
{
  int err = TRelation_application::rewrite(m);
  if (err == NOERR)
    _emask->recalc_amm();
  return err;
}

void TMovicespi::save_if_dirty() 
{ 
  if (save(true))
    set_mode(MODE_MOD);
}

bool TMovicespi::remove()
{
  const TRectype& curr = get_relation()->curr();

  const TString16 idcespite = curr.get(MOVCE_IDCESPITE);
  const TString16 idmov = curr.get(MOVCE_IDMOV);
  kill_mov(idcespite, idmov, LF_MOVAM);
  kill_mov(idcespite, idmov, LF_AMMMV);
  kill_rett(idcespite, idmov);
  TRelation_application::remove();
   
  _emask->recalc_amm();

  return true;
}

int ce1500(int argc, char* argv[])
{
  TMovicespi mc;
  mc.run(argc, argv, TR("Movimenti cespiti"));
  return 0;
}