#include <applicat.h>
#include <automask.h>
#include <progind.h>
#include <recarray.h>

#include "baeur.h"
#include "baeur20.h"
#include "../cg/cglib01.h"

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

///////////////////////////////////////////////////////////
// Main app
///////////////////////////////////////////////////////////

class TEuro02_app : public TEuro_app
{ 
public:
  virtual void main_loop();
};

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

///////////////////////////////////////////////////////////
// Gestione movimenti di apertura e chiusura
///////////////////////////////////////////////////////////

bool TCG_mov::add(int g, int c, long s, char sez, const real& imp)
{ 
  bool ok = s > 0;         
  if (ok)
  {
    TCG_rmov* rmov = new TCG_rmov;
    rmov->_gruppo = g;
    rmov->_conto = c;
    rmov->_sottoconto = s;
    rmov->_importo.set(sez, imp);
    TArray::add(rmov);
  }       
#ifdef DBG
  else
    error_box("Conto non valido: %d %d %ld", g, c, s);
#endif  
  return ok;
}

bool TCG_mov::add(const TBill& bill, const TImporto& imp)
{
  int g = bill.gruppo();
  int c = bill.conto();
  long s = bill.sottoconto();
  char sez = imp.sezione();
  real val = imp.valore();
  return add(g, c, s, sez, val);
}

void TCG_mov::crea_testata(TLocalisamfile& mov, const TString& caus, 
                           const TDate& datareg, const TString& desc) const
{
  long numreg = 1;
  if (mov.last() == NOERR)
    numreg = mov.get_long(MOV_NUMREG)+1;
  mov.zero();
  
  mov.put(MOV_NUMREG, numreg);   
  mov.put(MOV_CODCAUS, caus);
  mov.put(MOV_DATAREG, datareg);
  mov.put(MOV_DATACOMP, datareg);
  mov.put(MOV_DESCR, desc);
  mov.put(MOV_ANNOIVA, datareg.year());
  
  TEsercizi_contabili esc;
  const int annoes = esc.date2esc(datareg);
  mov.put(MOV_ANNOES, annoes);
}

void TCG_mov::crea_riga(const TRectype& mov, TRectype& rmov, int r) const
{
  rmov.zero();
  rmov.put(RMV_NUMREG, mov.get(MOV_NUMREG));
  rmov.put(RMV_NUMRIG, r);
  rmov.put(RMV_DATAREG, mov.get(MOV_DATAREG));
  rmov.put(RMV_ANNOES, mov.get(MOV_ANNOES));
}

void TCG_mov::update_saldo(const TRectype& mov, const TRectype& rmov, TLocalisamfile& saldi)
{ 
  TBill zio; zio.get(rmov);
  TRectype& curr = saldi.curr();
  curr.zero();
  curr.put(SLD_ANNOES, mov.get(MOV_ANNOES));
  curr.put(SLD_GRUPPO, zio.gruppo());
  curr.put(SLD_CONTO, zio.conto());
  curr.put(SLD_SOTTOCONTO, zio.sottoconto());
  curr.put(SLD_FLSCA, "");
  const bool found = saldi.read() == NOERR;
  if (!found)
  {
    curr.zero();
    curr.put(SLD_ANNOES, mov.get(MOV_ANNOES));
    curr.put(SLD_GRUPPO, zio.gruppo());
    curr.put(SLD_CONTO, zio.conto());
    curr.put(SLD_SOTTOCONTO, zio.sottoconto());
    curr.put(SLD_FLSCA, "");
  }
  
  const TDate sld_datareg = curr.get(SLD_DATAULMOV);
  const TDate mov_datareg = mov.get(MOV_DATAREG);
  if (mov_datareg >= sld_datareg)
  {
    curr.put(SLD_NUMULTMOV, mov.get(MOV_NUMREG));
    curr.put(SLD_DATAULMOV, mov_datareg);
  }
                               
  const TImporto importo(rmov.get_char(RMV_SEZIONE), rmov.get_real(RMV_IMPORTO));
  const TString4 caus = mov.get(MOV_CODCAUS);
  const char movap = cache().get(LF_CAUSALI, caus, CAU_MOVAP)[0];
  switch (movap)
  {
  case 'A':
    {
      TImporto saldo(curr.get_char(SLD_FLAGSALINI), curr.get_real(SLD_SALDO));
      saldo += importo;
      saldo.normalize();
      curr.put(SLD_FLAGSALINI, saldo.sezione());
      curr.put(SLD_SALDO, saldo.valore());
    }
    break;
  case 'C':
    {
      TImporto saldo(curr.get_char(SLD_FLAGSALFIN), curr.get_real(SLD_SALDOFIN));
      saldo += importo;
      saldo.normalize();
      curr.put(SLD_FLAGSALFIN, saldo.sezione());
      curr.put(SLD_SALDOFIN, saldo.valore());
    }
    break;
  default:
    {
      const char* field = importo.sezione() == 'D' ? SLD_PDARE : SLD_PAVERE;
      real saldo = curr.get(field);
      saldo += importo.valore();
      curr.put(field, saldo);
    }
    break;
  }
  
  if (found)
    saldi.rewrite();
  else
    saldi.write();  
}

