#include <applicat.h>
#include <automask.h>
#include <clifo.h>
#include <config.h>
#include <execp.h>
#include <filetext.h>
#include <progind.h>
#include <reprint.h>
#include <reputils.h>
#include <sheet.h>
#include <tabutil.h>
#include <utility.h>

#include "pg0388200a.h"

#include "../mg/mglib.h"
#include "../ve/velib.h"
#include "../sv/svlib09.h"
#include "../mg/mag.h"

//---------------------------------
//		TAutomask
//---------------------------------
class TImpBol0388_mask : public TAutomask
{
protected:
  bool on_field_event(TOperable_field& o, TField_event e, long jolly);
	bool select_file_ext(const char* ext, TFilename& filename);
public:
  TImpBol0388_mask();
  virtual ~TImpBol0388_mask(){};
};
  
TImpBol0388_mask::TImpBol0388_mask() :TAutomask ("pg0388200a")
{
}  

bool TImpBol0388_mask::select_file_ext(const char* ext, TFilename& filename)
{
	TArray_sheet as(-1, -1, 72, 20, TR("Selezione file"), 
                   "File@48");
	TFilename path = get(F_PATH);
	path.add("*");
	path.ext(ext);
	list_files(path, as.rows_array());
	TFilename name;
	FOR_EACH_ARRAY_ROW(as.rows_array(), i, row)
	{
		name = *row;
		*row = name.name();
	}
	bool ok = as.run() == K_ENTER;
	if (ok)
	{
		filename = as.row(as.selected());
	}
	return ok;
}

bool TImpBol0388_mask::on_field_event(TOperable_field& f, TField_event e, long jolly)
{ 
	switch (f.dlg())
	{
		//giochetto per avere la lista dei files validi nella directory di trasferimento!
		case F_NAMEBOL:
			if (e == fe_button)
			{
				TFilename name;
				if (select_file_ext("txt", name))
					f.set(name);
			}
			break;
		case F_NAMEINV:
			if (e == fe_button)
			{
				TFilename name;
				if (select_file_ext("csv", name))
					f.set(name);
			}
			break;
		default:
			break;
	}
  return true;
}

//--------------------------------
//		TFile_text
//--------------------------------
class TImpBol0388_file: public TFile_text
{ 
protected:
  virtual void validate(TCursor& cur,TRecord_text &rec, TToken_string &val, TString& str);

public:
  TImpBol0388_file(const TString& file_name, char tipo);
  virtual ~TImpBol0388_file() { }
};

TImpBol0388_file::TImpBol0388_file(const TString& file_name, char tipo)
: TFile_text(file_name, tipo == 'B' ? "pg0388200a.ini" : "pg0388200b.ini"){}

void TImpBol0388_file::validate(TCursor& cur,TRecord_text &rec, TToken_string &s, TString& str)
{
  const TString code(s.get(0));
  TString valore = str;
  if (code == "_UPPERCASE")
  {
    valore.upper(); 
  }
  else NFCHECK("Macro non definita: %s", (const char *)code);
  str = valore;
} 


//----------------------------------
//		TDocumento_cache
//----------------------------------
//classe necessaria per avere una lista di documenti in RAM di dimensione limitata
class TDocumento_cache : public TArray
{
public:
	void flush();
	TDocumento& doc(const int anno, const char* codnum, const long ndoc);
};

TDocumento& TDocumento_cache::doc(const int anno, const char* codnum, const long ndoc)
{
	TDocumento* doc = NULL;
	for (int i = 0; i < items(); i++)
	{
		TDocumento& d = (TDocumento&)operator[](i);
		if (d.get(DOC_CODNUM) == codnum)
		{
		  if (d.get_int(DOC_ANNO) != anno || d.get_long(DOC_NDOC) != ndoc)
			{
				const TString& tipodoc = d.get(DOC_TIPODOC);
				if (tipodoc.full())
					d.write();
				destroy(i, true);
			}
			else
				doc = &d;
			break;
		}
	}

	if (doc == NULL)
	{
		doc = new TDocumento('D', anno, codnum, ndoc);
		add(doc);
	}
	
	return *doc;
}

