#include <applicat.h>
#include <automask.h>
#include <defmask.h>
#include <dongle.h>
#include <modaut.h>
#include <progind.h>
#include <reputils.h>

#include <rcausali.h>
#include <clifo.h>

#include "../cg/cgsaldac.h"
#include "../cg/cg2101.h"

#include "pi0002.h"
#include "pi0002100a.h"

///////////////////////////////////////////////////////////
// Main Mask
///////////////////////////////////////////////////////////

class TPF_mask : public TAutomask
{
  TRelation* _rel;

protected:
  virtual bool on_field_event(TOperable_field& o, TField_event e, long jolly);
  bool on_sheet_event(TOperable_field& o, TField_event e, long jolly);
  
  
  TPartita& partita(TPartite_array& partite, int nrow);
  TRiga_scadenze& rata(TPartite_array& partite, int nrow);

  bool update_mov(bool simulation);

public:
  bool update_mov();
	void update_sheet();
  bool calc_residual(const TRiga_scadenze& scad, real& impres, 
                     bool& partially_unassigned, int& tipopag) const;
  bool fill_row(const TRiga_partite& rp, const TRiga_scadenze& rs, TToken_string& row, bool& partially_unassigned, bool force);
  void update_total(bool editing = false);
  TPF_mask();
  virtual ~TPF_mask();
};

bool TPF_mask::on_sheet_event(TOperable_field& o, TField_event e, long jolly)
{
  TMask& m = o.mask();
  switch (o.dlg())
  {
	case F_PAGATO:
    if (e == fe_modify)
			update_total(true);
    break;
  case F_IMPORTOANT:
    if (e == fe_modify)
			update_total(true);
    break;
  case F_SHEET:
    switch(e)
    {
    case se_query_del:
		case se_query_add:
			return false;
		case se_notify_modify:
			update_total();
			break;
		default:
			break;
    }
    break;
  default:
    break;
  }
  return true;
} 

bool TPF_mask::on_field_event(TOperable_field& o, TField_event e, long jolly)
{
	const int id = o.dlg();
	switch (id)
	{
	case DLG_SAVEREC:
	  if (e == fe_button && check_fields())
		{
      update_mov();
			update_sheet();
		}
		break;
	case F_CLIENTE:
		if (e == fe_modify)
			update_sheet();
		break;
 	case F_CODCAUS:
		if (e == fe_init)
    {
      // Cerca di proporre una causale in base alla configurazione della generazione effetti
      const TString& codcaus = ini_get_string(CONFIG_DITTA, "ef", MOV_CODCAUS);
			if (codcaus.full())
      {
        o.set(codcaus);
        if (!o.check())
          o.reset();
      }
    }
		break;
  case F_SHEET:
		return on_sheet_event(o, e, jolly);
	default:
		if (jolly > 0)
			return on_sheet_event(o, e, jolly);
		break;
	}
  return true;
}

TPartita& TPF_mask::partita(TPartite_array& partite, int nrow)
{
  TSheet_field& s = sfield(F_SHEET);
  TToken_string& row = s.row(nrow);
	const int posanno = s.cid2index(F_ANNO);
	const int posnumpart = s.cid2index(F_PARTITA);

  const TBill bill(0, 0, get_long(F_CLIENTE), 'C');
  TPartita& game = partite.partita(bill, row.get_int(posanno), row.get(posnumpart));
  return game;
}

TRiga_scadenze& TPF_mask::rata(TPartite_array& partite, int nrow)
{
  TSheet_field& s = sfield(F_SHEET);
  const TToken_string& row = s.row(nrow);
	const int posnriga = s.cid2index(F_RIGA);
	const int posnrata = s.cid2index(F_RATA);
  int nriga, nrata;
  row.get(posnriga, nriga);
  row.get(posnrata, nrata);
  return partita(partite, nrow).rata(nriga, nrata);
}

