#include <applicat.h>
#include <automask.h>
#include <config.h>
#include <filetext.h>
#include <modaut.h>
#include <reprint.h>

#include "../cg/cglib01.h"
#include "../mg/mglib.h"

#include "ve6.h"
#include "ve6500.h"                                                          //cosí includo i codici dei campi della maschera ve6500.uml

#include <rdoc.h>
#include <doc.h>
#include "../mg/anamag.h"
#include "velib.h"


//////////   Dichiarazione delle classi   /////////////////////

///////////////////////////////////////////////////////////
// TDoc_recordset
///////////////////////////////////////////////////////////

class TDoc_recordset : public TISAM_recordset
{
  TDocumento * _doc;
  TRecnotype _mypos;

public:
  virtual TRecnotype items() const { return _doc->physical_rows();}
  virtual TRecnotype current_row() const { return _mypos; }
  virtual bool move_to(TRecnotype pos);
  virtual const TVariant& get(int logic, const char* field) const;

  TDocumento& doc() { return *_doc; }
  TRiga_documento& riga_doc(int r = 0) const;

  TDoc_recordset(const TDocumento& doc, const TString & query);
  virtual ~TDoc_recordset();
};

TRiga_documento& TDoc_recordset::riga_doc(int n) const
{
  if (n <= 0)
    n = _mypos+1;
	else
		if (n > items()) // Non dovrebbe succedere mai
		{
			n = _doc->new_row().get_int(RDOC_NRIGA);
			(*_doc)[n].put(RDOC_TIPORIGA, "05"); // Crea ua riga descrizione fittizia
		}
  return (*_doc)[n];
}

bool TDoc_recordset::move_to(TRecnotype pos)
{
  const bool ok = pos >= 0 && pos < items();
  if (ok)
  {
    if (pos != _mypos)
		{
      TRelation& rel = *relation();

      _mypos = pos;
      rel.curr(LF_RIGHEDOC) = riga_doc(); // Copia riga corrente nella relazione
      rel.update(1);    // Aggiorna solo i file della relazione dipendenti da LF_RIGHEDOC

		}
  }
  return ok;
}

const TVariant& TDoc_recordset::get(int logic, const char* field) const
{
  if (logic == 0 || logic == LF_DOC)
  {
    const TFieldref ref(field, LF_DOC);
    return get_tmp_var() = ref.read(*_doc);
  } else
  if (logic == LF_RIGHEDOC)
  {
    const TFieldref ref(field, LF_RIGHEDOC);
    return get_tmp_var() = ref.read(riga_doc());
  }
  return TISAM_recordset::get_field(logic, field);
}

TDoc_recordset::TDoc_recordset(const TDocumento& doc, const TString & query)
              : TISAM_recordset(query), _doc(NULL)
{
  _doc = new TDocumento(doc);
}

TDoc_recordset::~TDoc_recordset()
{
  if (_doc != NULL)
    delete _doc;
}

class TPenna_mask : public TAutomask
{
private:
  bool _gestmag;
  bool _gestdep;
  bool _gestmultimag;
    
protected:
  virtual bool on_field_event(TOperable_field& f, TField_event e, long jolly);
  virtual void on_firm_change();
     
public:
  TPenna_mask();
  virtual ~TPenna_mask(){}  
};

bool TPenna_mask::on_field_event(TOperable_field& f, TField_event e, long jolly)        //definizione di on_field_event
{                                                                                       //é la funzione che gestisce gli
  switch (f.dlg())                                                                      //eventi, tipo pressione dei tasti
  {       
  case F_FILE:
    if(e==fe_button)                   //se e (che é l'evento) = pressione del bottone 'cerca' (fe button)
    {
      DIRECTORY dir; 
      FILE_SPEC fs;
    
      TFilename fname=f.get();         //se il nome del file non esiste -> gli assegna automaticamente il nome
      if(fname == "")                  //articoli.dat
        fname = "articoli";
      
      xvt_fsys_get_dir(&dir);          //funzioni di xvt (grafica); utilizzate per creare la finestra di ricerca del
      xvt_fsys_get_dir(&fs.dir);       //file di tipo .dat (é una finestra tipo gestione risorse Windows)
      strcpy(fs.type, "dat");
      strcpy(fs.name, fname);
      strcpy(fs.creator, "ELD");
        
      const bool good = xvt_dm_post_file_open(&fs, "Selezionare il file ...") == FL_OK;
      xvt_fsys_set_dir(&dir);
                     
      if (good)
      {                 
        xvt_fsys_convert_dir_to_str(&fs.dir, fname.get_buffer(), fname.size());   //converte il nome della directory in una
        fname.add(fs.name);                                                       //stringa aggiungendo il path del file
        f.set(fname);                                                         //mostra il campo sul video (la funzione set) 
      }
      
    }
    if(e==fe_init && f.get().empty())                  
			f.set("articoli.dat");
    if(e==fe_close)                //se e = alla pressione del bottone 'chiudi' (fe_close) controlla che il campo sia stato
    {                              //riempito correttamente nella maschera
      TFilename n=f.get();
      return n.exist();
    }     
    break;
  
  case F_TIPORIGA:                     
    if(e==fe_init && f.get().empty())                  
			f.set("01");
    break;
  case F_TIPO:                        //scelta del tipo di registrazione
    if(e==fe_init || e==fe_modify)    
    {
      if (!_gestdep)
        hide(F_DEP);
    }
    break;
    
  default:
    break;
  }
  return TRUE;
}



