// ba3700.cpp  - Tabella condizioni di pagamento

#include <relapp.h>       
#include <tabutil.h>
#include <msksheet.h>

#include "ba3700.h"
#include "../cg/cgpagame.h"

#define ALIAS 1
#define PERC_DECIMALS 2

class TCond_pag_app : public TRelation_application
{   
  TRelation*   _rel;
  TMask*       _msk;
  TPagamento*   _pag;
  TSheet_field* _cs;
  int          _mode;              // Modo maschera corrente
  int          _interv_rate; 
  int          _numero_rate;
  int          _riga;

protected:

  virtual bool         user_create();
  virtual bool         user_destroy();
  
  // minchia di cristo
  virtual TRelation*   get_relation() const    { return _rel;  }
  virtual TMask*       get_mask(int mode)      { return _msk;  }
  virtual bool         changing_mask(int mode) { return FALSE; }

  // file intertface
  virtual bool remove();
  virtual void init_insert_mode(TMask&);
  virtual void init_query_mode(TMask&);
  virtual int  rewrite(const TMask& m);
  virtual int  write(const TMask& m);
  virtual int  read(TMask& m);
  virtual void ini2sheet(TConfig& ini, TSheet_field &sheet);
  virtual void sheet2ini(TSheet_field &sheet,TConfig& ini);

  // notifier
  static bool          sheet_action(TSheet_field&, int r, KEY k);
  
  // handlers           
  static bool intervallo_rate     (TMask_field& f, KEY k);                  
  static bool rate_differenziate  (TMask_field& f, KEY k);                  
  static bool tipo_prima_rata     (TMask_field& f, KEY k);                  
  static bool numero_rate         (TMask_field& f, KEY k);                  
  static bool mese_commerciale    (TMask_field& f, KEY k);                  
  static bool fixed_scad          (TMask_field& f, KEY k);                  
  static bool tipo_rata           (TMask_field& f, KEY k);                  
  static bool ult_class           (TMask_field& f, KEY k);                  
  
  void*  _app_data;  
  
public:
  
  void   set_app_data(void* v) { _app_data = v;    }                
  static void* get_app_data()  { return ((TCond_pag_app &)main_app())._app_data; }
  
  TCond_pag_app() : TRelation_application() {} 
  virtual ~TCond_pag_app() {}
};

// app-data per handlers          
struct sht {
  TMask*        _msk;
  TPagamento*    _pag;
  TSheet_field* _sht;
  TArray*       _rws;
} shuttle;    

bool TCond_pag_app::user_create()
{
  _rel = new TRelation(TAB_CPG);
  _rel->add(TAB_RPG, "CODTAB[1,4]=CODTAB" ,1, 0, ALIAS);

  _interv_rate = 30;
  _pag = NULL;
  _msk = new TMask("ba3700a");
  _cs = &_msk->sfield(F_SHEET_RPG);
  _cs->set_notify(sheet_action);
  
  _msk->set_handler (F_INT_RATE        , intervallo_rate);        
  _msk->set_handler (F_RATE_DIFF       , rate_differenziate);     
  _msk->set_handler (F_TIPO_PRIMA_RATA , tipo_prima_rata);        
  _msk->set_handler (F_NUM_RATE        , numero_rate);            
  _msk->set_handler (F_MESECOMM        , mese_commerciale);       
  _msk->set_handler (F_FIXSCAD_1       , fixed_scad);        
  _msk->set_handler (F_FIXSCAD_2       , fixed_scad);        
  _msk->set_handler (F_FIXSCAD_3       , fixed_scad);        
  _msk->set_handler (F_TIPO_RATA       , tipo_rata);        
  _msk->set_handler (F_ULT_CLASS       , ult_class);        
  
  shuttle._msk = _msk;
  shuttle._sht = _cs;
  shuttle._rws = new TArray;
  
  set_app_data(&shuttle);
  
  return TRUE;
}

