#include <applicat.h>
#include <automask.h>
#include <progind.h>
#include <reputils.h>
#include <relation.h>
#include <textset.h>
#include <utility.h>

#include "../ca/calib01.h"
#include "../ca/movana.h"
#include "../ca/rmovana.h"
#include "../cg/cglib01.h"

#include "ps1001.h"
#include "ps1001100a.h"

///////////////////////////////////////////////////////////
// TAutomask
///////////////////////////////////////////////////////////

class TImporta_movana_mask : public TAutomask
{
protected:
  virtual bool on_field_event(TOperable_field& o, TField_event e, long jolly);

public:
  TImporta_movana_mask();
};
  
TImporta_movana_mask::TImporta_movana_mask() :TAutomask ("ps1001100a")
{
}  

bool TImporta_movana_mask::on_field_event(TOperable_field& o, TField_event e, long jolly)
{ 
	switch (o.dlg())
	{
		//giochetto per avere la lista dei files validi nella directory di trasferimento!
		case F_NAME:
			if (e == fe_button)
			{
				TArray_sheet as(-1, -1, 72, 20, TR("Selezione file"), "File@32");
				TFilename path = get(F_PATH);
				path.add("*.csv");	//files delle testate
				list_files(path, as.rows_array());
				TFilename name;
				FOR_EACH_ARRAY_ROW(as.rows_array(), i, row)
				{
					name = *row;
					*row = name.name();
				}
				if (as.run() == K_ENTER)
				{
					o.set(as.row(as.selected()));
				}
			}
			break;
//controlli sui campi della testata documento
    case F_DATAREG:
      if ((e == fe_modify || e == fe_close) && !query_mode())
      {
        const TDate datareg = o.get();
        const TEsercizi_contabili ec;
        if (ec.date2esc(datareg) <= 0)
          return error_box(((TEdit_field&)o).get_warning());
        if (e == fe_close && field(F_DATACOMP).empty())
          set(F_DATACOMP, datareg);
      }
      break;
    case F_DATACOMP:
      if ((e == fe_modify || e == fe_close) && !query_mode())
      {
        const TDate datareg = get(F_DATAREG);
        TDate datacomp = o.get();
        if (!datacomp.ok())
          datacomp = datareg;

        const bool preventivo = get(F_TIPO).full();
        //i movimenti normali devono avere data competenza nel presente o passato!!
        if (!preventivo && datacomp > datareg)
          return error_box(TR("La data di competenza non puo' superare la data di registrazione"));

        const TEsercizi_contabili ec;
        int ae = ec.date2esc(datacomp);

        //movimenti preventivi in esercizi futuri
        if (ae <= 0 && preventivo && datacomp > datareg)
          ae = ec.date2esc(datareg) + datacomp.year() - datareg.year();

        if (ae > 0)
          set(F_ANNOES, ae, 0x1);
        else
          return error_box(((TEdit_field&)o).get_warning());

        const int ar = ec.date2esc(datareg);
        const int ap = ec.pred(ar);
        
        //preventivi un anno indietro, nel presente o nel futuro
        if (preventivo)
        {
          if (ae < ap)
            return error_box(FR("La data di competenza non pu� precedere l'esercizio %d"), ap);
        }
        else  //normali solo un anno indietro o nel presente
        {
          if (ae != ar && ae != ap)
            return error_box(FR("La data di competenza deve appartenere all'esercizio in corso o al precedente"));
        }

      }
      break;
    case F_DATAFCOMP:
      if (e == fe_modify || e == fe_close)
      {
        const TDate datacomp = get(F_DATACOMP);
        const TDate datafcomp = o.get();
        if (datafcomp.ok())
        {
          if (datafcomp < datacomp)
            return error_box(((TEdit_field&)o).get_warning());
        }
        else
          o.set(datacomp.string()); //se la data fine competenza viene lasciata vuota -> e' uguale alla datacomp
      }
      
      break;
		default:
			break;
	}
  return true;
}
 
/////////////////////////////////////////////////////////////
//	Recordset specifici per i dati da trasferire
/////////////////////////////////////////////////////////////
class TImporta_movana_recordset : public TCSV_recordset
{
  char _sep_field;

  protected:
    virtual TRecnotype new_rec(const char* buf = NULL);    
  
  public:
    TImporta_movana_recordset(const char * fileName, char sep);
};

TRecnotype TImporta_movana_recordset::new_rec(const char* buf)
{
  TToken_string str(256,'\t'); //nuovo record tab separator

  if(buf && *buf)
  {
    bool apici = false;

    for (const char* c = buf; *c ; c++)
    {
      if (*c == '"')
      {
        apici = !apici;
      }
      else
      {
        if (*c == _sep_field)  //tipo di separatore dei campi che si trova nel record di origine
        {
          if (!apici)
            str << str.separator();
          else
            str << *c;
        }
        else
          str << *c;
      }
    }
  }

  const TRecnotype n = TText_recordset::new_rec(str);

  if (n >= 0)
    row(n).separator(str.separator());
  
  return n;
}


