#include <execp.h>
#include <mailbox.h>

#include "../cg/conto.h"
#include "../cg/pagament.h"
#include "../cg/saldacon.h"
#include "../cg/cg21sld.h"

#include "sc0100.h"
#include "sc0100a.h"

#define __EXTRA__
#include "../cg/cg2105.cpp"

///////////////////////////////////////////////////////////
// Maschera gestione rate
///////////////////////////////////////////////////////////

class TFattura_mask : public TMask
{
  TRiga_partite& _fattura;
  TPagamento* _pag;
  TString_array _pag_rows;
  
protected:        
  static bool totdoc_handler(TMask_field& f, KEY k);
  static bool totval_handler(TMask_field& f, KEY k);
  static bool cambio_handler(TMask_field& f, KEY k);
  static bool pag_notify(TSheet_field& ps, int r, KEY k);
  static bool pag_handler(TMask_field& f, KEY key);
  static bool reset_handler(TMask_field& f, KEY key);
  static bool recalc_handler(TMask_field& f, KEY key);
  static bool nrate_handler(TMask_field& f, KEY key);
  static bool codcab_handler(TMask_field& f, KEY key);

  TPagamento& pagamento() const { return *_pag; }
  TSheet_field& pag_sheet() const { return (TSheet_field&)field(FS_RATESHEET); }
  void pag2sheet();
  
  void read_scadenze();
  
public:
  void write_scadenze() const;

  TFattura_mask(TRiga_partite& fattura, const TString& codpag);
  virtual ~TFattura_mask();
};

TFattura_mask::TFattura_mask(TRiga_partite& fattura, const TString& codpag) 
             : TMask("sc0100b"), _fattura(fattura), _pag(NULL)
{             
  enable(FS_IMPONIBILI);   // Abilita il totale documento
  
  enable(FS_VALUTA);       // Abilita campi valuta
  enable(FS_DATACAMBIO);
  enable(FS_CAMBIO);
  
  hide(FS_IMPOSTE);        // Elimina le imposte (non gestite in extra-contabile)
  show(FS_TOTVAL);         // Attiva il totale documento in valuta

  
  set_handler(FS_IMPONIBILI, totdoc_handler);
  set_handler(FS_TOTVAL, totval_handler);
  set_handler(FS_CAMBIO, cambio_handler);

  set_handler(FS_RESET, reset_handler);
  set_handler(FS_RECALC, recalc_handler);
  set_handler(FS_NRATE, nrate_handler);
  set_handler(FS_NSCAB, codcab_handler);
  set_handler(FS_VSCAB, codcab_handler);

  pag_sheet().set_notify(pag_notify);
  pag_sheet().set_handler(pag_handler);
  
  const TRiga_partite& partita = _fattura;
  const TString datadoc = partita.get(PART_DATADOC);
  _pag = new TPagamento(codpag, datadoc);
  
  read_scadenze();
} 
                            
TFattura_mask::~TFattura_mask()
{
  delete _pag;
}
                            
void TFattura_mask::pag2sheet()
{
  TPagamento& pag = pagamento();
  TSheet_field& ps = pag_sheet();
  pag.set_sheet(ps);
  _pag_rows = ps.rows_array();
}

