#include <applicat.h>

#include "../ve/velib.h"

#include "../mg/anamag.h"
#include "../ve/rcondv.h"

#include "cfven.h"
#include "doc.h"
#include "rdoc.h"

                                 ///////////////////////////
                                 ////    TORDF_CACHE    ////
                                 ///////////////////////////

//classe TOrdf_cache
class TOrdf_cache : public TCache
{
  TString4 _codnum, _tipodoc, _stato;
  int      _anno;
  TDate    _data;

protected:
	virtual void discarding(const THash_object* obj);
  virtual TObject* key2obj(const char* key);

public:
	const TString& codnum() { return _codnum;}
	const TString& tipodoc() { return _tipodoc;}

	TDocumento& doc(const TDoc_key& kdoc);
  TDocumento& doc(const long      codcf);
  TOrdf_cache(const char* from_tipodoc, const TDate& datatdoc);
};

//DISCARDING: salva un documento sul disco prima di eliminarlo dalla cache
void TOrdf_cache::discarding(const THash_object* obj)
{
  TDocumento& doc = (TDocumento&)obj->obj();
	if (doc.physical_rows() == 0 || (doc.physical_rows() == 1 && doc[1].get(RDOC_TIPORIGA) == "05"))
		doc.remove();
	else
		doc.rewrite();
}

//KEY2OBJ:sceglie il documento giusto da disco in modo da poterlo continuare, o lo crea se non c'�
TObject* TOrdf_cache::key2obj(const char* key)
{
  TDocumento* doc = NULL;

  TToken_string tmp(key);
  if(tmp.items() == 1)
  {
    const long codcf = tmp.get_long(0);

    doc = new TDocumento('D', _anno, _codnum, 0);
    doc->put(DOC_TIPODOC,  _tipodoc);
    doc->put(DOC_STATO,    _stato);
    doc->put(DOC_TIPOCF,   'F');
    doc->put(DOC_CODCF,    codcf);
    doc->put(DOC_DATADOC,  _data);
    doc->put(DOC_DATACONS, _data);

  }
  else
  {
    TDoc_key chiave(key);

    const char     provv  = chiave.provv();
    const int      anno   = chiave.anno();
    const TString& codnum = chiave.codnum();
    const int      ndoc   = chiave.ndoc();

    doc = new TDocumento(provv, anno, codnum, ndoc);
  }

  return doc;
}

//DOC: restituisce un puntatore ad un documento identificato dalla chiave documento completa
TDocumento& TOrdf_cache::doc(const TDoc_key& kdoc)
{
  return *(TDocumento*)objptr(kdoc);
}

//DOC: restituisce un puntatore ad un documento identificato dal codcf
TDocumento& TOrdf_cache::doc(const long codcf)
{
  TString16 key;
  key << codcf;
  return *(TDocumento*)objptr(codcf);
}

//metodo costruttore di una cache di 20 elementi
TOrdf_cache::TOrdf_cache(const char* from_tipodoc, const TDate& datadoc) : TCache() 
{
  TToken_string key;
  key.add("TIP");
  key.add(from_tipodoc);
  TString80 cod_elab = cache().get(LF_TABCOM, key, "S3"); cod_elab.mid(4,8); cod_elab.strip(" ");
  
  key.cut(0);
  key.add("ELD");
  key.add(cod_elab);
  const TRectype& eld = cache().get(LF_TABCOM, key);
  _codnum  = eld.get("S6");
  _tipodoc = eld.get("S8");
  _stato   = eld.get("S9");
  
  _anno = datadoc.year();
  _data = datadoc;
}

                                                    ////////////////////////////////////
                                                    ////    TCREA_ORDINI_SRM_APP    ////
                                                    ////////////////////////////////////

//Classe TCrea_ordini_srm_app
class TCrea_ordini_srm_app : public TSkeleton_application
{
private:
  TFilename _ini_name;
    
protected:
  virtual bool check_autorization() const {return false;}
  virtual const char * extra_modules() const {return "cg";}

  virtual void main_loop();

  const real cerca_prezzo(const char* codart, const TDocumento & ordf);
	const bool cerca_orf(TDoc_key& dadoc, const long daidriga, const long codfor, TDoc_key& ordf);
  void aggiorna_righe(TDocumento& ordf, const TDocumento& ordc, const TRiga_documento& rordc, TRiga_documento& rordf, int idrigac);