void TPenna_mask::on_firm_change()               //funzione per settare i campi maschera in base alla ditta scelta
{
  TAutomask::on_firm_change();
  TConfig ini(CONFIG_DITTA,"mg");                     //apre il file di configurazione della ditta (modulo mg required)
  
  _gestmag=ini.get_bool("GESMAG");                    //setta la var bool in base all'interrogazione con get_bool (ovvero: se
                                                      //GESMAG esiste->true, altrimenti false
  _gestdep=_gestmag && ini.get_bool("GESDEPOSITI");   //fa la stessa cosa con la GESDEPOSITI (l'and ci vuole perché la gestio
  _gestmultimag=_gestmag && ini.get_bool("GESMULTIMAG");  //depositi non puó esistere senza il magazzino; uguale per la 
                                                          //GESMULTIMAG
  
}

TPenna_mask::TPenna_mask():TAutomask("ve6500")       //contructor di TPenna_mask;public di TAutomask chiama la maschera ve6500 
{
  on_firm_change();                                  //chiama la funzione on_firm_change, che aggiorna in tutti i campi della
                                                     //maschera che fanno rif. ad una ditta, con il rif. alla ditta attuale
}
                                                     
class TCheck_mask : public TAutomask
{
protected:
  virtual bool on_field_event(TOperable_field& f, TField_event e, long jolly);
     
public:
  TCheck_mask() : TAutomask("ve6500b") {}      //contructor di TPenna_mask;public di TAutomask chiama la maschera ve6500 
  virtual ~TCheck_mask(){}  
};

bool TCheck_mask::on_field_event(TOperable_field& f, TField_event e, long jolly)        //definizione di on_field_event
{                                                                                       //é la funzione che gestisce gli
  switch (f.dlg())                                                                      //eventi, tipo pressione dei tasti
  {       
	  case F_FILE:
			if(e==fe_button)                   //se e (che é l'evento) = pressione del bottone 'cerca' (fe button)
			{
				DIRECTORY dir; 
				FILE_SPEC fs;
    
				TFilename fname=f.get();         //se il nome del file non esiste -> gli assegna automaticamente il nome
				if(fname == "")                  //articoli.dat
					fname = "articoli";
      
				xvt_fsys_get_dir(&dir);          //funzioni di xvt (grafica); utilizzate per creare la finestra di ricerca del
				xvt_fsys_get_dir(&fs.dir);       //file di tipo .dat (é una finestra tipo gestione risorse Windows)
				strcpy(fs.type, "dat");
				strcpy(fs.name, fname);
				strcpy(fs.creator, "ELD");
        
				const bool good = xvt_dm_post_file_open(&fs, "Selezionare il file ...") == FL_OK;
				xvt_fsys_set_dir(&dir);
                     
				if (good)
				{                 
					xvt_fsys_convert_dir_to_str(&fs.dir, fname.get_buffer(), fname.size());   //converte il nome della directory in una
					fname.add(fs.name);                                                       //stringa aggiungendo il path del file
					f.set(fname);                                                         //mostra il campo sul video (la funzione set) 
				}
      
			}
			if(e==fe_init && f.get().empty())                  
				f.set("articoli.dat");
			if(e==fe_close)                //se e = alla pressione del bottone 'chiudi' (fe_close) controlla che il campo sia stato
			{                              //riempito correttamente nella maschera
				TFilename n=f.get();
				return n.exist();
			}     
			break;
	 default:
		  break;
  }
  return TRUE;
}

