#include "ps0099100.h"

#include <automask.h>
#include <modaut.h>
#include <progind.h>
#include <reprint.h>
#include <textset.h>

#include "../cg/cglib01.h"
#include "../ve/velib07.h"

///////////////////////////////////////////////////////////
//  MASCHERA
///////////////////////////////////////////////////////////
class TStatistiche_ANIVAL_mask : public TAutomask
{
protected:
  virtual bool on_field_event(TOperable_field& o, TField_event e, long jolly); 
	
public:
  TStatistiche_ANIVAL_mask();
  virtual ~TStatistiche_ANIVAL_mask() {}
};

TStatistiche_ANIVAL_mask::TStatistiche_ANIVAL_mask() : TAutomask("ps0099100")
{
}

bool TStatistiche_ANIVAL_mask::on_field_event(TOperable_field& o, TField_event e, long jolly)
{
  switch (o.dlg())
  {
  case F_ANNO:
    if (e == fe_modify && !o.empty())
    {
      TEsercizi_contabili esc;
      TDate inies, fines;
      if (esc.code2range(atoi(o.get()), inies, fines))
      {
        set(F_DADATA, inies);
        set(F_ADATA, fines);
      }
    }
    break;
  case F_DADATA:
	case F_ADATA:
		if (e == fe_close)
		{
			const int anno = get_int(F_ANNO);

			TEsercizi_contabili esc;	//..le date devono essere incluse nell'esercizio selezionato!
			const TDate data = o.get();
			if (!data.empty() && esc.date2esc(data) != anno)
				return error_box(TR("La data deve appartenere all'anno selezionato"));
		}
		break;
  default:
    break;
  }

  return true;
}

/////////////////////////////////////////////////////////////
//  REPORT
/////////////////////////////////////////////////////////////
class TStatistiche_ANIVAL_report : public TReport
{
protected:
virtual bool use_mask() { return false; }
public:
  TStatistiche_ANIVAL_report() {}
};

/////////////////////////////////////////////////////////////
//	CSV RECORDSET
/////////////////////////////////////////////////////////////

class TStatistiche_ANIVAL_csv_recordset : public TCSV_recordset
{
  real _tot[13];  //array con i totali per mese (e anno)

protected:
  long trova_riga(const TToken_string& key, const char tipo_stat, const char tipo_dettaglio);

public:
	TStatistiche_ANIVAL_csv_recordset(const TMask& mask);
	
	//virtual const TVariant& get(const char* column_name) const;
	//virtual const TVariant& get(unsigned int column) const { return TCSV_recordset::get(column); }
	
	void aggiungi_ai_totali(const TRiga_documento& riga, const char tipo_dati);
  void aggiungi_riga(TDocument_recordset& righe, const char tipo_dati, const char tipo_stat, const char tipo_dettaglio); //const TRiga_documento& riga, const char tipo_dati);
  void compila_intestazione();
  void calcola_percentuali();
};

TStatistiche_ANIVAL_csv_recordset::TStatistiche_ANIVAL_csv_recordset(const TMask& mask) 
: TCSV_recordset("CSV(\"\t\")")   //tab separated
{
  FOR_EACH_MASK_FIELD(mask, i, field)
	{
		const TFieldref* f = field->field();
		if (f != NULL)
		{
			const TString& name = f->name();
			set_var(name, field->get(), true);
		}
	}
}

/*const TVariant& TStatistiche_ANIVAL_csv_recordset::get(const char* field_name) const
{
	if (*field_name == '#')
		return get_var(field_name);

  return TRecordset::get(field_name);
}*/