bool TFattura_mask::pag_notify(TSheet_field& ps, int r, KEY k)
{       
  if (k == K_INS || k == K_DEL)
    return FALSE;
  if (k != K_ENTER)
    return TRUE;
                
  TFattura_mask& fm = (TFattura_mask&)ps.mask();
  TPagamento&    pag = *fm._pag;
  TString_array& rws = fm._pag_rows;                                
                                               
  const int rdiff = fm.get_int(FS_RDIFFER);
  const bool in_valuta = pag.in_valuta(); 
  const int impos = in_valuta ? 2 : 1;
  
  bool doit = TRUE, m_imp = FALSE, m_perc = FALSE, m_pag = FALSE;
  bool m_scad = FALSE, m_tipo = FALSE, m_ulc = FALSE, mod = FALSE;
  word ahiahi = P_OK;                                
  
  const bool recalc = fm.get_bool(FS_RECALC); 
  const bool mcomm  = fm.get_bool(FS_MCOMM);
  
  TToken_string ns  = ps.row(r);     
  TToken_string ts  = rws.row(r);    
    
  const TString news = ns.get(0);           // Data scadenza
  const TString newi = ns.get(impos);       // Imponibile (lire o valuta)
  const TString newp = ns.get(3);           // Percentuale
  const TString newt = ns.get(4);           // Tipo pagamento                                 
  const TString newu = ns.get(5);           // Ulteriore classificazione
    
  // qui viene il bello, si fa per dire 
  if (news != ts.get(0))  // modificata data scadenza
    mod = m_scad = TRUE;        
  if (newp != ts.get(3))  // modificata percentuale
    mod = m_perc = TRUE;        
  if (newi != ts.get(impos))  // modificato importo
  {          
    // se si modifica la percentuale l'importo non viene cagato
    if ((recalc && !m_perc) || (!recalc)) 
      mod = m_imp = TRUE;   
  }
  if (newt != ts.get(4))  // modificato tipo pagamento
    mod = m_tipo = m_ulc = TRUE;      
  if (newu != ts.get(5))  // modificato tipo pagamento
    mod = m_ulc  = TRUE;      

  // settato da recalc_rate se occorre ridefinire lo sheet
  // aggiungendo o togliendo righe
  bool need_recalc = FALSE;      

  if (mod && recalc)
  {
    // ricalcola sheet come sai fare tu
    ahiahi = pag.recalc_rate(r, m_perc, 
                             ((m_perc || m_imp) ? 
                              (m_perc ? (const char*)newp : (const char*)newi) : 
                              NULL), 
                             (m_scad ? (const char*)news : NULL),
                             (m_tipo ? (const char*)newt : NULL),  
                             (m_ulc  ? (const char*)newu : NULL), 
                             rdiff, mcomm, need_recalc);
    // see if rdiff changed                          
    fm.set(FS_RDIFFER, pag.rate_differenziate() ? "1" : "2");
    fm.set(FS_NRATE, pag.n_rate());
  }
  if (!recalc)
  {                              
    ahiahi = P_OK;
    // put data as they are
    TToken_string& trw = pag.rata(r);
    TToken_string  srw = trw;
    if (m_tipo) trw.add(newt,2);
    if (m_scad) trw.add(news,3);
    if (m_imp)  
    {
      trw.add(newi,4);
      const real p = pag.recalc_percrata(r);
      ps.row(r).add(p.string(), 3);
      rws.row(r) = ps.row(r);
      ps.force_update(r); 
    }  
  }
  if (ahiahi)  // any error? Rimetti le righe com'erano prima
  {         
    if (recalc)
    {
      beep(); 
      ps.row(r) = rws.row(r); 
      ps.force_update(r); 
    }  
  } 
  else if (recalc && mod && need_recalc) 
  {                                   
    // ridefinisci lo sheet sulla base delle nuove rate
    fm.pag2sheet();
  } 
  
  return doit;
}                            


bool TFattura_mask::totdoc_handler(TMask_field& f, KEY k)
{
  if (k == K_TAB && f.focusdirty())
  {                        
    TFattura_mask& fm = (TFattura_mask&)f.mask();
    const bool valuta = fm.get(FS_VALUTA).not_empty();
    
    if (valuta)
    {          
      if (fm.get(FS_TOTVAL).empty())
      {
        const real cambio(fm.get(FS_CAMBIO));
        if (!cambio.is_zero())
        {
          real tot(f.get());
          tot /= cambio;
          fm.set(FS_TOTVAL, tot, TRUE);
        }
      }  
    }
    else
    {
      TPagamento& pag = fm.pagamento();
      const real tot(f.get());
      pag.set_total(tot, ZERO, ZERO);                              
      pag.set_rate_auto();      
      fm.pag2sheet();    
    }  
  }
  return TRUE;
} 