// Salva un movimento di primanota, eventualmente spezzandolo su piu' records se necessario
TImporto TCG_mov::save(const TDate& datareg, const TString& caus, const TString& desc, 
                       const TBill& contro, bool adeuro, bool convert, bool invert)
{ 
  TProgind pi(items(), desc, FALSE, TRUE);
  
  // Apro comunque i file in lire per avere i tracciati
  TLocalisamfile lmov(LF_MOV);
  TLocalisamfile lrmov(LF_RMOV);
  TLocalisamfile lsaldi(LF_SALDI);
  TLocalisamfile *pmov, *prmov, *psaldi;
  
  // Apro quelli in euro se necessario
  if (adeuro)
  {
    pmov = new TEuroisamfile(LF_MOV, adeuro);
    prmov = new TEuroisamfile(LF_RMOV, adeuro);
    psaldi = new TEuroisamfile(LF_SALDI, adeuro);
  }
  else
  {
    pmov = &lmov;
    prmov = &lrmov;
    psaldi = &lsaldi;
  }
  TLocalisamfile& mov = *pmov;
  TLocalisamfile& rmov = *prmov;
  TLocalisamfile& saldi = *psaldi;

  TImporto bilancio;
  for (int i = 0; i < items(); i += MAX_CG_ROWS)
  {
    const int first_row = i;
    int last_row = i + MAX_CG_ROWS;
    if (last_row > items())
      last_row = items();
      
    crea_testata(mov, caus, datareg, desc);
    TImporto totale;
    int written = 0;
    for (int r = i; r < last_row; r++)
    {        
      pi.addstatus(1);
      const TCG_rmov& riga = row(r);
      TImporto imp = riga._importo;
      if (convert)
      {        
        real euro = imp.valore() / EURO;
        euro.round(2);
        imp.set(imp.sezione(), euro);
      }
      if (imp.is_zero())
        continue;
      
      if (invert)
        imp.swap_section();
      imp.normalize();
      totale += imp;  
      
      crea_riga(mov.curr(), rmov.curr(), ++written);
      rmov.put(RMV_SEZIONE, imp.sezione());
      rmov.put(RMV_IMPORTO, imp.valore());
      
      TBill conto(riga._gruppo, riga._conto, riga._sottoconto);
      if (!conto.find())
        error_box("Nella riga %d del movimento %ld sarebbe utile anche il conto %d %d %ld", 
                  written, mov.get_long(MOV_NUMREG),  riga._gruppo, riga._conto, riga._sottoconto);
      conto.put(rmov.curr(), FALSE);
      contro.put(rmov.curr(), TRUE);
      if (rmov.write() == NOERR)
        update_saldo(mov.curr(), rmov.curr(), saldi);
    }

    // Scrittura ultima riga di contropartita
    if (written > 0)
    {
      crea_riga(mov.curr(), rmov.curr(), ++written);
      totale.normalize();
      rmov.put(RMV_SEZIONE, totale.sezione() == 'D' ? 'A' : 'D');
      rmov.put(RMV_IMPORTO, totale.valore());
      contro.put(rmov.curr(), FALSE);
      
      const TCG_rmov& riga = row(0);
      const TBill conto(riga._gruppo, riga._conto, riga._sottoconto);
      conto.put(rmov.curr(), TRUE);
      
      if (rmov.write() == NOERR)
        update_saldo(mov.curr(), rmov.curr(), saldi);
      
      // Scrittura testata
      mov.put(MOV_TOTDOC, totale.valore());
      mov.write();
    }

    bilancio += totale;  
  }
  
  if (adeuro)
  {
    delete pmov;
    delete prmov;
    delete psaldi;
  }
  
  return bilancio;
}

