#include <automask.h>
#include <defmask.h>
#include <modaut.h>
#include <progind.h>
#include <relation.h>
#include <sheet.h>
#include <tabutil.h>
#include <viswin.h>

#include "../ca/movana.h"
#include "../ca/rmovana.h"
#include "../ve/velib04.h"
#include "../cg/cgsaldac.h"

#include "pg0068100a.h"


///////////////////////////////////////////////
//  MASCHERA
///////////////////////////////////////////////
class TElabollazione_HK_mask : public TAutomask
{
  TArray_sheet*   _num_sheet;
  TString_array   _tipi_doc;

protected:
  virtual bool on_field_event(TOperable_field& o, TField_event e, long jolly);
  // Costruisce lo sheet dei codici numerazione
  void build_num_sheet();

public:
  // Controlla se lo stato ed il tipo del documento sono validi e rispettano la selezione
  bool doc_tipo_stato_ok(const TRectype& doc);
  // Restituisce lo sheet con le numerazioni da elaborare
  TArray_sheet& num_sheet() const { return *_num_sheet; }
  // Constructor and Distructor
  TElabollazione_HK_mask();
  ~TElabollazione_HK_mask();
};

TElabollazione_HK_mask::TElabollazione_HK_mask()  : TAutomask("pg0068100a")
{
  _num_sheet = new TArray_sheet(-1, -1, -4, -4, "Codici numerazione", "@1|Cod. numerazione|Descrizione@50");
}

TElabollazione_HK_mask::~TElabollazione_HK_mask()
{ 
  delete _num_sheet;
}

bool TElabollazione_HK_mask::on_field_event(TOperable_field& o, TField_event e, long jolly)
{
  switch (o.dlg())
  {
  case F_ELAB_COSTI:
  case F_ELAB_PROVV:
    if (e == fe_modify || e == fe_init)
    {
      const bool full = !o.empty();
      if (full)
        build_num_sheet();

      enable(DLG_USER, full);
      enable(DLG_OK, full && _num_sheet->one_checked());
    }
    break;
  case DLG_USER:
    if (e == fe_button && _num_sheet->run())
    {
      // Hai selezionato qualcosa ? allora abilita il pulsante di conferma 
      enable(DLG_OK, _num_sheet->one_checked());
    }
    break;
  default:
    break;
  }
  return true;
}

void TElabollazione_HK_mask::build_num_sheet() //(const short field)
{
  _num_sheet->destroy();
  _tipi_doc.destroy();
  
  const TContabilizzazione_analitica contanal(get(F_ELAB_COSTI));
  if (!contanal.empty())
  {
    TToken_string t;
    TString4 tipo;
    for (int i = 0; i < TElaborazione::_max_tipi_doc_elab; i++)
    {
      tipo = contanal.tipo_iniziale(i);
      if (tipo.full())
      { 
        t = tipo;
        t.add(contanal.stato_iniziale(i));   // Stato iniziale
        _tipi_doc.add(t);                     // Aggiunge questo tipo documento alla lista
      }
    }
    
    TString s1,s2,s3;
    TTable num("%NUM");
    for (num.first(); num.good(); num.next()) // scorre tutte le numerazioni possibili
    {
      TToken_string t,z;
      t.add(" ");
      t.add(num.get("CODTAB"));
      t.add(num.get("S0"));
      
      s2 = num.get("S2"); // reperisce i tipi documento validi per questa numerazione

      for (int x = 0; x <= s2.len(); x += 4)
        z.add(s2.mid(x,4));

      bool found = false;  
      for  (int i = _tipi_doc.last(); !found && i >= 0; i--)
        found |= s2.find(_tipi_doc.row(i).get(0)) >= 0;
      if (found)
        _num_sheet->add(t);
    }

    if (_num_sheet->items() == 1)
      _num_sheet->check(0);
  }
}

bool TElabollazione_HK_mask::doc_tipo_stato_ok(const TRectype& doc)
// Verifica che il tipo documento corrente esista tra i tipi previsti dalla elaborazione
// differita selezionata
{
  bool found = false;
  const TString4 tipo = doc.get(DOC_TIPODOC);
  const char stato = doc.get_char(DOC_STATO);
  const int items = _tipi_doc.items();
  for (int i = 0; i < items && !found; i++)
  {
    TToken_string& t = _tipi_doc.row(i);
    const TString4 tipox(t.get(0));
    const char statox = t.get(1)[0];
    if (tipo == tipox && stato == statox)
      found = true;
  }
  return found;
}