bool TCond_pag_app::user_destroy()
{
  delete _msk;
  delete _rel;
  if (_pag) delete _pag;
  return TRUE;
}

int TCond_pag_app::read(TMask& m)
{
  m.autoload(*_rel);

  const TString& code = m.get(F_CODICE);
  int ir = m.get_int(F_INT_RATE); if (ir == 0) ir = -1;
  if (_pag != NULL) delete _pag;
  shuttle._pag = _pag = new TPagamento(code);
  _pag->set_sheet(*_cs, ir);
  if (_pag->n_rate() > 1)
    _interv_rate = _pag->scad_rata(_pag->n_rate() - 1);
  else _interv_rate = 30;
  
  *shuttle._rws = _cs->rows_array();  
  shuttle._msk->set(F_INT_RATE,_interv_rate);
  shuttle._msk->set(F_NUM_RATE,_pag->n_rate());
  shuttle._msk->set(F_RATE_DIFF, _pag->rate_differenziate() ? "1" : "2", TRUE);
  
  return NOERR;
}

void TCond_pag_app::init_insert_mode(TMask& m)
{
  const TString& code = m.get(F_CODICE);
  if (_pag != NULL) delete _pag;
  shuttle._pag = _pag = new TPagamento(code);
  _pag->set_sheet(*_cs);

  *shuttle._rws = _cs->rows_array();       
  shuttle._msk->field(F_INT_RATE).set("30");
  shuttle._msk->field(F_INIZIOSCAD).set("F");
  shuttle._msk->field(F_NUM_RATE).set("1"); 
  shuttle._msk->set(F_RATE_DIFF, "2", TRUE);
}

void TCond_pag_app::init_query_mode(TMask& m)
{
//  _cs->reset(); _cs->force_update();
  shuttle._rws->destroy();
}

// handlers

bool TCond_pag_app::intervallo_rate(TMask_field& f, KEY k)
{          
  // ricalcola tutte le scadenze
  // occorre pag->set_intervallo_rate(intervallo)       
  if (k != K_TAB || f.mask().query_mode() || !f.focusdirty()) return TRUE;
  
  sht* s = (sht*)get_app_data();
  TPagamento*    pag = s->_pag;
  TSheet_field* shf = s->_sht;
  TMask*        msk = s->_msk;
  
  if (!pag || msk->get(F_INT_RATE).empty()) return TRUE;

  pag->set_intervallo_rate(msk->get_int(F_INT_RATE));
                                                 
  if (pag->dirty())
  {
    pag->set_sheet(*shf);  
    (*(s->_rws)) = shf->rows_array(); 
    shf->force_update(); 
    msk->field(F_MESECOMM).set(pag->mese_commerciale() ? "X" : "");
  }  
  else 
  {
    char r[8]; 
    sprintf(r, "%d", pag->intervallo_rate());
    f.set(r);
  }  
  
  return TRUE;
}

bool TCond_pag_app::rate_differenziate(TMask_field& f, KEY k)                  
{
  // se erano differenziate e non lo sono piu' occorre riaggiustare
  // il riaggiustabile; altrimenti si lascia cosi'
  // pag->set_rate_differenziate() 
  if (k != K_TAB || f.mask().query_mode()) return TRUE;

  sht* s = (sht*)get_app_data();
  TPagamento*    pag = s->_pag;
  TSheet_field* shf = s->_sht;
  TMask*        msk = s->_msk;
  TArray*       rws = s->_rws;
  
  if (!pag) return TRUE;
  pag->set_rate_differenziate(msk->get_int(F_RATE_DIFF));
  if (pag->dirty())
  {
    pag->set_sheet(*shf);
    (*rws) = shf->rows_array();
    shf->force_update(); 
  }
  return TRUE;
}                                                                 