TImporto TCG_mov::calc_bil(bool convert, bool invert)
{   
  TImporto bilancio;
  for (int i = items()-1; i >= 0; i--)
  {
    const TCG_rmov& riga = row(i);
    TImporto imp = riga._importo;
    if (convert)
    {        
      real euro = imp.valore() / EURO;
      euro.round(2);
      imp.set(imp.sezione(), euro);
    }
    if (imp.is_zero())
      continue;      
    if (invert)
      imp.swap_section();
    bilancio += imp;  
  }  
  bilancio.normalize();
  return bilancio;
}


int TCG_movs::indbil(int g, int c) const
{
  TString16 key;
  key.format("%d|%d", g, c);
  const TRectype& rec = cache().get(LF_PCON, key);
  int ib = rec.get_int(PCN_INDBIL);
  if (ib <= 0) 
  {
    error_box("Manca l'indicatore di bilancio per il conto %d %d", g, c);
    ib = 1;
  }
  return ib;  
}

TCG_mov& TCG_movs::mov(int ib)
{
  TCG_mov* m = (TCG_mov*)objptr(ib);
  if (m == NULL)
  {
    m = new TCG_mov;
    TArray::add(m, ib);
  }
  return *m;
}

int TCG_movs::add(int g, int c, long s, char sez, const real& imp)
{
  const int ib = indbil(g, c);    // Determina l'inidicatore di bilancio
  mov(ib).add(g, c, s, sez, imp); // Somma il saldo al movimento opportuno
  return ib;
}

TImporto TCG_movs::save(const TDate& datareg, const TString& caus, const TString& desc, 
                        const TBill& contro, bool adeuro, bool convert, bool invert)
{ 
  TImporto bilancio;
  const int lib = last();
  for (int ib = 1; ib <= lib; ib++) if (objptr(ib)) 
    bilancio += mov(ib).save(datareg, caus, desc, contro, adeuro, convert, invert);
  return bilancio;  
}

///////////////////////////////////////////////////////////
// Main mask
///////////////////////////////////////////////////////////

class TEuro20_mask : public TAutomask
{ 
public:
  virtual bool on_field_event(TOperable_field& o, TField_event e, long jolly);
  virtual void on_firm_change();

  TEuro20_mask() : TAutomask("baeur20") { }
};

bool TEuro20_mask::on_field_event(TOperable_field& o, TField_event e, long jolly)
{
  return TRUE;
}

void TEuro20_mask::on_firm_change()
{
  const long firm = app().get_firm();
  TDate adozione(1,1,2002);
  
  TFilename dati, datie;
  app().get_aree_dati(dati, datie);
  
  TString8 ditta; 
  ditta.format("%05ldA", firm);
  
  TFilename inie = datie;
  inie.add(ditta);
  inie.add("prassid.ini");
  
  bool adotta = FALSE, inizio = FALSE;
  if (inie.exist())
  { 
    set(F20_DATI, dati);
    set(F20_DATIE, datie);
    
    adotta = app().data_adozione_euro(firm, adozione, inizio);
    set(F20_ADOZIONE, adozione);
  
    if (!adotta || inizio)
    {
      disable(DLG_OK);
      error_box("Non � possibile utilizzare questa procedura:\n"
                "la ditta %ld adotta l'Euro da inizio esercizio.", firm);
    }
    else
      enable(DLG_OK);
  }
  else
  {
    disable(DLG_OK);
    error_box("Non � possibile utilizzare questa procedura:\n"
              "la ditta %ld non esiste nello studio in Euro.", firm);
  }

  TFilename ini = dati;
  ini.add(ditta);
  ini.add("prassid.ini");
  if (ini.exist())
  {
    TConfig prassid(ini, "cg");
    set(F20_CAUS_C, prassid.get("CoCaCh"), TRUE);
    set(F20_CAUS_A, prassid.get("CoCaAp"), TRUE);

    set(F20_CONTROG_C, prassid.get("CsBiChG"));
    set(F20_CONTROC_C, prassid.get("CsBiChC"));
    set(F20_CONTROS_C, prassid.get("CsBiChS"), TRUE);
    
    set(F20_CONTROG_A, prassid.get("CsBiApG"));
    set(F20_CONTROC_A, prassid.get("CsBiApC"));
    set(F20_CONTROS_A, prassid.get("CsBiApS"), TRUE);
  }
  set(F20_DESC_C, "Chiusura conti");
  set(F20_DESC_A, "Apertura conti");
  
  TBill arrotino;
  if (app().load_round_bill(arrotino))
  {
    arrotino.set(*this, F20_DIFF_G, F20_DIFF_C, F20_DIFF_S);
    field(F20_DIFF_S).check(STARTING_CHECK);
  }
}