bool TPF_mask::update_mov(bool simulation)
{
  TLog_report log(main_app().title());

  bool can_write = !simulation;
  bool done = true;

  TSheet_field& s = sfield(F_SHEET);
	const int pospagato = s.cid2index(F_PAGATO);
	const int posanticip = s.cid2index(F_IMPORTOANT);
	const int posabi = s.cid2index(F_ABI);
	const int poscab = s.cid2index(F_CAB);
	const int posanno = s.cid2index(F_ANNO);
	const int posnumpart = s.cid2index(F_PARTITA);
	const int posnriga = s.cid2index(F_RIGA);
	const int posnrata = s.cid2index(F_RATA);
  TToken_string key;

  const long cliente = get_long(F_CLIENTE);
  key = "C"; key.add(cliente);
  const TRectype& clifo = cache().get(LF_CLIFO, key);

  TBill conto_cliente;
  key = get(F_CODCAUS); key.add(1);
  const TRectype& rcaus1 = cache().get(LF_RCAUSALI, key);
  const char sez1 = rcaus1.get_char(RCA_SEZIONE);
  const char sez2 = sez1 == 'A' ? 'D' : 'A';

  conto_cliente.get(rcaus1);
  conto_cliente.codclifo() = cliente;
  if (!conto_cliente.find())
  {
    conto_cliente.get(clifo);
    if (!conto_cliente.find())
    {
      log.log(2, TR("Impossibile determinare il conto cliente: controllare la causalo e/o l'anagrafica"));
      can_write = done = false;
    }
  }

  TPartite_array partite;

  FOR_EACH_SHEET_ROW(s, pos, row)
	{
    const TString8 abi = row->get(posabi);
    const TString8 cab = row->get(poscab);
    if (abi.blank() || cab.blank())
      continue;

 	  const real importopag(row->get(pospagato));
  	const real importoant(row->get(posanticip));
    const real anticipo = importoant <= importopag ? ZERO : importoant-importopag;
    
    TRiga_scadenze& scad = rata(partite, pos);
    TString msg;
    msg << TR("Elaborazione Partita ") << scad.get_int(SCAD_ANNO) << '/' << scad.get_int(SCAD_NUMPART) 
        << TR(" Riga:") << scad.get(SCAD_NRIGA) << TR(" Rata:") << scad.get(SCAD_NRATA);
    log.log(0, msg);
    
    scad.put(SCAD_IMPORTOANT, anticipo);
    scad.put(SCAD_CODABIPR, row->get(posabi));
    scad.put(SCAD_CODCABPR, row->get(poscab));
    
  	if (importopag > ZERO)
    {
      TMovimentoPN mov;
      TRectype& head = mov.curr();
      head.put(MOV_CODCAUS, get(F_CODCAUS));
      head.put(MOV_DATAREG, get(F_DATAMOV));
      head.put(MOV_TOTDOC, importopag);

      TBill conto_banca;
      key = abi; key << cab;
      const TRectype& bnp = cache().get("BNP", key);
      conto_banca.set(bnp.get_int("I0"), bnp.get_int("I1"), bnp.get_long("I2"));
      if (!conto_banca.find())
      {
        log.log(2, TR("Impossibile determinare il conto della banca di presentazione"));
        can_write = done = false;
      }

      TRectype& riga1 = mov.cg(0);
      riga1.put(RMV_SEZIONE, sez1);
      riga1.put(RMV_IMPORTO, importopag);
      riga1.put(RMV_ROWTYPE, 'K');
      conto_cliente.put(riga1, false);
      conto_banca.put(riga1, true);

      TRectype& riga2 = mov.cg(1);
      riga2.put(RMV_SEZIONE, sez2);
      riga2.put(RMV_IMPORTO, importopag);
      riga2.put(RMV_ROWTYPE, 'I');
      conto_banca.put(riga2, false);
      conto_cliente.put(riga2, true);

      int err = NOERR;
      if (can_write)
      {
        err = mov.write(true);
        if (err == NOERR)
        {
          TString msg;
          msg << TR("Generazione movimento di pagamento ") << head.get_long(MOV_NUMREG);
          log.log(0, msg);
        }
        else
        {
          TString msg;
          msg << TR("Impossibile creare il movimento di pagamento: errore ") << err;
          log.log(2, msg);
          can_write = done = false;
        }
      }

      if (err == NOERR)
      {
        TPartita& game = scad.partita();

        TRiga_partite& rigap = game.new_row();
        const int nrigp = rigap.get_int(PART_NRIGA);
        rigap.put(PART_NREG,    head.get_long(MOV_NUMREG));
        rigap.put(PART_NUMRIG,  mov.cg(0).get(RMV_NUMRIG));
        rigap.put(PART_SEZ,     mov.cg(0).get(RMV_SEZIONE));
        rigap.put(PART_DATAREG, head.get(MOV_DATAREG));
        rigap.put(PART_DATADOC, head.get(MOV_DATADOC));
        rigap.put(PART_DATAPAG, head.get(MOV_DATAREG));
        rigap.put(PART_NUMDOC,  head.get(MOV_NUMDOC));
        rigap.put(PART_DESCR,   head.get(MOV_DESCR));
        rigap.put(PART_CODCAUS, head.get(MOV_CODCAUS));
        
        TRectype pagsca = scad.new_row(nrigp);
        pagsca.put(PAGSCA_IMPORTO, importopag);
        pagsca.put(PAGSCA_CODABIPR, abi);
        pagsca.put(PAGSCA_CODCABPR, cab);
        conto_banca.put(pagsca, true);
        if (scad.get_long(SCAD_CODCAB)>0)
        {
          pagsca.put(PAGSCA_CODABI, scad.get(SCAD_CODABI));
          pagsca.put(PAGSCA_CODCAB, scad.get(SCAD_CODCAB));
        }
        else
        {
          pagsca.put(PAGSCA_CODABI, clifo.get(CLI_CODABI));
          pagsca.put(PAGSCA_CODCAB, clifo.get(CLI_CODCAB));
        }
        game.modifica_pagamento(pagsca, TValuta(), true);
      }
    }
  }

  if (can_write)
  {
    TString msg;
    msg << TR("Aggiornamento di ") << partite.items() << TR(" partite");
    log.log(0, msg);
    if (!partite.write(true))
    {
      log.log(2, TR("Errore di aggiornamento del saldaconto"));
      can_write = done = false;
    }
  }

  if (!done || !simulation)
    log.preview();

  return done;
}