bool TCond_pag_app::tipo_prima_rata(TMask_field& f, KEY k)                  
{   
  // aggiunge o toglie se necessario rata 0, lasciando 
  // le altre e shiftando le scadenze 
  if (f.mask().query_mode())
    return TRUE;
  sht* s = (sht*)get_app_data();
  TPagamento*    pag = s->_pag;
  TSheet_field* shf = s->_sht;
  TMask*        msk = s->_msk;
  TArray*       rws = s->_rws;
  
  if (!pag || pag->n_rate() == 0) return TRUE;

  int ir = msk->get_int(F_INT_RATE);
  if (ir == 0) ir = -1;

  pag->set_tipo_prima_rata(msk->get_int(F_TIPO_PRIMA_RATA),ir);
  if (pag->dirty())
  {
    pag->set_sheet(*shf);  
    (*rws) = shf->rows_array(); 
    shf->force_update(); 
    msk->set(F_NUM_RATE, pag->n_rate());
  }
  return TRUE;
}

bool TCond_pag_app::numero_rate(TMask_field& f, KEY k)                  
{
  // azzera tutto e ricrea da capo mantenendo le scadenze che ci sono 
  if (k != K_TAB || f.mask().query_mode()) return TRUE;

  sht* s = (sht*)get_app_data();
  TPagamento*    pag = s->_pag;
  TSheet_field* shf = s->_sht;
  TMask*        msk = s->_msk;
  TArray*       rws = s->_rws;
  int nr = msk->get_int(F_NUM_RATE);
  if (!pag || nr == pag->n_rate() || msk->get(F_NUM_RATE).empty()) 
    return TRUE;
  int ir = msk->get_int(F_INT_RATE);
  if (ir == 0) ir = -1;
  
  pag->set_numero_rate(nr,ir);       
  if (pag->dirty())
  {
    pag->set_sheet(*shf);  
    (*rws) = shf->rows_array(); 
    shf->force_update(); 
  }                              
  
  msk->set(F_NUM_RATE,pag->n_rate());
  return TRUE;
}

bool TCond_pag_app::mese_commerciale(TMask_field& f, KEY k)                  
{
  // setta mcomm e ricalcola le scadenze 
  // pag->set_mese_commerciale()
  //  if (k != K_ENTER) return FALSE;
  if (f.mask().query_mode())
    return TRUE;

  sht* s = (sht*)get_app_data();
  TPagamento*    pag = s->_pag;
  TSheet_field* shf = s->_sht;
  TMask*        msk = s->_msk; 
  TArray*       rws = s->_rws;

  if (!pag) return TRUE;
  int ir = msk->get_int(F_INT_RATE);
  if (ir == 0) ir = -1;

  pag->set_mese_commerciale(msk->get_bool(F_MESECOMM),ir);
  if (pag->dirty())
  {
    pag->set_sheet(*shf);
    (*rws) = shf->rows_array(); 
    shf->force_update(); 
    msk->set(F_INT_RATE,ir); 
  }
  return TRUE;
}

bool TCond_pag_app::fixed_scad(TMask_field& f, KEY k)                  
{
  // setta mcomm e ricalcola le scadenze 
  // pag->set_mese_commerciale()
  if (!f.to_check(k) || f.mask().query_mode()) return TRUE;
  sht* s = (sht*)get_app_data();
  TPagamento*    pag = s->_pag;

  if (!pag) return TRUE;                                                           
  
  const int val = atoi(f.get()); 
  if (val < 0 || val >31 )
    return f.error_box("Giorno errato");

  int i = 0;
  static int fld_arr[3] = {F_FIXSCAD_1, F_FIXSCAD_2, F_FIXSCAD_3};
  
  for (i = 0 ; i < 3; i++)
   if (f.dlg() == fld_arr[i])
     break;
  CHECK( i < 3, "Unknown field");
  
  if (i > 0)
  { 
    const int prev_val = f.mask().get_int(fld_arr[i -1]);
    
    if (val != 0 && (prev_val == 0 || prev_val >= val))
      return f.error_box(TR("Giorno non in sequenza"));
  }         
  if (k != K_ENTER && i < 2)
  {
    const int next_val = f.mask().get_int(fld_arr[i + 1]);
    
    if (next_val != 0 && next_val < val)
      return f.error_box(TR("Giorno non in sequenza"));
    }

  pag->set_fixed_scad(val, i);

  return TRUE;
}