///////////////////////////////////////////////////////////
// Main 
///////////////////////////////////////////////////////////

struct TSaldi_data : public TObject
{            
  int _anno;
  TCG_movs _movs;
};

static bool saldi_handler(TRectype& rec, void* jolly)
{                 
  TSaldi_data* sd = (TSaldi_data*)jolly;
  
  const int anno = rec.get_int(SLD_ANNOES);
  if (anno != sd->_anno)
    return FALSE;
  
  const int gruppo = rec.get_int(SLD_GRUPPO);
  const int conto = rec.get_int(SLD_CONTO);
  const long sottoconto = rec.get_long(SLD_SOTTOCONTO);
  if (gruppo <= 0 || conto <= 0 || sottoconto <= 0)  
    return error_box("Il file dei saldi contiene il conto errato %d %d %ld", gruppo, conto, sottoconto);

  TImporto saldo, pdare, pavere;
  saldo.set(rec.get_char(SLD_FLAGSALINI), rec.get_real(SLD_SALDO));
  pdare.set('D', rec.get_real(SLD_PDARE));
  pavere.set('A', rec.get_real(SLD_PAVERE));
  saldo += pdare;
  saldo += pavere;
  saldo.normalize();
  if (!saldo.is_zero())
  {
    sd->_movs.add(gruppo, conto, sottoconto, saldo.sezione(), saldo.valore());
  }
  return FALSE;
}

void TEuro02_app::main_loop()
{                
  const long f = argc() > 2 ? atol(argv(2)) : -1;
  goto_lire(f);
  if (f <= 0L)
    set_firm();
    
  TEuro20_mask msk;
  msk.on_firm_change(); // Preimposta data adozione euro ed altro

  if (msk.run() == K_ENTER)
  { 
    TSaldi_data sd;

    const TDate adozione = msk.get(F20_ADOZIONE);
    TEsercizi_contabili esc;
    sd._anno = esc.date2esc(adozione);

    convert_file(LF_SALDI, NULL, NULL, NULL, saldi_handler, &sd);
    
    TBill contro; 
    contro.get(msk, F20_CONTROG_C, F20_CONTROC_C, F20_CONTROS_C);
    sd._movs.save(adozione, msk.get(F20_CAUS_C), msk.get(F20_DESC_C), contro, FALSE, FALSE, TRUE);
    
    contro.get(msk, F20_CONTROG_A, F20_CONTROC_A, F20_CONTROS_A);
    TImporto bil_eur = sd._movs.save(adozione, msk.get(F20_CAUS_A), msk.get(F20_DESC_A), contro, TRUE, TRUE, FALSE);
    
    TBill arrotino;
    arrotino.get(msk, F20_DIFF_G, F20_DIFF_C, F20_DIFF_S);
    save_round_bill(arrotino);

    if (!bil_eur.is_zero())
    {          
      TCG_mov mov;             
      bil_eur.swap_section();
      mov.add(arrotino, bil_eur);
      mov.save(adozione, msk.get(F20_CAUS_A), msk.get(F20_DESC_A), contro, TRUE, FALSE, FALSE);
    }
  }
}

int baeur02(int argc, char* argv[])
{  
  TEuro02_app ma;
  ma.run(argc, argv, "Conversione saldi infrannuale");
  
  return 0;
}