void TStatistiche_ANIVAL_csv_recordset::compila_intestazione()
{
  insert_rec(0);
  //riempie i campi del primo record del csv in modo da avere l'intestazione (0=A...29=AD in excel)
  set(0, "CODART");
  set(1, "DESCRIZIONE");
  set(2, "GENNAIO");
  set(3, "%GEN");
  set(4, "FEBBRAIO");
  set(5, "%FEB");
  set(6, "MARZO");
  set(7, "%MAR");
  set(8, "APRILE");
  set(9, "%APR");
  set(10, "MAGGIO");
  set(11, "%MAG");
  set(12, "GIUGNO");
  set(13, "%GIU");
  set(14, "LUGLIO");
  set(15, "%LUG");
  set(16, "AGOSTO");
  set(17, "%AGO");
  set(18, "SETTEMBRE");
  set(19, "%SET");
  set(20, "OTTOBRE");
  set(21, "%OTT");
  set(22, "NOVEMBRE");
  set(23, "%NOV");
  set(24, "DICEMBRE");
  set(25, "%DIC");
  set(26, "ANNO");
  set(27, "%ANNO");
	set(28, "GRMERC");
	set(29, "CODCLI");
}


//metodo plutonico di ricerca dicotomica su una colonna di csv_recordset
long TStatistiche_ANIVAL_csv_recordset::trova_riga(const TToken_string& key, const char tipo_stat, const char tipo_dettaglio)
{
  long first = 0;
  long last = items() - 1;
  long riga = -1;

  while(first <= last)
  {
    const long guess = (first + last) / 2;
    move_to(guess);
    //const TString& guess_codart = get(0).as_string();		//ori
    //const int diff = guess_codart.compare(codart);		//ori

		TToken_string guess_key;
		
		switch (tipo_stat)
		{
		case 'A':	//articolo
			guess_key.add(get(0).as_string());
			break;
		case 'C':	//cliente-codart codart-cliente
			if (tipo_dettaglio == 'A')
			{
				guess_key.add(get(0).as_string());	//colonna 0=A codart
				guess_key.add(get(29).as_string());	//colonna 29=AD codcf
			}
			else
			{
				guess_key.add(get(29).as_string());	//colonna 29=AD codcf
				guess_key.add(get(0).as_string());  //colonna 0=A codart
			}
			break;
		case 'G':	//grmerc-codart
			guess_key.add(get(28).as_string());	//colonna 28=AC grmerc
			guess_key.add(get(0).as_string());
			break;

		default:
			break;
		}	


		const int diff = guess_key.compare(key);
    if (diff == 0)
    {
      riga = guess;
      break;
    }
    if (diff > 0)
    {
      last = guess - 1;
    }
    else
    {
      first = guess + 1;
    }
  }

  return riga;
}


//funzione di ordinamento per il campo codart (campo 0 sul csv)
static int compare_csv_rows_codart(const TObject** o1, const TObject** o2)
{
  TToken_string& s1 = *(TToken_string*)*o1;
  TToken_string& s2 = *(TToken_string*)*o2;

  //deve ordinare sul campo codart ed eventualmente clifo
  const TString& c1 = s1.get(0);
  const TString& c2 = s2.get(0);
  int cmp = c1.compare(c2);

  return cmp;
}

//funzione di ordinamento per il campo grmerc (campo 28 sul csv)
static int compare_csv_rows_grmerc_codart(const TObject** o1, const TObject** o2)
{
  TToken_string& s1 = *(TToken_string*)*o1;
  TToken_string& s2 = *(TToken_string*)*o2;

  //deve ordinare sul campo grmerc ed eventualmente codart
  TToken_string c1;
	c1.add(s1.get(28));
	c1.add(s1.get(0));
	
  TToken_string c2;
	c2.add(s2.get(28));
	c2.add(s2.get(0));
	
  int cmp = c1.compare(c2);

  return cmp;
}

//funzione di ordinamento per il campo codcf (campo 29 sul csv)
static int compare_csv_rows_codcf_codart(const TObject** o1, const TObject** o2)
{
  TToken_string& s1 = *(TToken_string*)*o1;
  TToken_string& s2 = *(TToken_string*)*o2;

  //deve ordinare sul campo codcf ed eventualmente codart
  TToken_string c1;
	c1.add(s1.get(29));
	c1.add(s1.get(0));
	
  TToken_string c2;
	c2.add(s2.get(29));
	c2.add(s2.get(0));
	
  int cmp = c1.compare(c2);

  return cmp;
}