  void ordini_fornitore(const TDocumento& ordc, const int nriga, TDocumento& ordf);
  void aggiorna_ordf(const TDocumento& ordc, const int nriga, const TDoc_key& adoc, TOrdf_cache& ca, bool first);
  TDoc_key * crea_ordf(const TDocumento& ordc, const long nriga, const long codfor, TOrdf_cache& ca, bool first);
	void elimina_ordf(const TDoc_key& adoc, TOrdf_cache& ca);
  void elimina_righe(const TDocumento& ordc, TOrdf_cache& ca);
  
  void elabora_ordini_cliente();

public:
  bool load_ini(bool check);  
};

//CERCA_PREZZO: cerco il prezzo di un articolo per un dato fornitore
const real TCrea_ordini_srm_app::cerca_prezzo(const char* codart, const TDocumento & ordf)
{
  TToken_string key;

  //gestione RCONDV - Contratti
  key.add('C');     //tipo
  key.add("");      //categoria di vendita nulla sui contratti
  key.add('F');     //tipocf
  key.add(ordf.get(DOC_CODCF));   //codcf
  key.add(ordf.get(DOC_CODCONT)); //codice contratto
  key.add('A');     //<A>rticolo
  key.add(codart);  //codice articolo

  real prezzo = cache().get(LF_RCONDV, key, RCONDV_PREZZO);

  if (prezzo.is_zero())
  {
    key.cut(0);
    //gestione RCONDV - Listini
    key.add('L');     //tipo
    key.add(ordf.get(DOC_CATVEN));  //categoria di vendita
    key.add("");      //tipocf
    key.add("");      //codcf
    key.add(ordf.get(DOC_CODLIST)); //codice listino
    key.add('A');     //<A>rticolo
    key.add(codart);  //codice articolo

    const TRectype& rcondv = cache().get(LF_RCONDV, key);
    prezzo = rcondv.get_real(RCONDV_PREZZO);

    if (prezzo.is_zero())
    {
      const TRectype& anamag = cache().get(LF_ANAMAG, codart);
      prezzo = anamag.get_real(ANAMAG_ULTCOS1);
    }
  }

  return prezzo;
}

//CERCA_ORF: cerco se esistono degli ordini fornitori che arrivano dall'ordine cliente in esame
const bool TCrea_ordini_srm_app::cerca_orf(TDoc_key& dadoc, const long daidriga, const long codfor, TDoc_key& ordf)
{
  TString query;
  query << "USE RDOC KEY 4\n"
        << "FROM DAPROVV=\"" << dadoc.provv() << "\" DAANNO=" << dadoc.anno() << " DACODNUM=\"" << dadoc.codnum() << "\" DANDOC=" << dadoc.ndoc() << " DAIDRIGA=" << daidriga << "\n"
				<< "TO DAPROVV=\""   << dadoc.provv() << "\" DAANNO=" << dadoc.anno() << " DACODNUM=\"" << dadoc.codnum() << "\" DANDOC=" << dadoc.ndoc();
	if (daidriga > 0)
		query << " DAIDRIGA=" << daidriga << "\n";
	else
		query << " DAIDRIGA=999999\n";

  TISAM_recordset rdoc(query);

  for(bool ok = rdoc.move_first(); ok; ok = rdoc.move_next())
  {
    const char     provv  = rdoc.get(RDOC_PROVV).as_string()[0];
    const int      anno   = rdoc.get(RDOC_ANNO).as_int();
    const TString8 codnum = rdoc.get(RDOC_CODNUM).as_string();
    const int      ndoc   = rdoc.get(RDOC_NDOC).as_int();

    TDoc_key key(anno, codnum, ndoc, provv);

    const long codcf = atol(cache().get(LF_DOC, key, DOC_CODCF));
    
    if(codcf == codfor)
		{
      ordf = key;
		  return true;
		}
  }

  return false;
}

//AGGIORNA_RIGHE: riempie le righe dell'ordine fornitore con i dati corretti
void TCrea_ordini_srm_app::aggiorna_righe(TDocumento& ordf, const TDocumento& ordc, const TRiga_documento& rordc, TRiga_documento& rordf, int idrigac)
{
  ordf.copy_data(rordf, rordc);
  rordf.put(RDOC_PREZZO, cerca_prezzo(rordf.get(RDOC_CODART), ordf));
  rordf.zero(RDOC_CODAGG1);
  rordf.zero(RDOC_CODAGG2);
  rordf.zero(RDOC_PERCPROV);
  rordf.zero(RDOC_PERCPROV1);
  rordf.put(RDOC_DAPROVV,  ordc.get_char(DOC_PROVV));
  rordf.put(RDOC_DAANNO,   ordc.get_int(DOC_ANNO));
  rordf.put(RDOC_DACODNUM, ordc.get(DOC_CODNUM));
  rordf.put(RDOC_DANDOC,   ordc.get(DOC_NDOC));
  rordf.put(RDOC_DAIDRIGA, idrigac);
  rordf.dirty_fields();
  
  const real r = rordf.get("IMPNS"); // serve per aggiornare i valori della riga
}