////////////////////////////////////////////////////////////////////////////////////////////////////
//  CLASSI DERIVATE DALLA TContabilizzazione_analitica (con le modifiche per il caso in questione)
////////////////////////////////////////////////////////////////////////////////////////////////////
class TContabilizzazione_analitica_costi : public TContabilizzazione_analitica
{
  TString4 _codcaus;
  bool _costi;
  TCausale * _caus;
  const TElabollazione_HK_mask* _msk;

public:
  virtual bool elabora(TDocumento& doc, long numreg_cg, TViswin* viswin, bool can_write, TAnal_mov& mov, 
               bool riclassifica_fdr_fde = true);

  virtual const TCausale& doc2caus(const TDocumento& doc);

  TContabilizzazione_analitica_costi(const TString& codcaus, const TElabollazione_HK_mask* msk, const char* cod = NULL, bool costi = true) 
                                     : TContabilizzazione_analitica(cod), _codcaus(codcaus), _msk(msk), _costi(costi), 
                                     _caus(NULL) {}
  TContabilizzazione_analitica_costi(const TString& codcaus, const TElabollazione_HK_mask* msk, const TRectype& rec, bool costi = true) 
                                     : TContabilizzazione_analitica(rec), _codcaus(codcaus), _msk(msk), _costi(costi), 
                                     _caus(NULL) {}
  virtual ~TContabilizzazione_analitica_costi() {}

};

const TCausale& TContabilizzazione_analitica_costi::doc2caus(const TDocumento& doc)
{
  if (_caus == NULL)
    _caus = new TCausale(_codcaus);

  return *_caus;
}

