#include <applicat.h>
#include <automask.h>
#include <progind.h>
#include <reprint.h>
#include <reputils.h>

#include "../db/dblib.h"
#include "../ve/velib.h"
#include "../mr/mrplib.h"

#include "ps0920.h"
#include "ps0920300a.h"

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

public:
  TPianifica_impianti_mask();
  virtual ~TPianifica_impianti_mask();
};

TPianifica_impianti_mask::TPianifica_impianti_mask()
            : TAutomask("ps0920300a")
{
}

TPianifica_impianti_mask::~TPianifica_impianti_mask()
{  
}             

bool TPianifica_impianti_mask::on_field_event(TOperable_field& o, TField_event e, long jolly)
{
	const int id = o.dlg();
	switch (id)
	{
		case F_ADATA:
			if (e == fe_modify)
			{
				const TDate dadata = get_date(F_DADATA);
				const int daanno = dadata.year();
				const TDate adata = o.get();
				const int aanno = adata.year();
				if (daanno != aanno)
					return error_box(TR("Le date devono appartenere allo stesso anno!"));
			}
			break;
		default:
			break;
	}
  return true;
}


/////////////////////////////////////////////////////////////////
//	APPLICAZIONE
/////////////////////////////////////////////////////////////////
class TPianifica_impianti : public TSkeleton_application  
{
  TPianifica_impianti_mask * _mask;
	TArray _target_dates;

	virtual bool check_autorization() const {return false;}
  virtual const char * extra_modules() const {return "ve";}
	bool find_my_sister_row(const TRiga_documento& source_row, const TString& a_codnum, const TCodice_articolo& son_codartmag, 
													const TString& a_impianto, TRectype& sister_row, TLog_report& log) const;
	void delete_old_docs();
	void create_new_docs(TLog_report& log);

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

public:
  const TPianifica_impianti_mask & mask() const {return *_mask;}
  virtual ~TPianifica_impianti();
};


void TPianifica_impianti::delete_old_docs()
{
  TDate dadata = _mask->get_date(F_DADATA);
	TDate adata = _mask->get_date(F_ADATA);
	const int lastday = adata - dadata;

	const int anno = dadata.year();
  const TString& codnum = _mask->get(F_NUM);		//accoppa i documenti con numerazione sorgente
	const TString& impianto = _mask->get(F_AIMP);	//..e con l'impianto destinazione
  TRelation rel_doc(LF_DOC);
  TRectype darec(LF_DOC), arec(LF_DOC);
	TDate from(1, 1, anno - 1);
	TDate to(31, 12, anno);

	darec.put(DOC_DATADOC, from);
  darec.put(DOC_PROVV, "D");
  darec.put(DOC_ANNO, anno);
  darec.put(DOC_CODNUM, codnum);

	arec.put(DOC_DATADOC, to);
  arec.put(DOC_PROVV, "D");
  arec.put(DOC_ANNO, anno);
  arec.put(DOC_CODNUM, codnum);

	TString filtro;
	filtro << "CODNUM='" << codnum << "'";

  TCursor cur_doc (&rel_doc, filtro, 3, &darec, &arec);
  const long items = cur_doc.items();
  cur_doc.freeze();
  TProgind progind(items, "Eliminazione vecchi documenti in corso...", false, true);

  dadata = (TDate&) _target_dates[0];
  adata = (TDate&) _target_dates[lastday];
	for (cur_doc = 0; cur_doc.pos() < items; ++cur_doc)
  { 
    progind.addstatus(1);
    TDocumento doc(cur_doc.curr());
		//trovato un documento che soddisfa il darec/arec deve eliminarlo se la prima riga ha impianto..
		//..uguale a quello nel campo F_AIMP sulla maschera
		const TDate h_datacons = doc.get_date(DOC_DATACONS);
  	bool imp_ok =false, datacons_ok = false;

		for (int i = 1; i <= doc.physical_rows(); i++)
		{
			const TRiga_documento& rigadoc = doc[i];
			if (rigadoc.get(RDOC_IMPIANTO) == impianto)
				imp_ok = true;
			const TDate datacons = rigadoc.get_date(RDOC_DATACONS);
			if (datacons.ok())
				datacons_ok = (datacons >= dadata) && (datacons <= adata);
			else
				datacons_ok = (h_datacons >= dadata) && (h_datacons <= adata);
			if (imp_ok && datacons_ok)
			{
				doc.remove();
				break;
			}
		} //for(int i =...
  }	//for(cur_doc..
}