class TPenna_app : public TSkeleton_application
{
private:
  TFilename _ini_name;
  int _first_row;
  
protected:
  virtual void main_loop();
  void load_doc();
  void check_doc();

public:
  bool load_ini(bool check);  
};


///////////   Definizione delle funzioni membro   ////////////

bool TPenna_app::load_ini(bool check)                           //definizione della member function load_ini, della classe TPenna_app
{   
  const int args = argc();                            //argc  é un intero che specifica quanti parametri vengono passati al
  const int narg = check ? 3 : 2;
                                                      //programma dalla linea di comando
  if (args <= narg)
    return FALSE;                                     //argv  é un array di null-terminated strings

	const TString& arg = argv(narg);
  if ((arg[0] != '-' && arg[0] != '/') || (arg[1] != 'i' && arg[1] != 'I'))
    return FALSE;
    
  _ini_name = arg.mid(2);
  if (_ini_name.blank() && args > 3)  
    _ini_name = argv(3);
    
  if (!_ini_name.exist())                            //controlla che il file su cui deve scrivere ci sia; se non c'é dá
    return FALSE;                                    //una segnalazione di errore
  
  TConfig ini(_ini_name, "Main");                    //istanza di TConfig, chiamata ini
  TString_array paragrafi;                           //array di stringhe
  int numpar = ini.list_paragraphs(paragrafi);       //legge la lista dei paragrafi, li conta e mette il numero in numpar; lo
                                                     //fa la funzione num_paragraphs
  if(numpar<2)
    return FALSE;
    
  TToken_string para(paragrafi.row(numpar-1),',');   //istanzia (con il nome di para) la classe TToken_string, utilizzando il suo
                                                     //primo constructor (ne ha 3); setta come separatore di caratteri la , al
                                                     //posto del | di default; row é un metodo della classe TString_array che
                                                     //ritorna la stringa alla posizione specificata dal parametro
  _first_row = para.get_int(1)+1;                    //assegna a _first_row il numero (trasformato da una stringa con get_int)
                                                     //dell'ultima riga usata+1, cioé la prima riga libera: é il punto in cui
                                                     //cominciare a scrivere
  return TRUE;                                                                                         
}