bool TContabilizzazione_analitica_costi::elabora(TDocumento& doc, long numreg_cg, TViswin* viswin, bool can_write, 
                                                 TAnal_mov& mov, bool riclassifica_fdr_fde)
{
  TDate datareg, datacomp, datadoc;
  int annoes = 0;
  TString descr, msg, codcaus;
  TCausale caus(_codcaus);

	datadoc = doc.get(DOC_DATADOC);
  datareg = datacomp = datadoc;
  annoes = esercizi().date2esc(datareg);
	doc.riferimento(descr);
	if (descr.empty())
		descr = doc.tipo().descrizione();

	const TString8 rif = doc.get(DOC_NUMDOCRIF);
	codcaus = caus.codice();
	const bool use_rif = caus.iva() == iva_acquisti && rif.not_empty();
	if (use_rif)
	{
		descr << TR(" n. ") << rif;
		descr << TR(" del ") << doc.get(DOC_DATADOCRIF);
	}
	else
	{
		descr << TR(" n. ") << doc.numero();
		descr << TR(" del ") << datadoc;
	}

  //..e quindi dovrebbe sempre bastare sezione = 'D'
  char sezione = 'D';
  //questo non dovrebbe mai capitare!...
  if (doc.is_nota_credito())
    sezione = 'A';

  const int decimals = TCurrency::get_firm_dec();

  //in caso di righe doc di costo -> sul doc usa numregca, in caso di righe provvigione usa un campo virtuale..
  //..NUMREGCAPR; questo � per poter avere i movimenti distinti (costi - provvigioni) e per poter collegare..
  //..movana e doc di origine nei 2 versi
  TString field_name(DOC_NUMREGCA);
  if (!_costi)
    field_name = "NUMREGCAPR";

  long numreg_ca = doc.get_long(field_name);
  if (numreg_ca > 0)
  {
    const int err = mov.read(numreg_ca);
    if (err == NOERR)
    {
      if (viswin != NULL)
      {
        msg.format("--- Il documento verr� ricontabilizzato nel movimento analitico %ld", numreg_ca);
        viswin->add_line(msg);
      }
      mov.body().destroy_rows();
    }
    else
      mov.put(MOVANA_NUMREG, numreg_ca = 0);
  }
  mov.put(MOVANA_DATAREG,  datareg);
  mov.put(MOVANA_DATACOMP, datacomp);
	mov.put(MOVANA_DATADOC,  datadoc);
  mov.put(MOVANA_ANNOES,   annoes);
  mov.put(MOVANA_DESCR,    descr);
  mov.put(MOVANA_NUMREGCG, numreg_cg);
	mov.put(MOVANA_CODCAUS,  caus.codice());
  mov.put(MOVANA_DPROVV,   doc.get(DOC_PROVV));      // Documento originale
  mov.put(MOVANA_DANNO,    doc.get(DOC_ANNO));
  mov.put(MOVANA_DCODNUM,  doc.get(DOC_CODNUM));
  mov.put(MOVANA_DNDOC,    doc.get(DOC_NDOC));


  TImporto totdoc; // Totale movimento analitico
	const TipoIVA tiva = caus.iva();
	TBill bill; caus.bill(RIGA_IVA_NON_DETRAIBILE, bill); 

 // Scandisco le righe del documento,
  const int righe_doc = doc.physical_rows();
  for (int r = 1; r <= righe_doc; r++)
  {
    const TRiga_documento& riga = doc[r];
		bool pareggio = false;

//  salto descrizioni, e omaggi
    if (riga.is_descrizione())
      continue;

    real valore;
    if (_costi)
    {
		  const real qta = riga.get(RDOC_QTA);
      const real costo = riga.articolo().get_real(ANAMAG_ULTCOS1);
      valore = qta * costo;
    }
    else
      valore = riga.provvigione();
    
    if (valore.is_zero())
      continue;

		if (tiva != iva_vendite && !riga.is_sconto())
		{
			const TString4 tipodet = riga.get(RDOC_TIPODET);
			int td;
			const real pind = indetraibile_al(tipodet, caus, datareg.year(), td);

			if (pind > ZERO)
			{
        const real imposta = riga.iva().imposta(valore);
				const real ivaind = (imposta * pind) / CENTO;
				if (bill.ok())
				{
					if (bill.is_analitico())
					{
						TString_array conti_ind;
            const char tipomov = mov.get_char(MOVANA_TIPOMOV);
						if (find_conti_iva_indetraibile(riga, bill, conti_ind, annoes, tipomov, pareggio)) //qui
						{
					    TGeneric_distrib esso(ivaind, decimals);

					    init_distrib(conti_ind, esso, pareggio);
							FOR_EACH_ARRAY_ROW(conti_ind, j, row_ind)
							{
								TRectype& rmov = mov.new_row();
								rmov.put(RMOVANA_ANNOES, annoes);
								rmov.put(RMOVANA_CODCONTO, row_ind->get(0));
								rmov.put(RMOVANA_CODCCOSTO,row_ind->get());
								rmov.put(RMOVANA_CODCMS,   row_ind->get());
								rmov.put(RMOVANA_CODFASE,  row_ind->get());
								rmov.put(RMOVANA_DESCR,    riga.get(RDOC_DESCR));

								const bool negative = pareggio && j > 0;
								const bool totale = pareggio && j == 0;
								TImporto imp(sezione, totale ? ivaind : real(esso.get()));

								if (negative)
									imp.swap_section();
								imp.normalize();
								rmov.put(RMOVANA_SEZIONE, imp.sezione());
								rmov.put(RMOVANA_IMPORTO, imp.valore());
								totdoc += imp;
							}
						}
					}
				}
				else
					valore += ivaind;
			}
		}              
  		
    if (valore.is_zero())
      continue;

    TString_array conti;
    const char tipomov = mov.get_char(MOVANA_TIPOMOV);
    const bool ok = find_conti(riga, conti, annoes, riclassifica_fdr_fde, tipomov, pareggio);

    if (!ok)
    {
      if (viswin != NULL)
      {
        TString msg; 
        msg.format(FR("*** Riga %d: Manca il conto analitico dell'articolo '%s'"), r, (const char*)riga.get(RDOC_CODART));
        viswin->add_line(msg);
      }
      //_error = conto_error;
      can_write = false;
      continue;
    }
    if (riga.is_omaggio())
    {
      const int gruppo = _msk->get_int(F_GRUPPO);
      const int conto = _msk->get_int(F_CONTO);
      const long sottoconto = _msk->get_int(F_SOTTOCONTO);
      TString conto_omaggio;
      conto_omaggio.format("%03d%03d%06ld", gruppo, conto, sottoconto);
      TToken_string& riga_conti = conti.row(0);
      riga_conti.add(conto_omaggio, 0);
    }

    TGeneric_distrib esso(valore, decimals);

    init_distrib(conti, esso, pareggio);
    FOR_EACH_ARRAY_ROW(conti, i, row)
    {
      TRectype& rmov = mov.new_row();
      rmov.put(RMOVANA_ANNOES,   annoes);
      rmov.put(RMOVANA_CODCONTO, row->get(0));
      rmov.put(RMOVANA_CODCCOSTO,row->get());
      rmov.put(RMOVANA_CODCMS,   row->get());
      rmov.put(RMOVANA_CODFASE,  row->get());
      rmov.put(RMOVANA_DESCR,    riga.get(RDOC_DESCR));

			const bool negative = pareggio && i > 0;
			const bool totale = pareggio && i == 0;
			TImporto imp(sezione, totale ? valore : real(esso.get()));

			if (negative)
				imp.swap_section();
      imp.normalize();
      rmov.put(RMOVANA_SEZIONE, imp.sezione());
      rmov.put(RMOVANA_IMPORTO, imp.valore());
      totdoc += imp;
    }
  }

  if (can_write && mov.rows() > 0)
  {
    totdoc.normalize();
    mov.put(MOVANA_SEZIONE, totdoc.sezione());
    mov.put(MOVANA_TOTDOC, totdoc.valore());

    TLocalisamfile movana(LF_MOVANA);
    if (numreg_ca > 0)
      mov.rewrite(movana);
    else
    {
      mov.write(movana);
      numreg_ca = mov.get_long(MOVANA_NUMREG);
      doc.put(field_name, numreg_ca);
    }
    if (viswin != NULL)
    {
      msg.format(FR("--- Movimento analitico $[r,w]%ld$[n,w] del %s"), numreg_ca, datacomp.string());
      viswin->add_line(msg);
    }
  }

  return can_write;
}