bool TFattura_mask::totval_handler(TMask_field& f, KEY key)
{
  if (key == K_TAB && f.focusdirty())
  {  
    TFattura_mask& fm = (TFattura_mask&)f.mask();

    real totval(f.get());
    real cambio(fm.get(FS_CAMBIO));
    real totale(fm.get(FS_IMPONIBILI));
    if (totale.is_zero())
    {
      totale = totval * cambio;
      fm.set(FS_IMPONIBILI, totale, TRUE);
    }
    else
    {
      if (cambio.is_zero() && !totval.is_zero())
      {
        cambio = totale / totval;
        fm.set(FS_CAMBIO, cambio, TRUE);
      }  
      
      TPagamento& pag = fm.pagamento();
      pag.set_total(totval, ZERO, ZERO);                              
      pag.set_rate_auto();      
      fm.pag2sheet();    
    }  
  }
  return TRUE;
}

bool TFattura_mask::cambio_handler(TMask_field& f, KEY key)
{
  if (key == K_TAB && f.focusdirty())
  {  
    TFattura_mask& fm = (TFattura_mask&)f.mask();
    TPagamento& pag = fm.pagamento();
    const real cambio(f.get());
    pag.set_cambio(cambio);
  }
  return TRUE;
}


bool TFattura_mask::pag_handler(TMask_field& f, KEY key)
{           
  if (key == K_TAB || key == K_ENTER)
  {                  
    TFattura_mask& fm = (TFattura_mask&)f.mask();
    TPagamento& pag = *fm._pag;
    const word err = pag.validate();
    if (err != P_OK)
    {
      TString256 s;
      pag.strerr(err, s);
      return error_box(s); 
    }
  }
  return TRUE;
}

bool TFattura_mask::reset_handler(TMask_field& f, KEY key)
{                                 
  if (key == K_SPACE)
  {
    TFattura_mask& fm = (TFattura_mask&)f.mask();
    if (fm._fattura.rate() == 0)
      fm._pag->read();
    fm.read_scadenze();
  }
  return TRUE;
}

bool TFattura_mask::recalc_handler(TMask_field& f, KEY key)
{
  if (key == K_SPACE)
  {
    const bool on = f.get().not_empty();
    const TMask& m = f.mask();
    TSheet_field& ps = (TSheet_field&)m.field(FS_RATESHEET);
    ps.enable_column(3, on);
  }
  
  return TRUE;
}

bool TFattura_mask::nrate_handler(TMask_field& f, KEY key)
{                                 
  if (key == K_TAB && f.focusdirty())
  {
    TFattura_mask& fm = (TFattura_mask&)f.mask();
    TPagamento& pag = *fm._pag;
    pag.set_numero_rate(atoi(f.get()));
    if (pag.dirty())
      fm.pag2sheet();
  }
  return TRUE;
}

bool TFattura_mask::codcab_handler(TMask_field& f, KEY key)
{
  if (key == K_TAB && f.focusdirty())
  {
    const TMask& m = f.mask();
    const short id = f.dlg() == FS_NSCAB ? FS_NSABI : FS_VSABI;
    const int pos = id == FS_NSABI ? 7 : 9;
    const TString16 abi = m.get(id);
    const TString16 cab = m.get(id+1);
    TSheet_field& ps = (TSheet_field&)m.field(FS_RATESHEET);
    for (int i = ps.items()-1; i >= 0; i--)
    {
      TToken_string& row = ps.row(i);
      row.add(abi, pos);
      row.add(cab, pos+1);
    }
    ps.force_update();
  }
  
  return TRUE;
}