void TDocumento_cache::flush()
{
	for (int i = 0; i < items(); i++)
	{
		const TDocumento& d = (const TDocumento&)operator[](i);
		const TString& tipodoc = d.get(DOC_TIPODOC);
		if (tipodoc.full())
			d.write();
	}
	destroy();
}

//----------------------------------
//		TSkeleton_application
//----------------------------------
class TImpBol0388 : public TSkeleton_application
{
	TImpBol0388_mask*	_msk;
	TImpBol0388_file* _trasfile;
	TConfig*					_configfile;
	TDocumento_cache	_doc;
  TRelation*				_rel;
  TCursor*					_cur;
  TProgind*					_prog;

	TString4 _numbol, _tipobol, _numord;
	int  _statobol;
	long _forbidden_ndoc, _lastbol;
	TString8 _codmag, _codmag_st;

	virtual const char * extra_modules() const {return "ba";}

protected:
	void mask2ini();
	void ini2mask();
	bool transfer();
	void transfer_testata(const TRecord_text& curr, TLog_report& log);
	void transfer_righe(const TRecord_text& curr);
	void evasione(TRiga_documento& riga_doc, const real& qta) const;
	int crea_riga_bolla(const TRecord_text& curr, TDocumento& bol, bool is_omaggio);

	bool transfer_inventario();

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

  TImpBol0388() {}
};

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


/////////////////////////////////////////////////
//  Trasferimento bolle e aggiornamento ordini
/////////////////////////////////////////////////

bool TImpBol0388::transfer()
{	
	_forbidden_ndoc = 0L;
	//file da trasferire
	//costruire il nome del file con path
	TFilename file = _msk->get(F_PATH);
	file.add(_msk->get(F_NAMEBOL));
	file.ext("txt");
  _trasfile = new TImpBol0388_file(file, 'B');
  _trasfile->open(file,'r');

	//preparazione del log
	TLog_report log(TR("Trasferimento bolle"));

	const long dimension = fsize(file);
  TProgind pi(dimension,TR("Importazione bolle in corso..."));

	int err = NOERR;
  TRecord_text curr;
	//parametri dei documenti da creare ed aggiornare
	_numbol = _msk->get(F_NUMBOL);
	_tipobol = _msk->get(F_TIPOBOL);
	_numord = _msk->get(F_NUMORD);
	_statobol = _msk->get_int(F_STATOBOL);

  while (_trasfile->read(curr) == NOERR && !pi.iscancelled() && err == NOERR) 
  {
    pi.setstatus(_trasfile->read_file()->tellg());
		//in base al tipo record scandisce tetate o righe con il relativo tracciato sul .ini
		const TString& tipo_rec = curr.type();
		if (tipo_rec == "TB01")
		{
			transfer_testata(curr, log);
		}
		if (tipo_rec == "RB01")
		{
			transfer_righe(curr);
		}
	}
  _trasfile->close();
	delete _trasfile;

	_doc.flush();	//accoppa la TDocumento_cache

	TReport_book buc;
	buc.add(log);
	buc.preview();

	return true;
}