class TContabilizzazione_analitica_provvigioni : public TContabilizzazione_analitica_costi
{
public:
  TContabilizzazione_analitica_provvigioni(const TString& codcaus, const TElabollazione_HK_mask* msk, const char* cod = NULL) 
                                     : TContabilizzazione_analitica_costi(codcaus, msk, cod, false) {}
  TContabilizzazione_analitica_provvigioni(const TString& codcaus, const TElabollazione_HK_mask* msk, const TRectype& rec) 
                                     : TContabilizzazione_analitica_costi(codcaus, msk, rec, false) {}
  virtual ~TContabilizzazione_analitica_provvigioni() {}

};

//////////////////////////////////////////////
//  APPLICAZIONE
/////////////////////////////////////////////

// TElabollazione_HK
// Applicazione di contabilizzazione documenti
class TElabollazione_HK_app : public TSkeleton_application
{
  TElabollazione_HK_mask*  _msk;

  virtual bool check_autorization() const {return false;}
  virtual const char * extra_modules() const {return "ve";}

protected: // TApplication
  // Contabilizza i documenti
  void contabilize();

public:
  virtual bool create();
  virtual bool destroy();
  virtual void main_loop();
};


void TElabollazione_HK_app::contabilize()
{
  //Prende i dati dalla maschera (date,sheet numerazioni)
  const TDate data_ini = _msk->get_date(F_DATA_INI);
  int year_from = data_ini.year();
  const TDate data_fine = _msk->get_date(F_DATA_FIN);
  int year_to = data_fine.year();
  const TDate data_reg(TODAY);
  TArray_sheet& num_sheet = _msk->num_sheet();
  const long items = num_sheet.items();

  //Relazione su LF_DOC
  TRelation doc_rel(LF_DOC);
  TRectype da(LF_DOC);
  TRectype a(LF_DOC);
 
  TString16 codnum;
  TString msg, filt_expr;
  TToken_string nums;
  
  // Compone la lista dei documenti da elaborare
  for (long i = 0L; i < items; i++)  // Scorre per tutte le numerazioni dello sheet
  {
    if (num_sheet.checked(i)) // Costruisce una espressione sul codice numerazione: "CODNUM="x0" || CODNUM="x1" || ..."
    {
      codnum = num_sheet.row(i).get(1);
      filt_expr << "(CODNUM=\"";
      filt_expr << codnum << "\")||";
      nums.add(codnum);
    }
  }
  filt_expr.rtrim(2);  

  da.put(DOC_DATADOC, data_ini);
  da.put(DOC_PROVV, "D");
  da.put(DOC_ANNO, year_from);
  a.put(DOC_DATADOC, data_fine);
  a.put(DOC_PROVV, "D");
  a.put(DOC_ANNO, year_to);

  // Se ho una sola numerazione ottimizzo la setregion!
  if (nums.items() == 1)
  {
    da.put(DOC_CODNUM, nums);
    a.put(DOC_CODNUM, nums);
  }

  // Cursore complessivo con limiti di data (chiave 3). Viene sfruttata l'ottimizzazione
  // sulla costruzione dei cursori nel caso i campi presenti nell'espressione siano campi
  // chiave, nel nostro caso CODNUM soddisfa i requisiti. 
  TCursor doc_cur(&doc_rel, filt_expr, 3, &da, &a);
  const TRecnotype cur_items = doc_cur.items(); 
  if (cur_items > 0)
  {
    TLista_documenti lista_in, lista_out;
  
    doc_cur.freeze();
    msg = "Selezione documenti dal ";
    msg << data_ini.string() << " al ";
    msg << data_fine.string();
    TProgind p(cur_items, msg, false, true);
    const TRectype& cur_rec = doc_cur.curr();

    // Scorre tutti i documenti che rientrano nell'intervallo selezionato
    for (doc_cur = 0; doc_cur.pos() < cur_items; ++doc_cur)
    { 
      p.addstatus(1); 
      // controlla che il tipo documento e lo stato siano coerenti con la ELD selezionata
      if (nums.get_pos(cur_rec.get(DOC_CODNUM)) >= 0 && _msk->doc_tipo_stato_ok(cur_rec)) 
      {   
        TDocumento* doc = new TDocumento;
        if (doc->read(doc_cur.curr()) == NOERR) // legge il documento
          lista_in.add(doc); // Viene aggiunto alla lista dei documenti
        else
          delete doc;
      }
    }
    //E finalmente fa l'agognata elaborazione!!!!!
    TString16 elaborazione_costi = _msk->get(F_ELAB_COSTI);
    TContabilizzazione_analitica_costi contanal_costi(_msk->get(F_CAUS_COSTI), _msk, elaborazione_costi);
    contanal_costi.TContabilizzazione_analitica::elabora(lista_in, lista_out, data_reg);

    if (contanal_costi.processed_docs() > 0L)
      message_box(FR("Totale documenti di costo contabilizzati: %ld"), contanal_costi.processed_docs());

    TString16 elaborazione_provv = _msk->get(F_ELAB_PROVV);
    TContabilizzazione_analitica_provvigioni contanal_provv(_msk->get(F_CAUS_PROVV), _msk, elaborazione_provv);
    contanal_provv.TContabilizzazione_analitica::elabora(lista_in, lista_out, data_reg);

    if (contanal_provv.processed_docs() > 0L)
      message_box(FR("Totale documenti di provvigione contabilizzati: %ld"), contanal_provv.processed_docs());
  }
  else
    warning_box(TR("Non vi sono documenti da contabilizzare per le numerazioni selezionate."));

  xvtil_statbar_set("");  
}


bool TElabollazione_HK_app::create()
{
    //se non ha le vendite e l'analitica � impossibile da utilizzare
  if (!has_module(VEAUT) || !has_module(CAAUT))
    return error_box(TR("Modulo non autorizzato"));

  //open_files(LF_TABCOM, LF_TAB, LF_DOC, LF_RIGHEDOC, LF_MOVANA, LF_RMOVANA, 0);
  _msk = new TElabollazione_HK_mask();
  return TSkeleton_application::create();
}

bool TElabollazione_HK_app::destroy()
{
  if (_msk) delete _msk;
  return TSkeleton_application::destroy();
}

void TElabollazione_HK_app::main_loop()
{
  while (_msk->run() == K_ENTER)
    contabilize();
}

int pg0068100 (int argc, char **argv)
{
  TElabollazione_HK_app a;
  a.run(argc,argv, TR("Contabilizzazione Hair Kulture"));
  return true;
}