void TPenna_app::check_doc()                              //definizione della member function main_loop, della classe TPenna_app
{              
  TCheck_mask m;                                          //istanza di TPenna_mask con il nome m (perche'ho chiamato una maschera)
  if(m.run()==K_ENTER)
  {
    TConfig ini(_ini_name, "Transaction");                       //apre il file su cui scrivere
                                                          //chiamo il nome del file F_FILE che abbiamo scelto con la maschera; get
                                                          //legge una stringa
		TRecord_cache c(LF_CODCORR, 2);

                                                          
    const TFilename nomefile = m.get(F_FILE);             //assegno alla variabile nomefile la stringa letta con m.get(F_FILE);   
                                                          //nomefile é una variabile di tipo TFilename
    TString16 para;                                       //codice del paragrafo corrente
    
                                                          
    TFile_text articoli(nomefile,"opticpen.ini");         //crea un oggetto articoli di tipo TFile_text
    TRecord_text articolocor;                             //crea un record vuoto con nome articolocor 
		TTracciato_record & rec = *(articoli.t_rec(""));
		const int nfields = rec.tracciati_campo().items();
    TString codice;                                       //crea una stringa per contenere il codice

    para.format("%d", LF_DOC);         
		
		const int anno = ini.get_int(DOC_ANNO, para);
		const TString16 codnum(ini.get(DOC_CODNUM));
		const char provv = ini.get_char(DOC_PROVV);
    const long ndoc = ini.get_long(DOC_NDOC);
		int fcod = -1;
		int fmag = -1;
		int fqta = -1;

		TDocumento d(provv, anno, codnum, ndoc);

		for (int f = 0; f < nfields; f++)
		{
			const TTracciato_campo & c = rec.get(f);
			const TString name = c.name();
			if (name == RDOC_CODART)
				fcod = f;
			else
				if (name == RDOC_CODMAG)
					fmag = f;
				else
					if (name == RDOC_QTA)
						fqta = f;
		}

		if (fcod < 0)
		{
			error_box("Manca il campo codice nel file del terminale");
			return;
		}
		if (fqta < 0)
		{
			error_box("Manca il campo quantità nel file del terminale");
			return; 
		}


    articoli.open();                                      //applico il metodo open che apre il file in lettura (contenuta in TFile_text)
    for(int i=_first_row;articoli.ok_r();i++)             //ok_r é una funzione che indica la fine del file
    { 
      if(articoli.read(articolocor)==NOERR)
      { 
				bool found = false;
				TString codice = articolocor.get(fcod);
				TString codicecorr;
				const real qta(articolocor.get(fqta));
				TString16 mag;

				codice.trim();
				const TRectype & codcorr = c.get(codice);

				if (!codcorr.empty())
					 codicecorr = codcorr.get("CODART");

				if (fmag >= 0)
					mag = articolocor.get(fmag);
				for (int r =1; !found && r <= d.physical_rows() ; r++)
				{
					TRiga_documento & row = d[r];
					const TString & rowcod = row.get(RDOC_CODART);

					if ((codice == rowcod) ||(codicecorr == rowcod))
					{
						if (mag.blank() || mag == row.get(RDOC_CODMAG))
						{
							const real q = qta + row.get_real(RDOC_QTAGG1);
							
							row.put(RDOC_QTAGG1, q);
							found = true;
						}
					}
        }
				if (!found)
				{
					TRiga_documento & row = d.new_row("01");
					row.put(RDOC_CODART, codice);
					row.put(RDOC_CODARTMAG, codice);
					row.put(RDOC_CODMAG, mag);
					row.put(RDOC_DESCR, cache().get(LF_ANAMAG, codice, ANAMAG_DESCR));
					row.put(RDOC_QTAGG1, qta);

				}
      }                                                       
    } 
		articoli.close();
		const int rows = d.physical_rows();
		bool doc_different = false;

		for (int r = rows; !doc_different && r > 0; r--)
		{
			TRiga_documento & row = d[r];
			const real qta1 = row.get_real(RDOC_QTAGG1);
			
			doc_different = row.get_real(RDOC_QTA) != qta1;
		}
		
		TReport rep;
		TReport_book  book;

		ini.set("Result", "SUCCESS", "Transaction");                        //comunica al chiamante il successo della transazione                                                      
		if (doc_different)
		{
		if (rep.load("ve6500a"))
		{
			const TString query(rep.recordset()->query_text());

			rep.set_recordset(new TDoc_recordset(d, query));
			book.add(rep);
			if (book.pages() > 0)
				book.print_or_preview();
				for (int r = rows; r > 0; r--)
				{
					TRiga_documento & row = d[r];
					const real qta1 = row.get_real(RDOC_QTAGG1);
					
					if (qta1 == ZERO)
						d.destroy_row(r, true);
					else
					{
						if (row.get_real(RDOC_QTA) != qta1)
							row.put(RDOC_QTA, qta1);
						row.zero(RDOC_QTAGG1);
					}
				}
				if (yesno_box("Si desidera aggiornare il documento\ncon la lettura da terminale"))
				{
					const int rows = d.rows();
					const int items = TRectype(LF_RIGHEDOC).items();
					for (int r = 1; r <= rows; r++)
					{
						const TRiga_documento & row = d[r];
						para.format("%d,%d", LF_RIGHEDOC, r);               // scrive i due numeri (%d) separati dalla , con format (che formatta
																															 // la stringa para con il formato specificato tra " ")
						ini.set_paragraph(para);                           // sposta il cursore sulla nuova para (nuovo valore)
						
						for (int f = 0; f < items; f++)
						{
							const char * name = row.fieldname(f);
							const TString & val = row.get(name);

							ini.set(name, val);                       // scrive il codice sul file ini
						}
					}
				}
				else
					ini.set("Result", "ERROR", "Transaction");                         //comunica al chiamante l'insuccesso della transazione                                                      
		}
		else
			error_box("Non posso leggere il report ve6500.rep");
		}
		xvt_fsys_removefile(nomefile);

  }
}  