bool TCond_pag_app::tipo_rata(TMask_field& f, KEY k)                  
{
  if (f.mask().query_mode())
    return TRUE; 
  sht* s = (sht*)get_app_data();
  TPagamento*    pag = s->_pag;
  TSheet_field*  shf = s->_sht;

  if (k == K_TAB && pag && f.focusdirty())  
  {
    pag->set_default_ulc("");
    f.mask().set(F_ULT_CLASS,"");
    pag->set_default_type(atoi(f.get()));
    pag->set_sheet(*shf);
    shf->force_update(); 
  }
  return TRUE;
}

bool TCond_pag_app::ult_class(TMask_field& f, KEY k)                  
{                            
  if (f.mask().query_mode())
    return TRUE; 
  sht* s = (sht*)get_app_data();
  TPagamento*    pag = s->_pag;
  TSheet_field*  shf = s->_sht;

  if (k == K_TAB && pag && f.focusdirty())  
  {
    pag->set_default_type(atoi(f.mask().get(F_TIPO_RATA)));
    pag->set_default_ulc(f.get());
    pag->set_sheet(*shf);
    shf->force_update(); 
  }
  return TRUE;
}

// qui viene il belloccio                  
bool TCond_pag_app::sheet_action(TSheet_field&, int r, KEY k)                                                                                
{                                              
  bool doit = TRUE, m_perc = FALSE;
  bool m_scad = FALSE, m_tipo = FALSE, m_ulc = FALSE, mod = FALSE;
  word ahiahi = P_OK;                                

  sht* s = (sht*)get_app_data();
  TPagamento*    pag = s->_pag;
  TSheet_field* shf = s->_sht;
  TMask*        msk = s->_msk;
  TArray*       rws = s->_rws;
  
  if (!pag) return TRUE;

  TString news(15), newp(15), newt(1), newu(1);                                  
  TToken_string ts(36), ns(36);
  
  switch (k)
  {
  case K_SPACE:
    break;
  case K_ENTER:
  {     
    ns  = shf->row(r);     
    ts  = (TToken_string&)(*rws)[r];    
    
    news = ns.get(0);
    newp = ns.get();
    newt = ns.get();                                            
    newu = ns.get();                                            
    
    // qui viene il bello, si fa per dire 
    if (ts.get_int(0) != atoi(news))  // modificata scadenza
      mod  = m_scad = TRUE;        

    real p0(ts.get(1)); p0.round(PERC_DECIMALS);
    real p1(newp); p1.round(PERC_DECIMALS);

    if (p0 != p1)  // modificata percentuale
      mod  = m_perc = TRUE;        

    if (newt != ts.get(2))  // modificato tipo pagamento
      mod  = m_tipo = m_ulc = TRUE;      
    
    if (newu != ts.get(3))  // modificata ulteriore classificazione
      mod  = m_ulc = TRUE;       
    else
     if (m_tipo)
       newu = "";
  }
    break;
  case K_DEL:
  case K_INS:
    doit = FALSE;
    break;
  default:
    break;
  }          
  
  // settato da recalc_rate se occorre ridefinire lo sheet
  // aggiungendo o togliendo righe
  bool need_recalc = FALSE;      

  if (k == K_ENTER) 
  {
    if (mod)
    {
      // ricalcola sheet come sai fare tu
      int rdiff = atoi(msk->get(F_RATE_DIFF));
      ahiahi = pag->recalc_rate(r, m_perc, 
                                (m_perc ? (const char*)newp : NULL),
                                NULL,
                                (m_scad ? (const char*)news : NULL),
                                (m_tipo ? (const char*)newt : NULL),  
                                (m_ulc  ? (const char*)newu : NULL), 
                                rdiff, 
                                pag->mese_commerciale(),
                                need_recalc);
      // see if parameters changed                          
      msk->set(F_RATE_DIFF, pag->rate_differenziate() ? "1" : "2", TRUE);
      msk->set(F_MESECOMM, pag->mese_commerciale()   ? "X" : "");
    }
    if (ahiahi)
      // rimetti le righe com'erano prima
    {  
      TString s(256);
      pag->strerr(ahiahi,s);
      warning_box(s);
      shf->row(r) = (TToken_string&)(*rws)[r]; 
      shf->force_update(r); 
      msk->set(F_NUM_RATE, pag->n_rate());
    } 
    
    else if (mod && need_recalc) 
    {                                   
     // ridefinisci lo sheet sulla base delle nuove rate
      pag->set_sheet(*shf);
      shf->force_update();  
      (*rws) = shf->rows_array();      
    }                                                 

    msk->set(F_NUM_RATE, pag->n_rate()); 
  }

  return doit;
}