//funzione di ordinamento per il campo codcf (campo 29 sul csv)
static int compare_csv_rows_codart_codcf(const TObject** o1, const TObject** o2)
{
  TToken_string& s1 = *(TToken_string*)*o1;
  TToken_string& s2 = *(TToken_string*)*o2;

  //deve ordinare sul campo codart ed eventualmente codcf
  TToken_string c1;
	c1.add(s1.get(0));
	c1.add(s1.get(29));
	
  TToken_string c2;
	c2.add(s2.get(0));
	c2.add(s2.get(29));
	
  int cmp = c1.compare(c2);

  return cmp;
}

void TStatistiche_ANIVAL_csv_recordset::aggiungi_ai_totali(const TRiga_documento& riga, const char tipo_dati)
{
  //datadoc (serve a stabilire in quale colonna andr� a sommarsi l'importo della riga corrente
  const TDate datadoc = riga.doc().get_date(DOC_DATADOC);
  const int mese = datadoc.month();

	real dato;
	//i dati da analizzare possono essere 'P'rezzi o 'Q'uantit�
	if (tipo_dati == 'P')
		dato = riga.importo(true, false); //importo riga corrente
	else
		dato = riga.quantita();	//qta riga corrente

	//aggiorna l'importone del mese (ricordarsi che l'array parte da 0 quindi ci va -1 nell'indice)
	_tot[mese - 1] += dato;
	//aggiorna anche il totale annuale
	_tot[12] += dato;
}

//metodo per la scrittura del csv
void TStatistiche_ANIVAL_csv_recordset::aggiungi_riga(TDocument_recordset& righe, 
																											const char tipo_dati, const char tipo_stat, const char tipo_dettaglio)
{
  const TDocumento& doc = righe.doc(righe.cursor()->curr());
  const int nriga = righe.get(RDOC_NRIGA).as_int();
	const TRiga_documento& riga = doc[nriga];

  //creazione di un nuovo record da esportare
  //esiste gi� questo codart?
  const TString80 codart = riga.get(RDOC_CODART);
	const long codcf = righe.get("DOC.CODCF").as_int();
	const TString& grmerc = righe.get("ANAMAG.GRMERC").as_string();

	//attenzione ai vari casi di composizione chiave!
	//A=articolo G=GrMerc+Articolo C=Cliente+Articolo o Articolo+Cliente in base al tipo di stampa scelta
	TToken_string key;
	switch (tipo_stat)
	{
		case 'A':
			key.add(codart);
			break;
		case 'C':
			{
				if (tipo_dettaglio == 'A')	//raccolto per articolo
				{
					key.add(codart);
					key.add(codcf);
				}
				else	//raccolto per cliente (con o senza esplosione)
				{
					key.add(codcf);
					key.add(codart);
				}
			}
			break;
		case 'G':
			key.add(grmerc);
			key.add(codart);
			break;
		default:
			break;
	}


	long numriga = trova_riga(key, tipo_stat, tipo_dettaglio);

  if (numriga < 0)
  {
    //codart
    new_rec("");
    set(0, TVariant(codart));
    //descrart
    const TString& descrart = cache().get(LF_ANAMAG, codart, ANAMAG_DESCR);
    set(1, TVariant(descrart));
    
		set(28, TVariant(grmerc));
		set(29, TVariant(codcf));

    //re-sorting per codart (serve perch� solo il recordset ordinato � scannerizzabile con il plutonico metodo dicotomico)
		switch (tipo_stat)
		{
		case 'A':
				sort(compare_csv_rows_codart);
			break;
		case 'C':
			  if (tipo_dettaglio == 'A')
					sort(compare_csv_rows_codart_codcf);
				else
					sort(compare_csv_rows_codcf_codart);
			break;
		case 'G':
				sort(compare_csv_rows_grmerc_codart);
			break;

		default:
			break;
		}
		
		//chiave.add(codart);

    numriga = trova_riga(key, tipo_stat, tipo_dettaglio);

    CHECKS(numriga >= 0, "Articolo bastardo ", (const char*)codart);
  }
  //riempimento del record secondo il tracciato:
  // codart+descrart+12*[impns+%incid]+grmerc+codcf
  
  //datadoc (serve a stabilire in quale colonna andr� a sommarsi l'importo della riga corrente
  const TDate datadoc = riga.doc().get_date(DOC_DATADOC);
  const int mese = datadoc.month();
  const int column = mese * 2;  //le colonne dei mesi sono gennaio=2,febbraio=4,marzo=6...

	real dato;
	real datone;

	if (tipo_dati == 'P')
		dato = riga.importo(true, false);				//'P'rezzo->importo
	else
		dato = riga.quantita();									//'Q'uantita

	//parte comune ai due casi
	datone = get(column).as_real();	//valora totale della colonna mese corrispondente nel csv
	datone += dato;									//aggiunge il valore riga al valore totale articolo del mese nel csv
	set(column, datone);						//riscrive il valore totale aggiornato nella sua colonna(in formato stringa, cio� con la "," e non il ".")


	//aggiunge anche grmerc, utilizzando codartmag per risalire al grmerc (se usasse codart potrebbe non esistere in anagrafica!)
	set(28, grmerc);
	//aggiunge pure il cliente
	set(29, codcf);

}