void TPenna_app::load_doc()                               //definizione della member function main_loop, della classe TPenna_app
{              
  TPenna_mask m;                                          //istanza di TPenna_mask con il nome m (perche'ho chiamato una maschera)
  if(m.run()==K_ENTER)
  {
    TConfig ini(_ini_name, "Transaction");                       //apre il file su cui scrivere
                                                          //chiamo il nome del file F_FILE che abbiamo scelto con la maschera; get
                                                          //legge una stringa
    ini.set("Result", "SUCCESS");                         //comunica al chiamante il successo della transazione                                                      
                                                          
    const TFilename nomefile = m.get(F_FILE);             //assegno alla variabile nomefile la stringa letta con m.get(F_FILE);   
                                                          //nomefile é una variabile di tipo TFilename
    const TString16 tiporiga = m.get(F_TIPORIGA); 
    
    const int tipo = m.get_int(F_TIPO);                   //var int che indica la modalitá di archiviazione (0=quantitá, 1=dif. giac.)  
    
    TString16 para;                                       //codice del paragrafo corrente
    
                                                          
    TFile_text articoli(nomefile,"opticpen.ini");         //crea un oggetto articoli di tipo TFile_text
    TRecord_text articolocor;                             //crea un record vuoto con nome articolocor 
		TTracciato_record & rec = *(articoli.t_rec(""));
		const int nfields = rec.tracciati_campo().items();
    TString codice;                                       //crea una stringa per contenere il codice

    para.format("%d", LF_DOC);                             
    
    TDate datadoc(ini.get(DOC_DATADOC, para));      //dal paragrafo testata documento prende il valore della data del
                                                     //documento
    TString16 annoes;
    annoes.format("%04d", esercizi().date2esc(datadoc));    //crea un intero lungo 4 riempito di 0 all'inizio
    TString8 codmag = m.get(F_MAG);                 // setta codmag in base al valore della maschera
    codmag.left_just(3) << m.get(F_DEP);             // attacca a codmag il F_DEP, codice deposito dalla maschera  

    
    articoli.open();                                      //applico il metodo open che apre il file in lettura (contenuta in TFile_text)
    for(int i=_first_row;articoli.ok_r();i++)             //ok_r é una funzione che indica la fine del file
    { 
      if(articoli.read(articolocor)==NOERR)
      { 
	      para.format("%d,%d", LF_RIGHEDOC,i);               // scrive i due numeri (%d) separati dalla , con format (che formatta
                                                           // la stringa para con il formato specificato tra " ")
		    ini.set_paragraph(para);                           // sposta il cursore sulla nuova para (nuovo valore)
				ini.set(RDOC_TIPORIGA,tiporiga);                                                            
	      ini.set(RDOC_CODARTMAG, NULL_CODART);              // scrive il codice per forzare il check
				ini.set(RDOC_CODMAG, codmag);										 // magazzino di default

				for (int f = 0; f < nfields; f++)
				{
					const TTracciato_campo & c = rec.get(f);
					const TString name = c.name();
					if (name.not_empty())
					{
						TString val = articolocor.get(f);
						val.trim();

						if (name == RDOC_CODART)
							codice = val;
						else
							if (name == RDOC_QTA && tipo==1)
							{ 
          
								TArticolo_giacenza  & art = cached_article_balances(codice);
								real quantita(val);

								const real giacenza=art.disponibilita(annoes,codmag,"",TRUE);
								quantita = giacenza - quantita;
								val = quantita.string();			
							}
		        ini.set(name, val);                       // scrive il codice sul file ini
					}
        }
      }                                                       
      else
        break;                           
    } 
		articoli.close();
		xvt_fsys_removefile(nomefile);
  }
     
}  

void TPenna_app::main_loop()                              //definizione della member function main_loop, della classe TPenna_app
{
  if (!has_module(POAUT, CHK_DONGLE))
	{
    error_box("Modulo penna ottica non attivato");
		return;
	}

	TString sw(argv(2)); sw.upper(); 
 	const bool chk_doc = sw == "-V";

  open_files(LF_TAB,LF_TABCOM,LF_DOC,LF_RIGHEDOC,LF_ANAMAG,LF_MAG,LF_UMART,0);

  if(! load_ini(chk_doc))
  {
    error_box("Impossibile aprire il file '%s'",(const char*)_ini_name); //mostra un messaggio di errore se non riesce ad aprire
                                                                         //il file richiesto; nota la conversione dei tipi che
                                                                         //é da eseguire quando devo passare una TString ad un %s  
    return;
  }
	if (chk_doc)
		check_doc();
	else
		load_doc();


}

//////  Esecuzione del programma  ///////////

int ve6500(int argc, char** argv)
{
  TPenna_app app;
  app.run(argc,argv,"Terminali portatili");
  return 0;
}