//ORDINI_FORNITORE: genera effettivamente gli ordini fornitore
void TCrea_ordini_srm_app::ordini_fornitore(const TDocumento& ordc, const int nriga, TDocumento& ordf)
{
  const TRiga_documento& rordc = ordc[nriga];
  const int idrigac = rordc.get_int(RDOC_IDRIGA);

  bool trovato = false;

  //ciclo di aggiornamento delle righe che ancora esistono
  FOR_EACH_PHYSICAL_RDOC(ordf, i, rordf)
  {
    if(idrigac == rordf->get_int(RDOC_DAIDRIGA))
    {
      trovato = true;
      aggiorna_righe(ordf, ordc, rordc, *rordf, idrigac);
    }
  }

  if (!trovato)
  {
    TRiga_documento& rordf = ordf.new_row();
    aggiorna_righe(ordf, ordc, rordc, rordf, idrigac);
  }  
}

//AGGIORNA_ORDF: aggiorno l'ordine fornitore che esiste gi� in base alle modifiche fatte sull'ordine cliente attuale
void TCrea_ordini_srm_app::aggiorna_ordf(const TDocumento& ordc, const int nriga, const TDoc_key& adoc, TOrdf_cache& ca, bool first)
{ 
  TDocumento& ordf = ca.doc(adoc);
  const TRiga_documento & orfrow = ordc[nriga];
	const long codcf = ordf.get_long(DOC_CODCF);
	TToken_string keyc;
	const TString8 codlist = orfrow.get(first ? "LF1" : "LF2");

  //gestione CONDV - Listini
	if (codlist.full())
	{
		keyc.add('L');     //tipo
		keyc.add(ordf.get(DOC_CATVEN));  //categoria di vendita
		keyc.add("");      //tipocf
		keyc.add("");      //codcf
		keyc.add(codlist);

		const TRectype& list = cache().get(LF_CONDV, keyc);
		if (!list.empty())
		  ordf.put(DOC_CODLIST, codlist);
	}
	//gestione CONDV - Contratti
	const TString8 codcont = orfrow.get(first ? "CF1" : "CF2");
  if (codcont.full())
	{
		keyc = "C";        //tipo
		keyc.add("");      //categoria di vendita nulla sui contratti
		keyc.add('F');     //tipocf
		keyc.add(codcf);   //codcf
		keyc.add(codcont);

		const TRectype& contr = cache().get(LF_CONDV, keyc);
		if (contr.empty())
    {
      warning_box("Non trovando il contratto %s si utilizza lo 001.", (const char*)keyc);
	    keyc.add("001", 4);  // codice contratto default
	    if (!cache().get(LF_CONDV, keyc).empty())
		    ordf.put(DOC_CODCONT, "001");
    }
		else
			ordf.put(DOC_CODCONT, codcont);
	}

  ordini_fornitore(ordc, nriga, ordf);
}