int TCond_pag_app::rewrite(const TMask& m)
{
  TTable& tab_cpg = (TTable&)_rel->lfile();
  TTable& tab_rpg = (TTable&)_rel->lfile(-ALIAS);
  const int err = _pag->rewrite(tab_rpg); 
  if (err != NOERR) return err; 
  m.autosave(*_rel);
  tab_cpg.put("B1", _pag->rate_differenziate() ? "X" : "");
  return _rel->lfile().rewrite();
}

int TCond_pag_app::write(const TMask& m)
{
  TTable& tab_cpg = (TTable&)_rel->lfile();
  TTable& tab_rpg = (TTable&)_rel->lfile(-ALIAS);  
  const int err = _pag->write(tab_rpg);  
  if (err != NOERR) return err; 
  m.autosave(*_rel);      
  tab_cpg.put("B1", _pag->rate_differenziate() ? "X" : "");
  return _rel->lfile().write();
}

bool TCond_pag_app::remove()
{
  TTable& tab_rpg = (TTable&)_rel->lfile(-ALIAS);
  return _pag->remove(tab_rpg) == NOERR && _rel->lfile().remove() == NOERR;
}

void TCond_pag_app::ini2sheet(TConfig& ini, TSheet_field &sheet)
{                  
  const TMask& m = sheet.mask();
  const int nrate = m.get_int(F_NUM_RATE); 
  TString16 par;
  for (int r = 0; r < nrate; r++)
  {
    par.format("%d,%d", LF_TABCOM, r+1);
    if (ini.set_paragraph(par))
    {  
      TToken_string& row = sheet.row(r);
      row = ini.get("I0");
      row.add(ini.get("R0"));
      row.add(ini.get("I1"));
      row.add(ini.get("S1"));
    }
  }
}

void TCond_pag_app::sheet2ini(TSheet_field &sheet,TConfig& ini)
{
  const TMask& m = sheet.mask();
  const int nrate = m.get_int(F_NUM_RATE); 
  TString16 par;
  int r;
  
  for (r = 0; r < nrate; r++)
  {
    par.format("%d,%d", LF_TABCOM, r+1);
    ini.set_paragraph(par);
    TToken_string& row = sheet.row(r);
    ini.set("I0", row.get(0));
    ini.set("R0", row.get());
    ini.set("I2", row.get());
    ini.set("S1", row.get());
  }
  for ( ; ; r++)
  {
    par.format("%d,%d", LF_TABCOM, r+1);
    if (ini.set_paragraph(par))
      ini.remove_all();
    else
      break;  
  }
}

int ba3700(int argc, char* argv[])
{
  TCond_pag_app a;
  a.run(argc, argv, TR("Condizioni di pagamento"));
  return 0;
}