void TFattura_mask::read_scadenze()
{              
  TRiga_partite& partita = _fattura;                  
  TPagamento& pag = *_pag;        
  TSheet_field& ps = pag_sheet();

  real imponibile, imposte, spese, cambio;
  
  if (partita.in_valuta())
  {                       
    imponibile = partita.get_real(PART_IMPORTOVAL);
    cambio = partita.get_real(PART_CAMBIO);
    
    real imp = imponibile;
    imp *= cambio;
    imp.round();
    set(FS_IMPONIBILI, imp);
  }
  else
  {
    imponibile = partita.get_real(PART_IMPORTO);
    set(FS_IMPONIBILI, imponibile);
  }
  
  pag.set_total(imponibile, imposte, spese);
  pag.set_cambio(cambio);
  
  if (partita.rate() > 0)
  {
    pag.zap_rate();
    for (int i = 1; i <= partita.rate(); i++)
    { 
      const TRiga_scadenze& scadenza = partita.rata(i);
      real  importo     = scadenza.get(SCAD_IMPORTO); 
      const TDate scad  = scadenza.get(SCAD_DATASCAD);
      const int tipop   = scadenza.get_int(SCAD_TIPOPAG);     
      const bool paid   = scadenza.get_bool(SCAD_PAGATA);
      const TString ulc = scadenza.get(SCAD_ULTCLASS);                       
      if (scadenza.in_valuta())
      {                                                        
        const real impval(scadenza.get(SCAD_IMPORTOVAL));
        pag.set_rata(i-1, impval, importo, scad, tipop, ulc, paid);
      }
      else
        pag.set_rata(i-1, importo, ZERO, scad, tipop, ulc, paid);
          
      TToken_string& str = ps.row(i-1);
      str.add(scadenza.get(SCAD_CODABIPR), 7);
      str.add(scadenza.get(SCAD_CODCABPR), 8);
      str.add(scadenza.get(SCAD_CODABI),   9);
      str.add(scadenza.get(SCAD_CODCAB),   10);    
      if (i == 1)
      {
        set(FS_NSABI, str.get(7));
        set(FS_NSCAB, str.get(8));
        set(FS_VSABI, str.get(9));
        set(FS_VSCAB, str.get(10));
        set(FS_AGENTE, scadenza.get(SCAD_CODAG));
      }
    } 
  }
  else
  {
    pag.set_rate_auto();
  }
    
  set(FS_RDIFFER, pag.rate_differenziate() ? "1" : "2");
  set(FS_NAMEPAG, pag.name());
  set(FS_TIPOPR,  pag.desc_tpr());
  set(FS_MCOMM,   pag.mese_commerciale() ? "X" : "");
  set(FS_RECALC,  "X", TRUE);
  set(FS_NRATE,   pag.n_rate());
                        
  // se la prima rata e' fissa non si tocca
  if (pag.tipo_prima_rata() > 3)
    ps.disable_cell(0,-1);

  pag2sheet();                      
}