void TImpBol0388::transfer_testata(const TRecord_text& curr, TLog_report& log)
{
	const long num_bol = atol(curr.get(1));
	const TDate datadoc = curr.get(2);
	const int anno_ord = atoi(curr.get(4));
	const long num_ord = atol(curr.get(5));

	// creo il documento, se esiste cancello le righe perche' le ricreo successivamente
  TDocumento& bol = _doc.doc(datadoc.year(), _numbol, num_bol);
  const bool isnew = bol.rows() == 0;

	const TString8 causmag = bol.tipo().caus_mov();
	
	_codmag = _codmag_st;
	if (causmag.full())
	{
		TCausale_magazzino causale(causmag);
		const TString & magdep = causale.default_magdep();
		if (magdep.full())
			_codmag = magdep;
		bol.put(DOC_CAUSMAG, causmag);
	}

	if (isnew)
	{
		_forbidden_ndoc = 0L;
		bol.put(DOC_TIPODOC, _tipobol);
		bol.put(DOC_DATADOC, datadoc);
		bol.put(DOC_STATO, _statobol);
		bol.put("DAANNO", anno_ord);
		bol.put("DANDOC", num_ord);
		//legge la testata dell'ordine per avere il clifo
		TDocumento& ordine = _doc.doc(anno_ord, _numord, num_ord);
		const char tipocf = ordine.get_char(DOC_TIPOCF);
	  const long  codcf = ordine.get_int(DOC_CODCF);
		bol.put(DOC_TIPOCF, tipocf);
		bol.put(DOC_CODCF, codcf);
		//codice pagamento e coordinate bancarie
		TToken_string key;
		key.add(tipocf);
		key.add(codcf);
		const TRectype& rec_clifo = cache().get(LF_CLIFO, key);
		bol.put(DOC_CODPAG, rec_clifo.get(CLI_CODPAG));
		bol.put(DOC_CODABIA, rec_clifo.get_long(CLI_CODABI));
		bol.put(DOC_CODCABA, rec_clifo.get_long(CLI_CODCAB));

		_lastbol = num_bol;	//aggiorna l'indicatore dell'ultima bolla trasferita
	}
	else
	{
		_forbidden_ndoc = num_bol;
		TString msg;
		msg.format(FR("La bolla %ld e' gia' stata trasferita"), num_bol);
		log.log(1, msg);
	}
	
}

void TImpBol0388::evasione(TRiga_documento& riga_doc, const real& qta) const
{
	const real quantita = riga_doc.quantita();
	const real qtaevasa = riga_doc.qtaevasa() + qta;
	riga_doc.put(riga_doc.field_qtaevasa(), qta);
	const bool evasa = qtaevasa >= quantita;
	if (evasa)
		riga_doc.put(RDOC_RIGAEVASA, evasa);
}

int TImpBol0388::crea_riga_bolla(const TRecord_text& curr, TDocumento& bol, bool is_omaggio)
{
	//carica i valori dal file di testo...
	//intanto la quantita', sia fatturata che omaggio
	const real qta = curr.get(is_omaggio ? 8 : 7);
	//se la quantita' e' vuota e' inutile proseguire (sara' una riga descrizione trattata a parte)
	if (qta <= ZERO)
		return 0;

  //il numero riga dell'ordine e' aumentato di 1 se omaggio
	const int nriga_ord = atoi(curr.get(4)) + (is_omaggio ? 1 : 0);
	TString16 codart = curr.get(5); codart.trim();

	TString16 strdata_scad = curr.get(10);
	strdata_scad.trim();
	const TDate data_scad(strdata_scad);
	const long lotto = atol(curr.get(11));

	//TRiga_documento& rdoc = bol.new_row(is_omaggio ? "09" : "01");
	TRiga_documento& rdoc = bol.new_row("01");

	//legge l'ordine originario della bolla sfruttando i dati noti
	const int ord_anno = bol.get_int("DAANNO");
	const long ord_ndoc = bol.get_long("DANDOC");
	TDocumento& ordine = _doc.doc(ord_anno, _numord, ord_ndoc);
	const int righe_ordine = ordine.physical_rows();

  bool da_ordine = nriga_ord > 0 && nriga_ord <= righe_ordine;
	if (da_ordine)
	{
		const TRiga_documento& rdocord = ordine[nriga_ord];
		da_ordine = rdocord.get(RDOC_CODART) == codart;
//			          rdocord.get(RDOC_TIPORIGA) == rdoc.get(RDOC_TIPORIGA);
	}

	//righe merce da ordine
	if (da_ordine)
	{
		TRiga_documento& rdocord = ordine[nriga_ord];
		//copia la riga dell'ordine originale nella riga della bolla, in modo da avere gia'..
		//..una parte dei campi completata, e dover solo aggiornare le quantita
		TDocumento::copy_data(rdoc, rdocord);
		//Evasione della riga ordine originaria
		evasione(rdocord, qta);
	}
	else	//righe merce non da ordine
	{
		const TString& codiva = cache().get(LF_ANAMAG, codart, ANAMAG_CODIVA);
		rdoc.put(RDOC_CODIVA, codiva);
		TToken_string key;
		key.format("%s|1", (const char*) codart);
		const TRectype& rec_umart = cache().get(LF_UMART, key);
		const TString& um = rec_umart.get(UMART_UM);
		rdoc.put(RDOC_UMQTA, um);
		const real prezzo = rec_umart.get(UMART_PREZZO);
		rdoc.put(RDOC_CODMAG, _codmag);
		rdoc.put(RDOC_PREZZO, prezzo);
  	rdoc.put(RDOC_CODART, codart);
	}

	rdoc.put(RDOC_DESCR, curr.get(6));
	rdoc.put(RDOC_QTA, qta);
	rdoc.put(RDOC_CODAGG1, data_scad);
	rdoc.put(RDOC_CODAGG2, lotto);

	if (is_omaggio)
	{
		rdoc.put(RDOC_GENERATA, "X");
		rdoc.put(RDOC_SCONTO, "100");
	}

	return rdoc.get_int(RDOC_NRIGA);
}