TImporta_movana_recordset::TImporta_movana_recordset(const char * filename, char sep_field)
                        : TCSV_recordset("CSV(;)"), _sep_field(sep_field)  //separatore campi
{
  load_file(filename);
}

///////////////////////////////////////
// TSkeleton_application
///////////////////////////////////////
class TImporta_movana : public TSkeleton_application
{
	virtual bool check_autorization() const { return false; }
  virtual const char * extra_modules() const { return "ca"; }

	TImporta_movana_mask* _msk;

protected:

public:           
  virtual bool create();
  virtual bool destroy();
  virtual void main_loop();
	bool transfer(const TFilename& file);
 
  TImporta_movana() {};
};

bool TImporta_movana::transfer(const TFilename& file)
{
  //roba dalla maschera
  //parametri di importazione
  const char sep_field = _msk->get(F_SEP_FIELD)[0]; //separatore dei campi
  const char sep_dec = _msk->get(F_SEP_DEC)[0]; //separatore decimali
  //parametri del movana
  const long numreg = _msk->get_long(F_NUMREGCA);
  const int annoes = _msk->get_int(F_ANNOES);
  const TDate datareg = _msk->get_date(F_DATAREG);
  const TDate datacomp = _msk->get_date(F_DATACOMP);
  const TDate datafcomp = _msk->get_date(F_DATAFCOMP);
  const bool autofcomp = _msk->get_bool(F_AUTOFCOMP);
  const TString descrizione = _msk->get(F_DESCR);
  const char tipomov = _msk->get(F_TIPO)[0];
  const TString4 codcaus = _msk->get(F_CODCAUS);
  const real ori_totimp = _msk->get_real(F_TOTMOV);
  const char ori_totsez = _msk->get(F_TOTMOV_SEZ)[0];


  //crea il recordset sul file di input utilizzando il separatore
  TImporta_movana_recordset s(file, sep_field);
  const long recset_items = s.items();

  //un po' di roba iniziale
  TProgind pi(s.items(),"Importazione movimento in corso ...",true,true);
  TLog_report log("ERRORI DI TRASFERIMENTO");
  int curr_line = 0;

  //testata
  //-------
  //movimento analitico che sar� generato
  TAnal_mov movana;
  //il movana esisteva gi� o � nuovo?
  const int err = movana.read(numreg);
  //se esisteva gi� deve accoppare tutte le righe visto che le sostituir�
  if (err == NOERR)
    movana.destroy_rows(LF_RMOVANA);
  else
    movana.put(MOVANA_NUMREG, numreg);

  movana.put(MOVANA_DATAREG, datareg);
  movana.put(MOVANA_DATACOMP, datacomp);
  movana.put(MOVANA_DATAFCOMP, datafcomp);
  movana.put(MOVANA_ANNOES, annoes);
  movana.put(MOVANA_DESCR, descrizione);
  movana.put(MOVANA_TIPOMOV, tipomov);
  movana.put(MOVANA_CODCAUS, codcaus);
  movana.put(MOVANA_AUTOFCOMP, autofcomp);

  TImporto totale;
  //giro su tutti i record del recordset per importarli; ogni record � una riga
  //TRACCIATO RECORD all'ultima moda (21/09/2010): codcms/cdc/importo/sezione/gr/co/sott/fase (valido per crpa e dinamica)
  //righe analitiche
  //----------------
  for (bool ok = s.move_first(); ok; ok = s.move_next())
  {
    if (!pi.addstatus(1)) 
      break;
    curr_line ++;

    //prende i dati dal record di input del file csv
    //----------------------------------------------
    //CODCMS ci deve essere e va maiuscolizzato; se non c'� va segnalato sul log..
    //..ed impedir� la write del movana
    TString80 codcms = s.get(0).as_string();
    if (codcms.blank())
    {
      //controllo necessario e decisivo per evitare l'errore dovuto all'ultimo 'a capo' che pretendeva una riga ulteriore
      if (curr_line < recset_items)
      {
        TString msg;
        msg.format("Manca la commessa nella riga %ld", curr_line);
        log.log(2, msg);
      }
      continue;
    }
    codcms.upper();
    //altra modifica all'ultima moda: se c'� un '_' in realt� vorrebbe un '/' (21/09/2010)
    codcms.replace('_', '/');

    //CDC ci deve essere e va maiuscolizzato; se non c'� va segnalato sul log..
    //..ed impedir� la write del movana
    TString80 cdc = s.get(1).as_string();
    if (cdc.blank())
    {
      TString msg;
      msg.format("Manca la sede nella riga %ld", curr_line);
      log.log(2, msg);
      continue;
    }
    cdc.upper();

    //IMPORTO (inizialmente come stringa x poter fare le replace); se nullo va segnalato..
    //..ma niente bloccaggio della write
    TString80 str_importo = s.get(2).as_string();
    if (sep_dec == ',')
    {
      str_importo.strip(".");  //togle il separatore delle migliaia
      str_importo.replace(',','.'); //sostituisce il separatore decimale
    }
    else
      str_importo.strip(",");

    const real importo = str_importo;
    /*if (importo == ZERO)
    {
      TString msg;
      msg.format("Importo nullo nella riga %ld", curr_line);
      log.log(0, msg);
    }*/

    //SEZIONE
    TString8 str_sezione = s.get(3).as_string();
    if (str_sezione.blank())
    {
      TString msg;
      msg.format("Manca la sezione nella riga %ld", curr_line);
      log.log(2, msg);
      continue;
    }
    str_sezione.upper();
    const char sezione = str_sezione[0];

    //CONTO
    const int gr = s.get(4).as_int();
    const int co = s.get(5).as_int();
    const long so = s.get(6).as_int();
    TString80 conto;
    conto.format("%03d%03d%06ld", gr, co, so);
    if (conto.blank())
    {
      TString msg;
      msg.format("Manca il conto nella riga %ld", curr_line);
      log.log(2, msg);
      continue;
    }

    //FASE ci pu� essere e va maiuscolizzata;
    TString80 fase = s.get(7).as_string();
    /*if (fase.blank())
    {
      TString msg;
      msg.format("Manca la fase nella riga %ld", curr_line);
      log.log(2, msg);
      continue;
    }*/
    fase.upper();

    //riempie la riga analitica
    //-------------------------
    //nuova riga del movana (sfrutta il fatto che � un multiple rectype)
    TRectype& rmovana = movana.new_row();

    rmovana.put(RMOVANA_DESCR, "Riga importata");
    rmovana.put(RMOVANA_CODCONTO, conto);
    rmovana.put(RMOVANA_CODCMS, codcms);
    rmovana.put(RMOVANA_CODCCOSTO, cdc);
    rmovana.put(RMOVANA_CODFASE, fase);
    rmovana.put(RMOVANA_SEZIONE, sezione);
    rmovana.put(RMOVANA_IMPORTO, importo);
    //aggiorna anche il totale documento..
    TImporto importo_riga(sezione, importo);
    totale += importo_riga;
  }

  //completa la testata
  //-------------------
  totale.normalize();
  const char totsez = totale.sezione();
  const real totimp = totale.valore();

  //controllo sul totmov in caso di riscrittura
  if (ori_totimp != totimp || (ori_totimp == ZERO && ori_totsez != totsez))
  {
    TString str_totimp;
    str_totimp << totimp;
    TString str_ori_totimp;
    str_ori_totimp << ori_totimp;
    TString msg;
    msg.format("Il totale movimento originale e' %s %c, quello importato e' %s %c.\n"
               "Registrare il movimento con il totale delle righe importate?", 
               (const char*)str_totimp, totsez, (const char*)str_ori_totimp, ori_totsez);
    if (!yesno_box(msg))
      log.log(2, "Totale movimento originale NON coincidente con quello importato !");
  }
  movana.put(MOVANA_SEZIONE, totale.sezione());
  movana.put(MOVANA_TOTDOC, totale.valore());

  //solo se non ci sono errori procede alla registrazione del movimento
	const int items = log.recordset()->items();
  if (items > 0)
    log.preview();
  else
  {
    TLocalisamfile fmovana(LF_MOVANA);
    int err = movana.rewrite_write(fmovana);
    if (err != NOERR)
      error_box("Impossibile registrare il movimento analitico !");
  }
  return true;
}

bool TImporta_movana::create()
{
  _msk = new TImporta_movana_mask();
  return TSkeleton_application::create();
}

bool TImporta_movana::destroy()
{
	delete _msk;
  return TApplication::destroy();
}

void TImporta_movana::main_loop()
{
  KEY	tasto;
	tasto = _msk->run();
  TConfig& cfg = ca_config();
  const bool use_pdcc =  cfg.get_bool("UsePdcc");
  if (use_pdcc)
  {
    if (tasto == K_ENTER)
    {
      //genero il nome del file da caricare
      TFilename name = _msk->get(F_PATH);
      name.add(_msk->get(F_NAME));
		  if (transfer(name))
		  {
        message_box(TR("Elaborazione completata"));
		  }
    }
  }
  else
    error_box("Il programma richiede che sia impostato l'uso del piano dei conti contabile in analitica!");
}


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


int ps1001100 (int argc, char* argv[])
{
  TImporta_movana main_app;
  main_app.run(argc, argv, TR("Importazione movimento analitico da CSV"));
  return true;
}