void TPianifica_impianti::create_new_docs(TLog_report& log)
{
	const TDate dadata = _mask->get_date(F_DADATA);
	const TDate adata = _mask->get_date(F_ADATA);
	const int anno = dadata.year();
  const TString& codnum = _mask->get(F_NUM);	//crea documenti con numerazioni sorgenti...
	const TString& da_impianto = _mask->get(F_DAIMP);	//..con l'impianto sorgente! (conserva la supersimmetria)
	//questi servono dopo!!!
	const TString& a_impianto = _mask->get(F_AIMP);
  TRelation rel_doc(LF_DOC);
  TRectype darec(LF_DOC), arec(LF_DOC);
	TLocalisamfile db(LF_RDIST);
	TDate from(1, 1, anno - 1);
	TDate to(31, 12, anno);

	db.setkey(2);
	darec.put(DOC_DATADOC, from);	
  darec.put(DOC_PROVV, "D");
  darec.put(DOC_ANNO, anno);
  darec.put(DOC_CODNUM, codnum);

	arec.put(DOC_DATADOC, to);
  arec.put(DOC_PROVV, "D");
  arec.put(DOC_ANNO, anno);
  arec.put(DOC_CODNUM, codnum);

	TString filtro;
	TDistinta_tree albero;
	filtro << "CODNUM='" << codnum << "'";
  TCursor cur_doc (&rel_doc, filtro, 3, &darec, &arec);
  const long items = cur_doc.items();
  cur_doc.freeze();
  TProgind progind(items, "Generazione nuovi documenti in corso...", false, true);

	for (cur_doc = 0; cur_doc.pos() < items; ++cur_doc)
  { 
    progind.addstatus(1);
    TDocumento doc(cur_doc.curr());
		TDocumento nuovo_doc('D', anno, codnum, 0);

		// crea un documento...
		nuovo_doc.copy_data(nuovo_doc, doc);
		nuovo_doc.put(DOC_TIPODOC, codnum);	//il tipo deve corrispondere alla numerazione!
		nuovo_doc.put(DOC_STATO, 2);
		TDate datadoc = nuovo_doc.get(DOC_DATADOC);

		TDate h_datacons = nuovo_doc.get(DOC_DATACONS);

		if (h_datacons < dadata || h_datacons > adata)
			continue;

    TDate datacons = h_datacons;

		
		const int ndays = datacons - dadata;
	
		if (ndays > 365)
			continue;

		datacons = (TDate &)_target_dates[ndays];
		nuovo_doc.put(DOC_DATACONS, datacons);
		
		for (int i = 1; i <= doc.rows(); i++)
		{
			const TRiga_documento& source_row = doc[i];	//riga del documento sorgente
			const TString80 codice(source_row.get(RDOC_CODARTMAG));


  		//il casino va fatto solo se la riga non e' gia' evasa, l'impianto e' quello selezionato e la riga e' riga articolo
			if (source_row.get(RDOC_IMPIANTO) == da_impianto && source_row.is_articolo())
			{

				db.zero();
				db.put("CODCOMP", codice);
				const int err = db.read(_isgteq);

				if (err == NOERR && codice == db.get("CODCOMP"))
				{
					const TCodice_articolo dest_codart = db.get("CODDIST");
					TArray labors;
					bool found = false;
					
					albero.set_root(dest_codart);

          for (TRiga_esplosione * l = albero.first_labor(labors); l != NULL && !found; l = albero.next_labor(labors))
					{
						TLavorazione * lav = TDistinta_tree::find_labor(l);

						if (lav != NULL && a_impianto == lav->cod_impianto(0))
						{
							const int priority = source_row.get_int(RDOC_PRIORITY);
							TDate datacons = source_row.get_date(RDOC_DATACONS);

							if (!datacons.ok())
								datacons = h_datacons;
							const int ndays = datacons - dadata;
							datacons = (TDate &)_target_dates[ndays];
							if (datacons < dadata || datacons > adata)
								continue;
							//..e la sua riga vuota
							TRiga_documento& nuova_rigadoc = nuovo_doc.new_row(source_row.get(RDOC_TIPORIGA));
							//riempie la nuova riga 

							nuovo_doc.copy_data(nuova_rigadoc, source_row);
							nuova_rigadoc.put(RDOC_CODART, dest_codart);
							nuova_rigadoc.put(RDOC_CODARTMAG, dest_codart);
							nuova_rigadoc.put(RDOC_PRIORITY, priority);
							nuova_rigadoc.zero(nuova_rigadoc.field_qtaevasa());
							nuova_rigadoc.zero(RDOC_RIGAEVASA);
							datacons = source_row.get_date(RDOC_DATACONS);
							if (datacons.ok())
							{
								datacons = (TDate &)_target_dates[ndays];
								nuova_rigadoc.put(RDOC_DATACONS, datacons);
							}
							nuova_rigadoc.put(RDOC_IMPIANTO, a_impianto);
							const TString &linea = lav->cod_linea(0);
							nuova_rigadoc.put(RDOC_LINEA, linea);
							found = true;
						}
					}
				}
			}	//if(source_row.get...
		}	//for(int i...
		if (nuovo_doc.physical_rows() > 0)
		{
			int err = nuovo_doc.write();
			if (err != NOERR)
			{
				TString errore;
				errore.format("Errore %d nella scrittura del documento %ld", err, nuovo_doc.get_long(DOC_NDOC));
				log.log(2, errore);
			}
		}
	}	//for(cur_doc...
}