bool TPF_mask::update_mov()
{
  bool done = update_mov(true); // Simulation
  if (done)
  {
    if (yesno_box(TR("Non sono stati rilevati errori formali:\nSi desidera proseguire con l'aggiornamento del saldaconto?")))
     done = update_mov(false);   // Write on DB               
  }
  else
    error_box(TR("Sono stati rilevati errori formali che impediscono l'aggiornamento del saldaconto"));
  return done;
}


// Calcola il residuo di una rata tenendo conto anche degli eventuali
// effetti non ancora contabilizzati
bool TPF_mask::calc_residual(const TRiga_scadenze& scad, real& impres,
                             bool& partially_unassigned, int& tipopag) const
{                            
  tipopag = scad.get_int(SCAD_TIPOPAG);
  const bool valuta = scad.in_valuta();
  const char tipocf = scad.get_char(SCAD_TIPOCF);

  TImporto importo = scad.residuo(TRUE);
  importo.normalize(tipocf == 'C' ? 'D' : 'A');

  impres = importo.valore();
  partially_unassigned = FALSE;  
  
  // Contolla se ci sono pagamenti non assegnati da gestire
  const TRiga_partite& fattura = scad.riga();
  TPartita& partita = fattura.partita();
  const TRecord_array& unassigned = partita.unassigned();
  if (unassigned.rows() > 0)  
  {
    real tot_unassigned;  // Totale pagamenti non assegnati
    for (int u = unassigned.last_row(); u > 0; u = unassigned.pred_row(u))
    {
      const real imp = unassigned[u].get(valuta ? PAGSCA_IMPORTOVAL : PAGSCA_IMPORTO);
      tot_unassigned += imp;
    } 
    
    // Scala non assegnati dalle rate precedenti ancora aperte
    const int nrata = scad.get_int(SCAD_NRATA);
    for (int r = 1; r < nrata; r++)
    {                                               
      if (!fattura.rata(r).chiusa())   
      {
        TImporto res = fattura.rata(r).residuo(TRUE);
        tot_unassigned -= res.valore();
        if (tot_unassigned <= ZERO)
          break;
      }                                             
    }
    // Scala non assegnati dalla rata corrente
    if (tot_unassigned > ZERO)
    {
      if (tot_unassigned >= impres)
        impres = ZERO;
      else
        impres -= tot_unassigned;
      partially_unassigned = TRUE;  
    }
  }
  return !impres.is_zero();
}

void TPF_mask::update_total(bool editing)
{
  const TSheet_field& s = sfield(F_SHEET);
  const int postop = s.cid2index(F_PAGATO);
  const int postoa = s.cid2index(F_IMPORTOANT);
  real tota, totp;
  FOR_EACH_SHEET_ROW_BACK(s, r, row)
	{
    if (editing && r == s.selected())
    {
      const TMask& rm = s.sheet_row_mask(r);
      totp += rm.get_real(F_PAGATO);
      tota += rm.get_real(F_IMPORTOANT);
   }
    else
    {
      totp += real(row->get(postop));
      tota += real(row->get(postoa));
    }
	}
  set(F_TOTALEPAG, totp);
  set(F_TOTALEANT, tota);
	enable(DLG_SAVEREC, !tota.is_zero() || !totp.is_zero()); 	
}