void TImpBol0388::transfer_righe(const TRecord_text& curr)
{
	const long numbol = atol(curr.get(1));
	const TDate datadoc = curr.get(2);
	const int nriga = atoi(curr.get(3));

	if (numbol != _forbidden_ndoc)	//non puo' riscrivere righe di una bolla esistente!
	{
		// crea la bolla della testata
		TDocumento& bol = _doc.doc(datadoc.year(), _numbol, numbol);

		/***prima versione dei file da importare con sottorighe descrizione
		//deve distinguere le righe speciali di descrizione
		TString8 str_numriga = curr.get(4);
		const int riga_descr = atoi(str_numriga.left(3));
		const int riga_ord = atoi(str_numriga.mid(3));
		if (riga_descr > 0 || codart == "**")	//il codart e' comunque lungo 10
		{
      switch (riga_descr)
			{
			case 0:
				break;
			case 1:
				{
  		    TRiga_documento& rdoc = bol.new_row("05");
					rdoc.put(RDOC_DESCR, curr.get(6));
				}
				break;
			default:
				{
					TRiga_documento& rdoc = bol[bol.rows()];
					TString descest = rdoc.get(RDOC_DESCEST);
					descest << "\n" << curr.get(6);
					rdoc.put(RDOC_DESCEST, descest);
					rdoc.put(RDOC_DESCLUNGA, 'X');
				}
				break;
			}
		}	//if (riga_descr > 0...*/

		//versione attuale
		TString16 codart = curr.get(5); codart.trim();
		//riga descrizione
		if (codart.blank())
		{
			TRiga_documento& rdoc = bol.new_row("05");
			rdoc.put(RDOC_DESCR, curr.get(6));
		}
		else	//e' riga merce o omaggio...
		{			
      crea_riga_bolla(curr, bol, false);  // Crea eventuale riga merce
			crea_riga_bolla(curr, bol, true);   // Crea eventuale riga omaggio
		}	
	}	//if (numbol != _forbidden_ndoc...
}

//////////////////////////////////////
//  Trasferimento inventario
//////////////////////////////////////