void TPianifica_impianti::main_loop()
{
  while (_mask->run() == K_ENTER)
  {
	  TDate dadata = _mask->get_date(F_DADATA);
		TDate adata = _mask->get_date(F_ADATA);
		TDate data, end_date;
		const TString8 impianto = _mask->get(F_AIMP);
		const TString8 linea = _mask->get(F_LIN);
		TMRP_calendar c(linea, impianto);
		int ndays = adata - dadata + 1;
		const int ritardo = _mask->get_int(F_RITARDO);
		int min, max;

		data = dadata;
		end_date = dadata;
		end_date += ritardo;
		if (!data.is_holiday() && data.wday() != 6)
		{
			c.turni(end_date, min, max);

			while (max == 0 || end_date.is_holiday() || end_date.wday() == 6)
				c.turni(++end_date, min, max);
		}
		_target_dates.add(end_date, 0);

		for (int i = 1; i < 366; i++)
		{
			data = dadata;
			data += i;
			end_date = (TDate &) _target_dates[i - 1];
			++end_date;
			if (!data.is_holiday() && data.wday() != 6)
			{
				c.turni(end_date, min, max);

				while (max == 0 || end_date.is_holiday() || end_date.wday() == 6)
				{
					++end_date;
					c.turni(end_date, min, max);
				}

			}
			else 
				if (end_date - data > ritardo)
					--end_date;
			_target_dates.add(end_date, i);
		}
		delete_old_docs(); //intanto accoppa i vecchi documenti di tipo destinazione
		//poi crea i nuovi documenti di produzione
		TLog_report log("Errori generazione documenti pianificazione");

		create_new_docs(log);	

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

bool TPianifica_impianti::create()
{
	open_files(LF_TAB, LF_TABCOM, LF_DOC, LF_RIGHEDOC, LF_DIST, LF_RDIST, LF_ANAMAG, NULL);
	_mask = new TPianifica_impianti_mask;
	return TSkeleton_application:: create();
}

TPianifica_impianti::~TPianifica_impianti()
{
	delete _mask;
}

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

int ps0920300(int argc, char* argv[])
{
  TPianifica_impianti a;
  a.run(argc, argv, "Pianificazione impianti");
  return 0;
}