bool TPF_mask::fill_row(const TRiga_partite& rp, const TRiga_scadenze& rs, TToken_string& row, bool& partially_unassigned, bool force)
{
  int tipopag = 0;
  real impres;
  row.cut(0);
  bool ok = calc_residual(rs, impres, partially_unassigned, tipopag) || force;
  if (ok)
  {           
    row.add(""); // importo pagato e' solo da inserire
		row.add(rs.get(SCAD_IMPORTOANT)); // importo anticipato
    row.add(rs.get(SCAD_CODABIPR)); 
    row.add(rs.get(SCAD_CODCABPR)); 
		row.add(rp.get(PART_IMPTOTDOC));  // importo fattura
		row.add(impres.string());         // residuo
    row.add(rs.get(SCAD_ANNO)); 
    row.add(rs.get(SCAD_NUMPART));
    row.add(rs.get(SCAD_NRIGA));
    row.add(rs.get(SCAD_NRATA));
    row.add(rs.get(SCAD_DATASCAD));
    switch (tipopag)
    {              
    case 1:row.add(TR("Rimessa Diretta")); break;
    case 2:row.add(TR("Tratta")); break;
    case 3:row.add(TR("Ricevuta Bancaria")); break;
    case 4:row.add(TR("Cessione")); break;
    case 5:row.add(TR("Pagher�")); break;
    case 6:row.add(TR("Lettera di credito")); break;
    case 7:row.add(TR("Tratta accettata")); break;
    case 8:row.add(TR("Rapporti interbancari diretti")); break;
    case 9:row.add(TR("Bonifico")); break;
    default: break;
    }
  }
  return ok;
}

void TPF_mask::update_sheet()
{
	TSheet_field& sheet = sfield(F_SHEET);
  sheet.destroy();

  TRelation rel(LF_PARTITE);
  TRectype& filter = rel.curr(); 
  filter.put(PART_TIPOCF, 'C');
  filter.put(PART_SOTTOCONTO, get(F_CLIENTE));
  
  const char* filtro = "(CHIUSA!=\"X\")&&(TIPOMOV==1)";
  TCursor partite(&rel, filtro, 1, &filter, &filter); 

  const long items = partite.items();
  partite.freeze();
  if (items > 0)
  {
    TRectype& partita = partite.curr();
  
    TProgind pi(items, TR("Caricamento partite aperte"));
  
    long last_cf = 0;
    int last_year = 0, games = 0;
    TString16 last_game; 
    
    TToken_string row;
    
    for (partite = 0L; partite.pos() < items; ++partite)
    {
      if (!pi.addstatus(1))
        break;
      if (sheet.items() > 900)  // Anche troppe righe
        break;
      
      const long cur_cf = partita.get_long(PART_SOTTOCONTO);
      const int cur_year = partita.get_int(PART_ANNO);
      const TString& cur_game = partita.get(PART_NUMPART);
      if (cur_cf == last_cf && cur_year == last_year && cur_game == last_game)
        continue;
      last_cf   = cur_cf;
      last_year = cur_year;
      last_game = cur_game;
      games++;
  
      const TPartita game(partita);
      const int last = game.last();
      for (int riga = game.prima_fattura(); riga > 0 && riga <= last; riga = game.succ(riga))
      {
        const TRiga_partite& rp = game.riga(riga);
        if (rp.is_fattura())
        {
          for (int rata = 1; rata <= rp.rate(); rata++)
          {
            const TRiga_scadenze& rs = rp.rata(rata);
            if (!rs.chiusa())
            {
              bool partially_unassigned;
              if (fill_row(rp, rs, row, partially_unassigned, false))
              {
                const int numrow = sheet.items();
                sheet.row(numrow) = row;            
              }
            } // if rata aperta
          } // for ogni rata
        } // if e' una fattura
      } // for ogni fattura
    } // for ogni partita

    TString msg; msg << games << ' ' << TR("partite") << " : " << sheet.items() << ' ' << TR("scadenze aperte");
    xvtil_statbar_set(msg);
  } // if ci sono partite 
	sheet.force_update();
  update_total();
}

TPF_mask::TPF_mask() : TAutomask("pi0002100a")
{ 
}

TPF_mask::~TPF_mask() 
{
}

///////////////////////////////////////////////////////////
// Main Program
///////////////////////////////////////////////////////////

class TPresent_fatture : public TSkeleton_application
{
protected:
  virtual const char* extra_modules() const { return "sc"; }

public:
	virtual bool create();
  virtual void main_loop();
  virtual bool firm_change_enabled() const { return false; }
};

void TPresent_fatture::main_loop()
{
  open_files(LF_TABCOM, LF_TAB, LF_CLIFO, LF_PARTITE, LF_SCADENZE, LF_PAGSCA, LF_CAUSALI, LF_RCAUSALI, 0);
  TPF_mask m;
  while (m.run() != K_QUIT)
  {
    m.reset();
  }
} 

bool TPresent_fatture::create()
{
    //se non ha la sc non pu� proseguire
  if (!has_module(SCAUT))
    return error_box(TR("Modulo non autorizzato"));
  Tdninst dninst;
  if (!dninst.can_I_run(true))
    return error_box(TR("Programma non autorizzato!"));
  return TSkeleton_application::create();
}

int pi0002100(int argc, char* argv[])
{
  int n = 0;
  TPresent_fatture pe;
  pe.run(argc, argv, TR("Gestione anticipi fatture"));
  return 0;
}