bool TImpBol0388::transfer_inventario()
{
	//file da trasferire
	//costruire il nome del file con path
	TFilename file = _msk->get(F_PATH);
	file.add(_msk->get(F_NAMEINV));
	file.ext("csv");
  _trasfile = new TImpBol0388_file(file, 'I');
  _trasfile->open(file,'r');

	//preparazione del log
	TLog_report log(TR("Trasferimento inventario"));

	const long dimension = fsize(file);
  TProgind pi(dimension,TR("Importazione inventario in corso..."));

	int err = NOERR;
  TRecord_text curr;
	TLocalisamfile mag_file(LF_MAG);
	TString80 last_codart;
	TAssoc_array last_codmag;

  while (_trasfile->read(curr) == NOERR && !pi.iscancelled() && err == NOERR) 
  {
    pi.setstatus(_trasfile->read_file()->tellg());

		//acquisizione dati dal record del file csv
		TString80 codart = curr.get(0);
		codart.strip("\"");
		//campi senza destinazione, per ora
/*		TString80 lotto = curr.get(1);
		lotto.strip("\"");
		TString16 str_data = curr.get(2);
		str_data.strip("\"");
		TDate data(str_data);*/
		TString16 str_giac = curr.get(3);
		str_giac.strip("\"");
		real giac(str_giac);
		TString8 codmag = curr.get(4);
		codmag.strip("\"");

		//scrittura dei record		
		if (codart == last_codart && last_codmag.objptr(codmag) != NULL)
		{
			real& tot_giac = *(real*)last_codmag.objptr(codmag);
			tot_giac += giac;
			mag_file.put(MAG_RIM, tot_giac);
			mag_file.put(MAG_GIAC, tot_giac);
			mag_file.rewrite();
		}
		else
		{
			mag_file.put(MAG_CODMAG, codmag);
			mag_file.put(MAG_CODART, codart);
			mag_file.put(MAG_NRIGA, 1);
			mag_file.put(MAG_RIM, giac);
			mag_file.put(MAG_GIAC, giac);
			last_codart = codart;
			last_codmag.destroy();
			last_codmag.add(codmag, giac);
			//precauzione nel caso venga reimportato un record simile
			if (mag_file.write() != NOERR)
				mag_file.rewrite();
		}

	}
  _trasfile->close();
	delete _trasfile;

	TReport_book buc;
	buc.add(log);
	buc.preview();

	return true;
}

////////////////////////////
//	Parte comune
////////////////////////////
void TImpBol0388::mask2ini()
{
	//aggiorna il numero dell'ultima bolla trasferita
	_msk->set(F_LASTBOL, _lastbol);
	//carica i parametri del file di configurazione
	_configfile->set_paragraph("RICEZIONE");
  for (int i = 0; i < _msk->fields() ; i++)
	{
		TMask_field& f = _msk->fld(i);
		const TFieldref* fr = f.field();
		if (fr != NULL)
			_configfile->set(fr->name(), f.get());
	}
}

void TImpBol0388::ini2mask()
{
	//carica i parametri del file di configurazione
	_configfile->set_paragraph("RICEZIONE");
	_lastbol = atol(_configfile->get("LASTBOL"));
  for (int i = 0; i < _msk->fields() ; i++)
	{
		TMask_field& f = _msk->fld(i);
		const TFieldref* fr = f.field();
		if (fr != NULL)
		{
			const TString& val = _configfile->get(fr->name());
			f.set(val);
		}
	}
}

bool TImpBol0388::create()
{
  _configfile = new TConfig("pg0388conf.ini");
  _msk = new TImpBol0388_mask();
  open_files(LF_DOC, LF_RIGHEDOC, 0);       
	TMagazzini mags;

	_codmag_st = mags.standardmag();
	_codmag_st.left_just(3);
	_codmag_st << mags.standarddep();
  return TSkeleton_application::create();
}

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

void TImpBol0388::main_loop()
{  
  KEY	tasto;
	ini2mask();
  tasto = _msk->run();
  if (tasto == K_ENTER)
  {
		if (_msk->get_bool(F_IMPBOL))
		{
			if (transfer())
				message_box(TR("Importazione bolle completata"));
		}
		if (_msk->get_bool(F_IMPINV))
		{
			if (transfer_inventario())
				message_box(TR("Importazione inventario completata"));
		}
		mask2ini();
  }
}

int pg0388200 (int argc, char* argv[])
{
  TImpBol0388 main_app;
  main_app.run(argc, argv, TR("Importazione Bolle"));
  return TRUE;
}