//CREA_ORDF: creo un nuovo ordine cliente
TDoc_key * TCrea_ordini_srm_app::crea_ordf(const TDocumento& ordc, const long nriga, const long codfor, TOrdf_cache& ca, bool first)
{
	TDoc_key kordf;
	TDoc_key kdadoc(ordc.get_int(DOC_ANNO), ordc.get(DOC_CODNUM), ordc.get_long(DOC_NDOC), ordc.get_char(DOC_PROVV));
	TDocumento& ordf = cerca_orf(kdadoc, 0, codfor, kordf) ? ca.doc(kordf) :	ca.doc(codfor);
	
	if (ordf.physical_rows() == 0)
	{
		ordf.cli2doc();
		ordf.put(DOC_CODCMS, ordc.get(DOC_CODCMS));
		ordf.put(DOC_CODCOSTO, ordc.get(DOC_CODCOSTO));
		ordf.put(DOC_FASCMS, ordc.get(RDOC_FASCMS));
		ordf.put(DOC_NUMDOCRIF, ordc.numero());
		ordf.put(DOC_DATADOCRIF, ordc.get(DOC_DATADOC));
		TRiga_documento & row = ordf.new_row("05");

    TString rif;
		
		ordc.riferimento(rif);
		if (rif.empty())
			rif = ordc.tipo().descrizione();
		if (rif.full())
			rif << ' ';
		rif << "n. " << ordc.numero();
		rif << " del " << ordc.get(DOC_DATADOC);

		row.put(RDOC_DESCR, rif);
	}

  const TRiga_documento & orfrow = ordc[nriga];
	const long codcf = ordf.get_long(DOC_CODCF);
	TToken_string keyc;
	const TString8 codlist = orfrow.get(first ? "LF1" : "LF2");

  //gestione CONDV - Listini
	if (codlist.full())
	{
		keyc = "L";        // 'L'istino
		keyc.add(ordf.get(DOC_CATVEN));  //categoria di vendita
		keyc.add("");      //tipocf
		keyc.add("");      //codcf
		keyc.add(codlist);

		const TRectype& list = cache().get(LF_CONDV, keyc);
		if (!list.empty())
				ordf.put(DOC_CODLIST, codlist);
	}
	//gestione CONDV - Contratti
	const TString8 codcont = orfrow.get(first ? "CF1" : "CF2");

  if (codcont.full())
	{
		keyc = "C";        // 'C'ontratti
		keyc.add("");      // categoria di vendita nulla sui contratti
		keyc.add('F');     // tipocf
		keyc.add(codcf);   // codcf
		keyc.add(codcont); // codice contratto

		const TRectype& contr = cache().get(LF_CONDV, keyc);
		if (contr.empty())
	  {
      warning_box("Non trovando il contratto %s si utilizza lo 001.", (const char*)keyc);
		  keyc.add("001", 4);   // codice contratto di default
		  if (!cache().get(LF_CONDV, keyc).empty())
			  ordf.put(DOC_CODCONT, "001");
	  }
		else
			ordf.put(DOC_CODCONT, codcont);
	}
  ordini_fornitore(ordc, nriga, ordf);
	return new TDoc_key(ordf.get_int(DOC_ANNO), ordf.get(DOC_CODNUM), ordf.get_long(DOC_NDOC), ordf.get_char(DOC_PROVV));
}

//ELIMINA_ORDF: aggiorno l'ordine fornitore che esiste gi� in base alle modifiche fatte sull'ordine cliente attuale
void TCrea_ordini_srm_app::elimina_ordf(const TDoc_key& adoc, TOrdf_cache& ca)
{ 
  TDocumento& ordf = ca.doc(adoc);

	ordf.destroy_rows();
}

//ELIMINA_RIGHE: elimino le righe dagli ordini fornitore che non compaiono pi� nell'ordine cliente in esame
void TCrea_ordini_srm_app::elimina_righe(const TDocumento& ordc, TOrdf_cache& ca)
{
  const char     provv  = ordc.get_char(DOC_PROVV);
  const int      anno   = ordc.get_int(DOC_ANNO);
  const TString4 codnum = ordc.get(DOC_CODNUM);
  const long     ndoc   =  ordc.get_long(DOC_NDOC);

  TString query;
  query << "USE RDOC KEY 4 SELECT CODNUM==\"" << ca.codnum() << "\"\n"
        << "FROM DAPROVV=\"" << provv << "\" DAANNO=" << anno << " DACODNUM=\"" << codnum << "\" DANDOC=" << ndoc << "\n"
        << "TO DAPROVV=\""   << provv << "\" DAANNO=" << anno << " DACODNUM=\"" << codnum << "\" DANDOC=" << ndoc;

  TISAM_recordset rdoc(query);

  //scorro tutte le righe degli ordini fornitori; per ogni riga controllo se esiste ancora sull'ordine cliente;
  //se non esiste pi� la elimino dall'ordine fornitore, sfruttando la cache dei documenti
  for(bool ok = rdoc.move_first(); ok; ok = rdoc.move_next())
  {
    const int daidriga = rdoc.get(RDOC_DAIDRIGA).as_int();
    const int nriga    = rdoc.get(RDOC_NRIGA).as_int();
    TDoc_key kordf(rdoc.get(RDOC_ANNO).as_int(), rdoc.get(RDOC_CODNUM).as_string(), rdoc.get(RDOC_NDOC).as_int(), rdoc.get(RDOC_PROVV).as_string()[0]);
    TDocumento& ordf = ca.doc(kordf);
	  const long ndoc     =  ordf.get_long(DOC_NDOC);
	  const long codfor   =  ordf.get_long(DOC_CODCF);
//		bool trovato = false;

		TRiga_documento & r = ordf[nriga];
		const TRectype * rordc = r.find_original_rdoc();

		if (rordc != NULL)
		{
			if (rordc->get(RDOC_PROVV) == rordc->get(DOC_PROVV) &&
			    rordc->get(RDOC_CODNUM) == rordc->get(DOC_CODNUM) &&
			    rordc->get(RDOC_ANNO) == rordc->get(DOC_ANNO) &&
			    rordc->get(RDOC_NDOC) == rordc->get(DOC_NDOC))
			{
				if ((rordc->get_long(RDOC_CODAGG1) != codfor) &&
				     (rordc->get_long(RDOC_CODAGG2) != codfor))
					r.put("D", "X");
			}
		}
	}
  for(bool ok = rdoc.move_first(); ok; ok = rdoc.move_next())
  {
    TDoc_key kordf(rdoc.get(RDOC_ANNO).as_int(), rdoc.get(RDOC_CODNUM).as_string(), rdoc.get(RDOC_NDOC).as_int(), rdoc.get(RDOC_PROVV).as_string()[0]);
    TDocumento& ordf = ca.doc(kordf);

		for(int i = ordf.physical_rows(); i >= 1 ; i--)
		{
			if (ordf[i].get("D") == "X")
			  ordf.destroy_row(i, true);
		}
		for (int i = 1; i <= ordf.physical_rows(); i++)
			ordf[i].put(RDOC_NRIGA, i);
  }
}