void TStatistiche_ANIVAL_csv_recordset::calcola_percentuali()
{
  //%incidenza articolo sul mese = imp_articolo mese / imp tot mese
  for (bool ok = move_first(); ok; ok = move_next())
  {
    real totale_anno;
    for (int i = 2; i <= 24; i += 2)
    {
      const real imp_art_mese = get(i).as_real();
      //ricordando che l'array parte da 0
      real perc = imp_art_mese * CENTO / _tot[i/2 - 1]; //calcola la % incidenza articolo sul totale per quel mese
			perc.round(5);
      set(i + 1, perc);
      totale_anno += imp_art_mese;
    }
    set(26, totale_anno);
    const real perc_anno = totale_anno * CENTO / _tot[12]; //calcola la % incidenza articolo sul totale annuale
    set(27, perc_anno);
  }
}


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

class TStatistiche_ANIVAL : public TSkeleton_application
{
  virtual bool check_autorization() const {return false;}
  virtual const char * extra_modules() const {return "ve";}

protected:
  void elabora(const TMask& mask) const;

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

};
                                                


//metodo di base per la ricerca delle righe documento che soddisfano i parametri dell'utonto
void TStatistiche_ANIVAL::elabora(const TMask& mask) const
{
	//PARAMETRI DI STAMPA
	//--------------------
	//tipo dati da estrarre (si utilizza nel metodo di scrittura riga e totali;stabilisce se si vuole qta o prezzo)
	const char tipo_dati = mask.get(F_TIPODATA)[0];
	//tipologia di statistica (Articolo - GruppoMerceologico - Cliente)
	const char tipo_stat = mask.get(F_TIPOSTAT)[0];
	//se sceglie per cliente ha 3 possibilit� di dettaglio (''=nessuno,'C'=raccolto per cliente,'A'=raccolto per articolo)
	const char tipo_dettaglio = mask.get(F_DETTAGLIO)[0];

	//stampa particolare bernazzalica in caso di UN SOLO CLIENTE selezionato nella stampa per clienete dettagliata per cliente
	//in questo caso i totali non sono generali ma solo su quel cliente (paranoie!)
	bool bernazzata = false;
	if (tipo_stat == 'C' && tipo_dettaglio == 'C')
	{
		const long dacli = mask.get_long(F_DACODCLI);
		const long acodcli = mask.get_long(F_ACODCLI);
		if (dacli == acodcli)
			bernazzata = true;
	}

	//creazione del csv recordset che verra' riempito dai record del recordset righe
  TStatistiche_ANIVAL_csv_recordset* csv = new TStatistiche_ANIVAL_csv_recordset(mask);

	//recordset per il calcolo dei totali mese/anno;servono per i calcoli futuri delle percentuali
	TString prequery;
  prequery << "USE RDOC KEY 1\n";
  prequery << "SELECT ((CODARTMAG!=\"\")&&(BETWEEN(33->DATADOC,#DADATA,#ADATA))";
	if (bernazzata)
		prequery << "&&(DOC.TIPOCF=='C')&&(DOC.CODCF==#DACODCLI)";
	prequery <<")\n";
  prequery << "JOIN DOC INTO PROVV==PROVV ANNO==ANNO CODNUM==CODNUM NDOC==NDOC\n";
  prequery << "FROM CODNUM=#CODNUM ANNO=#ANNO PROVV='D'\n";
  prequery << "TO CODNUM=#CODNUM ANNO=#ANNO PROVV='D'\n";
	TDocument_recordset totali(prequery);
	totali.set_var("#CODNUM", TVariant(mask.get(F_CODNUM)));
  totali.set_var("#DADATA", mask.get_date(F_DADATA));
  totali.set_var("#ADATA", mask.get_date(F_ADATA));
  totali.set_var("#ANNO", TVariant((long)mask.get_int(F_ANNO)));
	if (bernazzata)
		totali.set_var("#DACODCLI", mask.get_long(F_DACODCLI));
	const long totali_items = totali.items();
	if (totali_items > 0)
	{
		//E crea pure la progind..
    TProgind pi(totali_items, TR("Calcolo totali mensili ed annuali"), true, true);

    //Scansione del recordset trovato
    for (bool ok = totali.move_first(); ok; ok = totali.move_next())
	  {
      if (!pi.addstatus(1))
        break;
      const TDocumento& doc = totali.doc(totali.cursor()->curr());
      const int nriga = totali.get(RDOC_NRIGA).as_int();
      //scrive sul CSV i campi che servono al file di excel e al report
      csv->aggiungi_ai_totali(doc[nriga], tipo_dati);
		}
	}





	//CREAZIONE QUERY
	//---------------
	//scatta la query per la costruzione del recordset
	TString query;
	query << "USE RDOC KEY 1\n";
	query << "SELECT ((CODARTMAG!=\"\")&&(BETWEEN(33->DATADOC,#DADATA,#ADATA))&&";

	switch (tipo_stat)
	{
		case 'A':	//range su articoli (CODARTMAG)
			query << "(BETWEEN(CODARTMAG,#DACODART,#ACODART)))\n";			
			break;
		case 'C':
		  query << "(DOC.TIPOCF=='C')&&(BETWEEN(DOC.CODCF,#DACODCLI,#ACODCLI)))\n";	
			break;
		case 'G':
		  query << "(BETWEEN(ANAMAG.GRMERC,#DAGRMERC,#AGRMERC)))\n";
			break;
		default:
			break;
	}

	//parte comune ai vari casi di statistica della query
	query << "JOIN DOC INTO PROVV==PROVV ANNO==ANNO CODNUM==CODNUM NDOC==NDOC\n";

	//parte non comune (deve joinare anameg per avere il grmerc
	if (tipo_stat == 'G')
		query << "JOIN ANAMAG INTO CODART==CODARTMAG\n";


	//ri-parte comune
	query << "FROM CODNUM=#CODNUM ANNO=#ANNO PROVV='D'\n";
	query << "TO CODNUM=#CODNUM ANNO=#ANNO PROVV='D'\n";


	//CREAZIONE RECORDSET
	//-------------------
	TDocument_recordset righe(query);

	//parte comune di settaggio variabili
	righe.set_var("#CODNUM", TVariant(mask.get(F_CODNUM)));
	righe.set_var("#DADATA", mask.get_date(F_DADATA));
	righe.set_var("#ADATA", mask.get_date(F_ADATA));
	righe.set_var("#ANNO", TVariant((long)mask.get_int(F_ANNO)));

	switch(tipo_stat)
	{
		case 'A':
			righe.set_var("#DACODART", TVariant(mask.get(F_DACODART)));
			righe.set_var("#ACODART", TVariant(mask.get(F_ACODART)));
			break;
		case 'C':
			righe.set_var("#DACODCLI", TVariant(mask.get(F_DACODCLI)));
			righe.set_var("#ACODCLI", TVariant(mask.get(F_ACODCLI)));
			break;
		case 'G':
			righe.set_var("#DAGRMERC", TVariant(mask.get(F_DAGRMERC)));
			righe.set_var("#AGRMERC", TVariant(mask.get(F_AGRMERC)));
			break;

		default:
			break;
	}

	
	//CREAZIONE STAMPE/ESPORTAZIONI
	//-----------------------------
  //se trova (si spera!) almeno una rigadoc buona comincia il bello del programma
  const long righe_items = righe.items();
  if (righe_items > 0)
  {
    //E crea pure la progind..
    TProgind pi(righe_items, TR("Generazione file statistiche..."), true, true);

    //Scansione del recordset trovato
    for (bool ok = righe.move_first(); ok; ok = righe.move_next())
	  {
      if (!pi.addstatus(1))
        break;
      //scrive sul CSV i campi che servono al file di excel e al report
			csv->aggiungi_riga(righe, tipo_dati, tipo_stat, tipo_dettaglio);
    }

    //aggiorna le colonne delle percentuali
    csv->calcola_percentuali();

    //se richiesto il file in formato excel...
    if (mask.get_bool(F_EXCEL))
    {
      //crea la riga con le intestazioni dei campi e la mette all'inizio
      csv->compila_intestazione();
      //salva il file come richiesto; il nome del file viene deciso in base al tipo di statistica richiesta; il file alla fine..
			//..della storia � sempre lo stesso ma ordinato in modo diverso
      TString path = mask.get(F_PATH);
      path.lower();
			path << "\\statanival";
			switch (tipo_stat)
			{
			case 'A':
				path << "_art.xls";
				break;
			case 'C':
				path << "_cli.xls";
				break;
			case 'G':
				path << "_gmc.xls";
				break;

			default:
				break;
			}
			csv->save_as(path, fmt_silk);
      
      //accoppa la riga con le intestazioni dei campi
      csv->destroy(0);

#ifdef DBG
	    xvt_sys_goto_url(path, "open");
#endif
    }
		
		//REPORT DI STAMPA
		//----------------
    //creazione del report di stampa
	  TStatistiche_ANIVAL_report rep;
		bool stampa;
		//in base alle scelte dell'utonto stabilisce quale report usare
		switch(tipo_stat)
		{ 
		case 'A':
			stampa = rep.load("ps0099100a");
			break;
		case 'C':
			{
				switch (tipo_dettaglio)
				{
				case 'A':
					stampa = rep.load("ps0099100e");
					break;
				case 'C':
					stampa = rep.load("ps0099100d");
					break;
				default:
					stampa = rep.load("ps0099100c");
					break;
				}	
			}
			break;
		case 'G':
			stampa = rep.load("ps0099100b");
			break;

		default:
			break;
		}

    //setta il recordset...
    rep.set_recordset(csv);

	  if (stampa)
	  {
		  TReport_book book;
		  stampa = book.add(rep);
		  if (stampa)
			  book.print_or_preview();
    }

  } //if(righe_items>0...
	else
		delete csv;
}

void TStatistiche_ANIVAL::main_loop()
{
  TStatistiche_ANIVAL_mask mask;
  
  while (mask.run() == K_ENTER)
  {
    elabora(mask);
  }
} 

bool TStatistiche_ANIVAL::create()
{
  //se non ha le vendite � impossibile da utilizzare
  if (!has_module(VEAUT))
    return error_box(TR("Modulo non autorizzato"));

	//se non paghi ti stronco!!!
	const TDate oggi(TODAY);
	if (oggi >= 20091114)
		return false;

	return TSkeleton_application::create();
}

int ps0099100(int argc, char* argv[])
{
  TStatistiche_ANIVAL stat_anal;
  stat_anal.run(argc, argv, TR("Statistiche ANIVAL"));
  return 0;
}