void TFattura_mask::write_scadenze() const
{                                   
  CHECK(_pag, "Null pagament");
  const TPagamento& pag = *_pag;          
  TSheet_field& ps = pag_sheet();
                                         
  _fattura.put(PART_IMPORTO,    get(FS_IMPONIBILI));
  _fattura.put(PART_CODVAL,     get(FS_VALUTA));
  _fattura.put(PART_DATACAM,    get(FS_DATACAMBIO));
  _fattura.put(PART_CAMBIO,     get(FS_CAMBIO));
  _fattura.put(PART_IMPORTOVAL, get(FS_TOTVAL));
  
  const TString agente = get(FS_AGENTE);
  
  const int old_rate = _fattura.rate();
  for (int i = 0; i < pag.n_rate(); i++)
  {    
    TToken_string& row = ps.row(i);
    
    TRiga_scadenze& scadenza = i >= old_rate ? _fattura.new_row() : _fattura.rata(i+1); 
    scadenza.put(SCAD_CODPAG,   pag.code());
    scadenza.put(SCAD_TIPOPAG,  pag.tipo_rata(i));
    scadenza.put(SCAD_ULTCLASS, pag.ulc_rata(i));
    if (_fattura.in_valuta())    
    {
      scadenza.put(SCAD_IMPORTO, pag.tlit_rata(i));
      scadenza.put(SCAD_IMPORTOVAL, pag.tpay_rata(i));
    }
    else
    {
      scadenza.put(SCAD_IMPORTO, pag.tpay_rata(i));
      scadenza.zero(SCAD_IMPORTOVAL);
    }  
    scadenza.put(SCAD_DATASCAD, pag.data_rata(i));
    scadenza.put(SCAD_CODABIPR, row.get(7));    
    scadenza.put(SCAD_CODCABPR, row.get(8));    
    scadenza.put(SCAD_CODABI,   row.get(9));    
    scadenza.put(SCAD_CODCAB,   row.get(10));    
    scadenza.put(SCAD_CODAG,    agente);
    scadenza.put(SCAD_PAGATA,   scadenza.chiusa(TRUE));
  }
  
  TPartita& game = _fattura.partita();
  if (i < old_rate)
  {
    const int riga = _fattura.get_int(PART_NRIGA);
    for (int r = old_rate; r > i; r--)
    {
      game.scollega_pagamenti(riga, r);
      _fattura.elimina_rata(r);
    }
  }  

  game.chiusa(TRUE);
}


///////////////////////////////////////////////////////////
// Addendum alla maschera gestione partite
///////////////////////////////////////////////////////////

bool TGame_mask::edit_fattura(TPartita& p, int nriga)
{                                       
  const bool nuova = nriga <= 0;          
  if (nuova)
    nriga = nuova_riga(p);
  
  TRiga_partite& f = p.riga(nriga);
  if (f.rate() > 0)
    _codpag = f.rata(1).get(SCAD_CODPAG);

  TFattura_mask fm(f, _codpag);
  const KEY k = fm.run();

  if (k == K_ENTER)  
  {
    fm.write_scadenze();
  }
  else
  {
    if (nuova || k == K_DEL)
      p.rimuovi_riga(nriga);
  }  
  
  return k != K_ESC;
}              

bool TGame_mask::prima_nota(const long nreg)
{   
  bool ok = FALSE;
  bool can_run = TRUE;
  if (_changed)
  {
    can_run = yesno_box("Salvare le parite modificate?");
    if (can_run)
    {
      app().partite().rewrite(); 
      _changed = FALSE;             // Resetta flag di cambiato
    }  
  }
  if (can_run)
  {
    app().partite().destroy();       // Distrugge tutte le partite in memoria

    const char* const pn = "cg2 -0";
    TExternal_app prima_nota(pn);
    TString16 numreg; numreg << "1|" << nreg;
    TMessage msg(pn, MSG_LN, numreg);
    msg.send();                      // Messaggio di collegamento al movimento nreg
    ok = prima_nota.run() == 0;      // Lancia la prima nota

    const int       anno   = get_int(P_ANNO); 
    const TString16 numero = get(P_NUMERO);
    fill_partite(anno, numero);      // Rilegge partite
  }
  
  return ok;
}

///////////////////////////////////////////////////////////
// Gestione partite extra-contabili
///////////////////////////////////////////////////////////

void TSaldaconto_app::edit_partite(const TMask& m)
{                                           
  const char t = m.get(F_TIPO)[0];
  const int g  = m.get_int(F_GRUPPO);
  const int c  = m.get_int(F_CONTO);
  const long s = m.get_long(F_SOTTOCONTO);
  const TBill b(g, c, s, t);                                // Legge il conto della riga selezionata
  TGame_mask gm(b, 0, 0);                
  gm.run();
  if (gm.changed() && yesno_box("Si vogliono salvare le partite modificate?"))
    partite().rewrite();
  partite().destroy();                
}