//ELABORA_ORDINI_CLIENTE: scorro il file ini e aggiungo alla tabella dei listini le righe mancanti
void TCrea_ordini_srm_app::elabora_ordini_cliente()
{ 
  TConfig ini(_ini_name, "33");
  TString4 para;  para.format("%d", LF_DOC);
  const char     provv   = ini.get_char(DOC_PROVV, para);
	const int      anno    = ini.get_int(DOC_ANNO, para);
  const TString& codnum  = ini.get(DOC_CODNUM, para);
  const long     ndoc    = ini.get_long(DOC_NDOC, para);
	const bool delete_docs = ini.get(DOC_NOTE, para) == "DELETING";

  TDoc_key dadoc(anno, codnum, ndoc, provv);
  TDoc_key ordf;

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

  TOrdf_cache ca(ordc.get(DOC_TIPODOC), ordc.get_date(DOC_DATADOC));

  //scorro le righe documento, fermandomi quando la set_paragraph fallisce
  //dal momento che non so a priori quante sono le righe del documento
  for(int i = 1; i <= ordc.rows(); i++)
  {
    TRiga_documento& rordc = ordc[i];
    const long codfor1  = rordc.get_long(RDOC_CODAGG1); //da prendere dalla riga
    const long codfor2  = rordc.get_long(RDOC_CODAGG2); //da prendere dalla riga
//  const int  idrigac  = rordc.get_int(RDOC_IDRIGA);

		if (codfor1 > 0)
		{
			if(cerca_orf(dadoc, 0, codfor1, ordf))
			{
				if (delete_docs)
					elimina_ordf(ordf, ca);
				else
					aggiorna_ordf(ordc, i, ordf, ca, true);
			}
			else
				TDoc_key * ordf = crea_ordf(ordc, i, codfor1, ca, true);
		}
		if (codfor2 > 0)
		{

			if(cerca_orf(dadoc, 0, codfor2, ordf))
			{
				if (delete_docs)
					elimina_ordf(ordf, ca);
				else
					aggiorna_ordf(ordc, i, ordf, ca, false);
			}
			else
				TDoc_key * ordf = crea_ordf(ordc, i, codfor2, ca, false);
		}
  }

	if (!delete_docs)
		elimina_righe(ordc, ca);
  ca.destroy();
}

//LOAD_INI: carica il file ini che contiene il documento
bool TCrea_ordini_srm_app::load_ini(bool check)
{   
  //argc  � un intero che specifica quanti parametri vengono passati al programma dalla linea di comando
  const int args = argc();
  
  const int narg = check ? 3 : 2;
  
  //argv  � un array di null-terminated strings
  if (args <= narg)
    return false;                                     

	const TFixed_string 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);
    
  //controlla che il file su cui deve scrivere ci sia; se non c'� d� una segnalazione di errore
  if (!_ini_name.exist())
    return false;
  
  return true;
}

void TCrea_ordini_srm_app::main_loop() //definizione della member function main_loop, della classe TCrea_ordini_srm_app
{
 	TString sw = argv(2); sw.upper(); 
 	const bool chk_doc = sw == "-V";

  open_files(LF_TAB, LF_TABCOM, LF_DOC, LF_RIGHEDOC, 0);

  // mostra un messaggio di errore se non riesce ad aprire  il file richiesto
  if (load_ini(chk_doc))
  {
	  elabora_ordini_cliente();
    message_box(TR("Creazione ordini fornitore completata"));
  }
  else
    cantread_box(_ini_name);
}

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

int pd0610200(int argc, char* argv[])
{
  TCrea_ordini_srm_app app;
  app.run(argc,argv, TR("Ordini Fornitori"));
  return 0;
}