#include <colors.h>
#include <urldefid.h>

#include "../ve/sconti.h"
#include "../ve/ve0100.h"
#include "../ve/veini.h"
#include "../ve/veuml.h"
#include "../ve/veuml3.h"
#include "../ve/verig.h"
#include "../ve/vepriv.h"

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

#define F_K F_USERFLD

#define F_LIVELLO F_USERFLD+1
#define F_DETTAGLIO F_USERFLD+2
#define F_COPIA F_USERFLD+3
#define F_INCOLLA F_USERFLD+4
#define F_SHRINK_ALL F_USERFLD+5
#define F_ZOOM_ALL F_USERFLD+6
#define F_SHRINK_ROW F_USERFLD+7
#define F_ZOOM_ROW F_USERFLD+8
#define F_USEK F_USERFLD+9
#define F_REVISION F_USERFLD+10
#define F_LOAD_COSTS F_USERFLD+11
#define F_COPIA_TUTTO F_USERFLD+12
#define FOR_EACH_DOC_ROW_BACK(d, r, row) const TRiga_documento* row = NULL; for (int r = d.rows(); r > 0 && (row = &d[r]) != NULL; r--)

                            //////////////////////////////////////////
                            ////    CLASSE TGestione_preventivo_msk    ////
                            //////////////////////////////////////////

//Definizione della classe della maschera
class TGestione_preventivo_msk : public TDocumento_mask
{
	int _rule;
	int _clipboard_row;
	long _clipboard_ndoc;
	char _clipboard_provv;
	TString8 _clipboard_codnum;
	int _clipboard_anno;
	TString16 _field_costo;

protected:  
//hanlder di documento:
  static bool pe_data_handler(TMask_field& f, KEY k);
//handler di riga:
  static bool pe_ritirato_handler(TMask_field& f, KEY k);
  static bool pe_codart_handler(TMask_field& f, KEY k);
  static bool pe_qta_handler(TMask_field& f, KEY k);
	static bool pe_k_handler(TMask_field& f, KEY k);
	static bool pe_prezzo_handler(TMask_field& f, KEY k);
	static bool pe_costo_handler(TMask_field& f, KEY k);

  static bool ss_notify(TSheet_field& ss, int r, KEY key);

	virtual void on_idle();
	virtual void highlight_row(int row, COLOR back = COLOR_INVALID, COLOR fore = COLOR_INVALID, bool dirty = true, bool update = true);
	const TString & build_tree_string(int level);
	bool new_revision(const char* codnum, long& ndoc) const;

public:
	static bool pe_espandi_handler(TMask_field& f, KEY k);
	static bool pe_espandi_riga_handler(TMask_field& f, KEY k);
  static bool pe_copia_handler(TMask_field& f, KEY k);
	static bool pe_incolla_handler(TMask_field& f, KEY k);
	static bool pe_new_revision_handler(TMask_field& f, KEY k);
	static bool pe_update_costi_handler(TMask_field& f, KEY k);
	virtual void user_set_handler( short fieldid, int index);
  virtual void user_set_row_handler(TMask& rm, short field, int index);

	void update_prezzo_vendita(TMask & row_mask);
	void update_costi_ricavi(int row = -1, bool update_sheet = false);

	virtual TVariable_mask * riga_mask(int numriga);
	virtual void doc2mask(bool reload_clifo = true, bool force_load = false, bool update = true);

  TGestione_preventivo_msk(const char* tipodoc);
};


                            //////////////////////////////////////////
                            ////    CLASSE TGestione_preventivo_APP    ////
                            //////////////////////////////////////////

// Definizione della classe dell'applicazione motore
class TGestione_preventivo_app : public TMotore_application
{
 protected:
	virtual TMask* get_mask( int mode );
  virtual int write( const TMask& m );
  virtual int rewrite( const TMask& m );
  virtual int read ( TMask& m );
  virtual void init_insert_mode( TMask& m );
	virtual void init_modify_mode(TMask &m);
  void elimina_vuote( const TMask& m);
  virtual bool last_doc(char provv, int anno, const char* codnum, long& ndoc, TDate& datadoc) const;

public:
  TGestione_preventivo_app() {}
};

inline TGestione_preventivo_app& peapp() { return (TGestione_preventivo_app &)main_app(); };

                            //////////////////////////////////////////
                            ////    CLASSE TGestione_preventivo_MSK    ////

                            //////////////////////////////////////////

const TString & TGestione_preventivo_msk::build_tree_string(int level)
{
	TString & tree = get_tmp_string(30);

	if (level < 10)
	{
		tree.fill(' ', level + 3);
		tree << '+';
		tree.rpad(13, '-');
	}
	else
		tree = "            +";
	TString l; l.format("%02d", level + 1);

	tree.overwrite(l, 0, 2);
	return tree;
}

////////////////////////////
//  HANDLER DI DOCUMENTO  //
////////////////////////////

void TGestione_preventivo_msk:: on_idle()
{
  TDocumento_mask::on_idle();
}

//magico metodo per settare, al cambio riga dello sheet, il focus sul campo desiderato (il campo in questione �..
//..definito nella on_idle(); ricordarsi la set_notify() nel costruttore della maschera senno' viene eseguito l'ss_notify()..
//..standard e non questo qui ridefinito. Allah!

void TGestione_preventivo_msk::update_costi_ricavi(int nrow, bool update_sheet)
{
	TDocumento& d = doc();
	const int rows = d.physical_rows();
	TArray valori;
	TArray costi;
  TSheet_field & sh = sfield(F_SHEET);
	
  const bool all = nrow < 0;
  if (all)
		nrow = rows;
	else
		d[nrow].autosave(sh);

	const int cur_level = d[nrow].get_int(RDOC_LEVEL);

	while (nrow < rows && cur_level < d[nrow + 1].get_int(RDOC_LEVEL))
		nrow++;
	for (int i = nrow; i > 0; i--)
	{
		TRiga_documento& row = d[i];
		const int level = row.get_int(RDOC_LEVEL);

		if (i == rows || level >= doc()[i + 1].get_int(RDOC_LEVEL))
		{
			const real importo = row.importo(true, false);
			
			row.put(RDOC_VALV, importo);
			for (int j = level - 1; j >= 0; j--)
			{
				real * r = (real *) valori.objptr(j);

				if (r == NULL)
				{
					r = new real;
					valori.add(r, j);
				}
				*r += importo;
			}

			const real costo = row.get_real(RDOC_QTA) * row.get_real(RDOC_COSTO);

			row.put(RDOC_VALC, costo);
			for (int j = level - 1; j >= 0; j--)
			{
				real * r = (real *) costi.objptr(j);

				if (r == NULL)
				{
					r = new real;
					costi.add(r, j);
				}
				*r += costo;
			}
		}
		else
		{
			real * r = (real *) valori.objptr(level);
			
			if (r != NULL)
			{
				row.put(RDOC_VALV, *r);
				*r = ZERO;
			}
			r = (real *) costi.objptr(level);
			if (r != NULL)
			{
				row.put(RDOC_VALC, *r);
				*r = ZERO;
			}
		}
		if (update_sheet)
		{
			row.autoload(sh);
			sh.check_row(i - 1);
			sh.force_update(i - 1);
		}
		if (!all && level <= 0)
			break;
	}
}

bool TGestione_preventivo_msk::ss_notify(TSheet_field& ss, int r, KEY key)
{
  TGestione_preventivo_msk& mask = (TGestione_preventivo_msk&) ss.mask();        
  TSheet_field & sh = mask.sfield(F_SHEET);        
	TDocumento& doc = mask.doc();

  switch (key)
  {
	case K_TAB: // entrata
		{
			TRiga_documento& row = doc[r + 1];
			TMask & row_mask = sh.sheet_row_mask(r);
			const int level = row.get_int(RDOC_LEVEL);
			const bool prodfin = level == 0;
			const bool price_enabled = (r + 1 == doc.physical_rows()) || (level >= doc[r + 2].get_int(RDOC_LEVEL));
			short id;

			sh.enable_cell(r, sh.cid2index(FR_PREZZO), price_enabled);
			sh.enable_cell(r, sh.cid2index(FR_JOLLY3), price_enabled);
			for (id = FR_CDC1; id < FR_CDC12; id++)
				sh.enable_cell(r, sh.cid2index(id), prodfin);
			for (id = FR_DESCDC1; id < FR_DESCDC12; id++)
				sh.enable_cell(r, sh.cid2index(id), prodfin);
			sh.force_update(r);
	  }
		break;
		case K_CTRL + K_INS: // Inserimento
		{
			const int doc_row = r + 1;
			int level = 0;

	 	 	TRiga_documento & row = doc[doc_row];

			if (r > 0 && row.is_merce())
			{
				const int father_sheet_row = r - 1;
				TRiga_documento& father_row = doc[father_sheet_row + 1];
				const int rows = doc.physical_rows();

				level = father_row.get_int(RDOC_LEVEL);
				const int next_level = mask.get_int(F_LIVELLO);
				if ( next_level > 0 && (doc_row == rows || level > doc[doc_row + 1].get_int(RDOC_LEVEL)))
					level = (doc_row == rows || next_level  - 1 >= doc[doc_row + 1].get_int(RDOC_LEVEL)) ? next_level - 1 : doc[doc_row + 1].get_int(RDOC_LEVEL);
				else
					if (mask.get_bool(F_DETTAGLIO) || (doc_row < rows && level < doc[doc_row + 1].get_int(RDOC_LEVEL)))
						level++;
				if (level > 0)
				{
					row.put(RDOC_CODCOSTO, father_row.get(RDOC_CODCOSTO));
					row.put(RDOC_CODCMS, father_row.get(RDOC_CODCMS));
					row.put(RDOC_FASCMS, father_row.get(RDOC_FASCMS));
					
					if (level > father_row.get_int(RDOC_LEVEL))
					{
						sh.disable_cell(father_sheet_row, sh.cid2index(FR_PREZZO));
						sh.disable_cell(father_sheet_row, sh.cid2index(FR_JOLLY3));
						father_row.zero(RDOC_COSTO);
						father_row.zero(RDOC_PREZZO);
						father_row.zero(RDOC_PPROV);
						father_row.zero(RDOC_K);
						father_row.autoload(sh);
						sh.force_update(father_sheet_row);
					}
					short id;

					for (id = FR_CDC1; id < FR_CDC12; id++)
						sh.disable_cell(r, sh.cid2index(id));
				}
			}
			row.put(RDOC_LEVEL, level);
			row.put(RDOC_TREE, mask.build_tree_string(level));
			row.autoload(sh);
			mask.reset(F_LIVELLO);
			mask.reset(F_DETTAGLIO);
		}
		break;
		case K_DEL: // Cancellazione
			{
				const int doc_row = r + 1;
				const int rows = doc.physical_rows();

				if (doc_row < rows)
				{
	 	 			TRiga_documento & row = doc[doc_row];
					const int level = row.get_int(RDOC_LEVEL);
					int i;
					int last_row = doc_row;

					for (i = doc_row + 1; i <= rows; i++)
						if (level < doc[i].get_int(RDOC_LEVEL))
							last_row = i;
						else
							break;
					for (i = last_row; i > doc_row; i--)
					{
						doc.destroy_row(i, true);
						sh.destroy(i - 1);
					}
					sh.force_update();
				}
			}
		break;
		case K_CTRL + K_DEL: // Cancellazione
			mask.update_costi_ricavi(-1, true);
		break;
		case K_ENTER: // Uscita con modifiche
			mask.update_costi_ricavi(r + 1, true);
		default:
		break;
  }
	return TDocumento_mask::ss_notify(ss, r, key);
}

void TGestione_preventivo_msk::highlight_row(int row, COLOR back, COLOR fore, bool dirty, bool update)
{			 
	TDocumento_mask::highlight_row(row, back, fore, dirty, false);
	TRiga_documento& rigadoc = doc()[row + 1];
	TSheet_field& sf = sfield(F_SHEET);
	COLOR back1 = back, back2 = COLOR_BLUE;
	const TColor_rule * c = (TColor_rule *) color_rules().objptr(_rule);

	if (c != NULL)
		c->colors(back2, fore);
	back = blend_colors(back1, back2, 1.0 - (((double) rigadoc.get_int(RDOC_LEVEL)) / 10.0));

	sf.set_back_and_fore_color(back, fore, row);
	if (update)
		sf.force_update(row);
}

TVariable_mask * TGestione_preventivo_msk::riga_mask(int numriga)
{ 
	const bool is_new = new_mask(numriga);
	TVariable_mask* m = TDocumento_mask::riga_mask(numriga);

	if (is_new && m != NULL)
	{
	  const TRiga_documento& riga = doc()[numriga + 1];
		TEdit_field & tree = m->add_string(FR_JOLLY1, 0, "Albero ", 2, 20, 13, "D_");
		TEdit_field & k = m->add_string(FR_JOLLY2, 0, "K ", 25, 20, 25);
		TEdit_field & v = m->add_currency(FR_JOLLY3, 0, "Costo", 53, 20, 15, "U");
		TEdit_field & p = m->add_string(FR_JOLLY4, 0, "", 75, 20, 1, "U");

	  m->set_handler(FR_CODART, pe_codart_handler);
		m->set_handler(FR_QTA, pe_qta_handler);
		m->set_handler(FR_PREZZO, pe_prezzo_handler);
		m->set_handler(FR_JOLLY2, pe_k_handler);
		m->set_handler(FR_JOLLY3, pe_costo_handler);
		tree.set_field(RDOC_TREE);
		k.set_field(RDOC_K);
		v.set_field(RDOC_COSTO);
		p.set_field(RDOC_PPROV);

	}
	return m;

}

void TGestione_preventivo_msk::doc2mask(bool reload_clifo, bool force_load, bool update)
{ 
	TDocumento_mask::doc2mask(reload_clifo, force_load, false);

	TSheet_field& s = sfield(F_SHEET);
	const int righe = s.items();
	const TDocumento & d = doc();
	const bool has_k = get_bool(F_USEK);

	for (int i = 0; i < righe; i++)
	{
    const TRiga_documento & row = d[i + 1];
		const int level = row.get_int(RDOC_LEVEL);
		const bool prodfin =  level == 0;
		const bool price_enabled = (i + 1 == d.physical_rows()) || (level >= d[i + 2].get_int(RDOC_LEVEL));

		s.enable_cell(i, s.cid2index(FR_PREZZO), price_enabled);
		s.enable_cell(i, s.cid2index(FR_JOLLY3), price_enabled);
		for (short id = FR_CDC1; id < FR_CDC12; id++)
			s.enable_cell(i, s.cid2index(id), prodfin);
	}
	//aggiorna i colori delle righe e forza l'update dello sheet
  highlight();
}

//PE_TESTATA_HANDLER: handler che si occupa di decodificare i campi data in testata
bool TGestione_preventivo_msk::pe_data_handler(TMask_field& f, KEY k)
{
  bool ok =  true;
  TGestione_preventivo_msk& mask = (TGestione_preventivo_msk&)f.mask();

	return ok;
}

bool TGestione_preventivo_msk::pe_espandi_handler(TMask_field& f, KEY k)
{
  if (k == K_SPACE)
  {
		TGestione_preventivo_msk& mask = (TGestione_preventivo_msk&)f.mask();
    TSheet_field& sh = mask.sfield(F_SHEET);
    const int r = sh.selected();
		const bool shrink = f.dlg() == F_SHRINK_ALL;
    const int size = shrink ? 1 : -1;
		int row_to_select = 0;

    FOR_EACH_SHEET_ROW(sh, i, row)
    {
			const int row_level = mask.doc()[i + 1].get_int(RDOC_LEVEL);

      if (row_level > 0) 
        sh.set_row_height(i, size);
			else 
				if (i <= r)
					row_to_select = i;
    }
		if (size < 0)
			sh.select(row_to_select); 
		sh.force_update();
  }
	return true;
}

bool TGestione_preventivo_msk::pe_espandi_riga_handler(TMask_field& f, KEY k)
{
  if (k == K_SPACE)
  {
		TGestione_preventivo_msk& mask = (TGestione_preventivo_msk&)f.mask();
    TSheet_field& sh = mask.sfield(F_SHEET);
    const int r = sh.selected();
		const int level = mask.doc()[r + 1].get_int(RDOC_LEVEL);

    if (r >= 0 && r < sh.items())
    {
			const bool shrink = f.dlg() == F_SHRINK_ROW;
      const int size = shrink ? 1 : -1;

      for (int i = r + 1; i < sh.items(); i++)
      {
				const int child_level = mask.doc()[i + 1].get_int(RDOC_LEVEL);
        
				if (child_level == level + 1)
					sh.set_row_height(i, size);
        else
					if (child_level > level + 1)
						sh.set_row_height(i, 1);
					else
          break;
      }
    }
		sh.force_update();
  }
	return true;
}

bool TGestione_preventivo_msk::pe_copia_handler(TMask_field& f, KEY k)
{
  bool ok =  true;
	if (k == K_SPACE)
	{
		TGestione_preventivo_msk& mask = (TGestione_preventivo_msk&)f.mask();
		
		mask._clipboard_ndoc = mask.get_long(F_NDOC);
		mask._clipboard_provv = mask.get(F_PROVV)[0];
		mask._clipboard_codnum = mask.get(F_CODNUM);
		mask._clipboard_anno = mask.get_int(F_ANNO);
		mask._clipboard_row = f.dlg() == F_COPIA ? mask.sfield(F_SHEET).selected() + 1 : 0;
	}
	return ok;
}

bool TGestione_preventivo_msk::pe_incolla_handler(TMask_field& f, KEY k)
{
  bool ok =  true;
	if (k == K_SPACE)
	{
		TGestione_preventivo_msk& mask = (TGestione_preventivo_msk&)f.mask();
		const bool alldoc = mask._clipboard_row == 0;
		const int cliprow = alldoc ? 1 : mask._clipboard_row;

		if (cliprow > 0)
		{
			TDocumento & dest = mask.doc();
			TDocumento * newdoc = &mask.doc();
			bool different_doc = dest.get_char(DOC_PROVV) != mask._clipboard_provv ||
													 dest.get_int(DOC_ANNO) != mask._clipboard_anno ||
													 dest.get(DOC_CODNUM) != mask._clipboard_codnum ||
													 dest.get_long(DOC_NDOC) != mask._clipboard_ndoc;
			
			if (different_doc)
				newdoc = new TDocumento(mask._clipboard_provv, mask._clipboard_anno, mask._clipboard_codnum, mask._clipboard_ndoc);

			TDocumento & orig = *newdoc;
			TSheet_field & sh = mask.sfield(F_SHEET);
			const int selected_row = sh.selected() + 1;
			const int rows = dest.physical_rows();
			const int next_level = mask.get_int(F_LIVELLO);
			const int orig_level = orig[cliprow].get_int(RDOC_LEVEL);
			const int level = next_level > 0 ? next_level - 1 : orig_level;
			const int level_offset = level - orig_level;
			int i;
			int start_row = rows > 0 ? -1 : 1;

			for (i = selected_row; start_row < 0 && i <= rows; i++)
			{
				const int row_level = dest[i].get_int(RDOC_LEVEL);
				
				if (level == row_level)
					start_row = i;
			}
			if (start_row > 0)
			{
				const	bool shift_source_rows = (!different_doc) && start_row <= cliprow;
				const int nrows = orig.physical_rows();

				for (i = cliprow; i <= nrows; i++)
				{
					int row_level = orig[i].get_int(RDOC_LEVEL);
					
					if (!alldoc && i > cliprow && orig_level >= row_level)
						break;
					const TRiga_documento & row = orig[i];
					
					sh.insert(start_row - 1, false, true);
					TRiga_documento & new_row = dest[start_row];
					dest.copy_data(new_row, row);
					row_level += level_offset;
					new_row.put(RDOC_LEVEL, row_level);
					new_row.put(RDOC_TREE, mask.build_tree_string(row_level));

					new_row.autoload(sh);
					start_row++;
					if (shift_source_rows)
						i++;
				}

				mask.highlight();
				sh.force_update();
			} 
			if (different_doc)
				delete newdoc;
			mask.reset(F_LIVELLO);
		}
	}
	return ok;
}

bool TGestione_preventivo_msk::pe_new_revision_handler(TMask_field& f, KEY k)
{
	bool ok = true;

	if (k == K_SPACE)
	{
		TGestione_preventivo_msk& mask = (TGestione_preventivo_msk&)f.mask();
		TDocumento& d = mask.doc();

    if (d.bloccato())
      return f.error_box(TR("Documento bloccato: non e' possibile farne una nuova revisione"));

		TDocumento backup(d);
    const TTipo_documento& td = d.tipo();
		const char stato_iniziale = d.stato();

		d.put(DOC_STATO, td.stato_bloccato());
		if (d.rewrite() == NOERR)
		{
			long ndoc = d.get_long(DOC_NDOC);

			if (mask.new_revision(d.get(DOC_CODNUM), ndoc))
			{
				d.renum_ndoc(ndoc);
				d.put(DOC_STATO, td.stato_finale_inserimento());

				ok = d.write() == NOERR;
				if (ok)
				{
          /* Trucco demenziale che non aggiorna bene ne' la TMask ne' la TRelation_application
					mask.doc2mask(false);
					mask.load_checks();
					mask.mask2doc();
					const int err = app().get_relation()->lfile().read(mask.doc());
					app().get_relation()->save_status();
          */
          mask.enable(DLG_NEXTREC); // Mi assicuro che sia acceso il bottone Avanti
          mask.stop_run(K_NEXT);    // Passo al documento successivo appena creato
				}
			}
			else
				ok = false;
		}
		else
			ok = false;
		if (!ok)
		{
			backup.stato(stato_iniziale);
			ok = backup.rewrite() == NOERR;
		}
	}
	return ok;
}

bool TGestione_preventivo_msk::pe_update_costi_handler(TMask_field& f, KEY k)
{
	if (k == K_SPACE)
	{
		TGestione_preventivo_msk & mask = (TGestione_preventivo_msk &) f.mask();
		TDocumento & doc = mask.doc();
		TSheet_field & sf = mask.sfield(F_SHEET);

		mask.mask2doc();

		const int rows = doc.physical_rows();

		for (int i = 1; i <= rows; i++)
		{
			bool ok = i == rows;
			TRiga_documento & row = doc[i];
			const int level = row.get_int(RDOC_LEVEL);

			if (!ok)
				ok = (level >= doc[i + 1].get_int(RDOC_LEVEL));

			if (ok)
			{
				const TString& codart = row.get(RDOC_CODARTMAG);

				if (codart.full())
				{
					const TArticolo_giacenza & art = cached_article(codart);
					const real costo = art.get_real(mask._field_costo);

					row.put(RDOC_COSTO, costo);
					row.autoload(sf);
				}
			}
		}
		sf.force_update();
	}
	return true;
}

///////////////////////
//  HANDLER DI RIGA  //
///////////////////////

bool TGestione_preventivo_msk::pe_codart_handler(TMask_field& f, KEY k)
{
  TMask& row_mask = f.mask();
  bool ok = codart_handler( f, k );

	TSheet_field * sh = row_mask.get_sheet();

	if (ok && sh != NULL && k == K_TAB && f.focusdirty())
	{
		TGestione_preventivo_msk & mask = (TGestione_preventivo_msk &) sh->mask();
		const bool has_k = mask.get_bool(F_USEK);
		bool update = false;
		const int r = sh->selected();

		const TString& codart = row_mask.get(FR_CODARTMAG);

		if (codart.full())
		{
			const TArticolo_giacenza & art = cached_article(codart);
			const real costo = art.get_real(mask._field_costo);

			if (has_k)
			{
				const real ric = art.get(ANAMAG_PERCRIC);
				TString80 k;

				if (ric != ZERO)
					k = ric.string();

				if (k.blank())
				{
					const TString catmer = art.get(ANAMAG_GRMERC);
					k = cache().get("GMC", catmer, "S5");
					if (k.blank())
						k = cache().get("GMC", catmer.left(3), "S5");
				}
				if (k.full())
					row_mask.set(FR_JOLLY2, k);
			}
			row_mask.set(FR_JOLLY3, costo);
		}
		if (has_k || !row_mask.field(FR_PREZZO).enabled())
			row_mask.set(FR_PREZZO, "");

		row_mask.set(FR_JOLLY4, mask.condv().get_prov());

		if (row_mask.field(FR_JOLLY3).enabled())
		{
				if (row_mask.get(FR_PREZZO).blank() &&
					  row_mask.get(FR_JOLLY3).full())
					mask.update_prezzo_vendita(row_mask);
		}
		else
			row_mask.set(FR_JOLLY3, "");
	}
	if (k == K_F8)
	{
		TSheet_field * sh = row_mask.get_sheet();

		if (sh != NULL)
		{
			TGestione_preventivo_msk & mask = (TGestione_preventivo_msk &) sh->mask();
			TDocumento & doc = mask.doc();
			const int rows = doc.physical_rows();
			const bool has_k = mask.get_bool(F_USEK);
			const int start_level = doc[sh->selected() + 1].get_int(RDOC_LEVEL);

			for (int i = sh->selected() + 2; i <= rows; i++)
			{
				TRiga_documento & row = doc[i];
				const int level = row.get_int(RDOC_LEVEL);
				if (level <= start_level)
					break;
				const bool prodfin = level == 0;
				const bool price_enabled = (i == doc.physical_rows()) || (level >= doc[i + 1].get_int(RDOC_LEVEL));
				short id;

				sh->enable_cell(i - 1, sh->cid2index(FR_PREZZO), price_enabled);
//				sh->enable_cell(i - 1, sh->cid2index(FR_JOLLY2), price_enabled && has_k);
				sh->enable_cell(i - 1, sh->cid2index(FR_JOLLY3), price_enabled);

				const TString& codart = row.get(RDOC_CODARTMAG);
				const TArticolo_giacenza & art = cached_article(codart);
				const bool has_k = mask.get_bool(F_USEK);
				
				if (has_k || ! price_enabled)
					row.zero(RDOC_PREZZO);

				if (price_enabled && has_k)
				{
					const real ric = art.get(ANAMAG_PERCRIC);
					TString80 k;

					if (ric != ZERO)
						k = ric.string();
	
					if (k.blank())
				{
						const TString catmer = art.get(ANAMAG_GRMERC);
						k = cache().get("GMC", catmer, "S5");
						if (k.blank())
							k = cache().get("GMC", catmer.left(3), "S5");
					}
					if (k.full())
						row.put(RDOC_K, k);
				}
				if (price_enabled)
				{
					const real costo = art.get_real(mask._field_costo);
					row.put(RDOC_COSTO, costo);
				}

				for (id = FR_CDC1; id < FR_CDC12; id++)
					sh->enable_cell(i - 1, id, prodfin);
				for (id = FR_DESCDC1; id < FR_DESCDC12; id++)
					sh->enable_cell(i - 1, id, prodfin);
				row.put(RDOC_TREE, mask.build_tree_string(level));
				row.autoload(*sh);
				mask.highlight_row(i -1, COLOR_INVALID, COLOR_INVALID, true, false);
			}
			sh->force_update();
		}
	}
  return ok;
}

bool TGestione_preventivo_msk::pe_qta_handler(TMask_field& f, KEY k)
{
  bool ok = qta_handler( f, k );

	if (ok)
	{
		TMask& row_mask = f.mask();
		TSheet_field * sh = row_mask.get_sheet();
		if (sh != NULL)
		{
			TDocumento_mask & mask = (TDocumento_mask &) sh->mask();
			TDocumento & doc = mask.doc();

			if (k == K_F8)
			{
				const int r = sh->selected();
				
				if (r > 0)
				{
					TRiga_documento & row = doc[r + 1];
					const int level = row.get_int(RDOC_LEVEL);

					if (level > 0)
					{
						int father_row = r;
						while (r > 1 && level == doc[father_row].get_int(RDOC_LEVEL))
							father_row--;

						TRiga_documento & row = doc[father_row];
						const real qta_prec = row.get_real(RDOC_QTA);
						real qta(f.get());
						TToken_string & shrow = sh->row(r);

						qta *= qta_prec;
						shrow.add(qta.string(), sh->cid2index(FR_QTA));
						sh->force_update(r);
						row.put(RDOC_QTA, qta);
						f.focusdirty();
					}
				}
			}
			if (f.to_check(k, true))
			{
				const int r = sh->selected();
				TRiga_documento & row = doc[r + 1];
				const real old_qta = row.get_real(RDOC_QTA);
				const real qta = row_mask.get_real(FR_QTA);
				const int level = row.get_int(RDOC_LEVEL);

				if (old_qta != qta)
				{
					const int rows = doc.physical_rows();

					for (int i = r + 2; i <= rows && level < doc[i].get_int(RDOC_LEVEL); i++)
					{
						TToken_string & shrow = sh->row(i - 1);
						real row_qta(shrow.get(sh->cid2index(FR_QTA)));

						row_qta *= qta;
						if (!old_qta.is_zero())
							row_qta /= old_qta;
						row_qta.round(5);
						shrow.add(row_qta.string(), sh->cid2index(FR_QTA));
						doc[i].put(RDOC_QTA, row_qta);
					}
					row.put(RDOC_QTA, qta);
					sh->force_update();
				}
			}
		}
	}
  return ok;
}
void TGestione_preventivo_msk::update_prezzo_vendita(TMask & row_mask)
{
	real costo = row_mask.get(FR_JOLLY3);
	TString k(row_mask.get(FR_JOLLY2));
	TString ge;
	real perc;
	TSheet_field * sh = row_mask.get_sheet();
	const int r = sh->selected();

	if (k.blank())
		k = get(F_K);
	if (scontoexpr2perc(k, false, ge, perc))
		costo *= (real(2.00) - perc);
	
	TCurrency_documento prezzo(costo, doc(), true);
	row_mask.set(FR_PREZZO, prezzo.get_num().string());
	row_mask.set(FR_JOLLY4, "K");
}

bool TGestione_preventivo_msk::pe_k_handler(TMask_field& f, KEY k)
{
  TMask& row_mask = f.mask();
  TSheet_field * sh = row_mask.get_sheet();

	if (sh == NULL)
		return true;

	TGestione_preventivo_msk& mask = (TGestione_preventivo_msk&)sh->mask();

	const bool last_level = row_mask.field(FR_JOLLY3).enabled();
	if (last_level)
	{
		if ((k == K_TAB && f.focusdirty()) || k == K_F8)
			mask.update_prezzo_vendita(row_mask);
	}
	else 
		if (k == K_F8)
		{
			const TString k(row_mask.get(FR_JOLLY2));
			const int r = sh->selected();
			TDocumento & doc = mask.doc();
			const int start_level = doc[r + 1].get_int(RDOC_LEVEL);
			TString ge;
			real perc;

//			row_mask.reset(FR_JOLLY2);
			sh->row(r).add("", sh->cid2index(FR_JOLLY2));
			sh->force_update(r);
			for (int i = r + 2; i <= doc.physical_rows(); i++)
			{
				TRiga_documento & row = doc[i];
				const int level = row.get_int(RDOC_LEVEL);
				
				if (level <= start_level)
					break;
				if (sh->cell_enabled(i -1, sh->cid2index(FR_PREZZO)))
				{
					TToken_string & shrow = sh->row(i - 1);

					row.put(RDOC_K, k);
					shrow.add(k, sh->cid2index(FR_JOLLY2));
					TString row_k(k);

					if (row_k.blank())
						row_k = mask.get(F_K);
					
					real val = row.get_real(RDOC_COSTO);
					
					if (scontoexpr2perc(row_k, false, ge, perc))
						val *= (2 - perc);
					TCurrency_documento prezzo(val, doc, true);
					row.put(RDOC_PREZZO, prezzo.get_num());
					row.put(RDOC_PPROV, "K");
					shrow.add(prezzo.get_num().string(), sh->cid2index(FR_PREZZO));
					shrow.add("K", sh->cid2index(FR_JOLLY4));
				}
			}
			mask.update_costi_ricavi(r + 1, true);
		}

  return true;
}

bool TGestione_preventivo_msk::pe_prezzo_handler(TMask_field& f, KEY k)
{
  TMask& row_mask = f.mask();
	const bool last_level = row_mask.field(FR_JOLLY3).enabled();

	if (last_level)
	{
		if (k == K_F8)
		{
			TSheet_field * sh = row_mask.get_sheet();

			if (sh != NULL)
			{
				TGestione_preventivo_msk & mask = (TGestione_preventivo_msk &) sh->mask();
				TDocumento & doc = mask.doc();
				const int row = sh->selected();
				TCond_vendita & condv = mask.condv();

				condv.set_testa(&mask);
				condv.set_riga(&row_mask);
				condv.ricerca();
				doc[row + 1].put(RDOC_PREZZO, row_mask.get(FR_PREZZO));
				doc[row + 1].put(RDOC_PPROV, condv.get_prov());

				row_mask.reset(FR_JOLLY2);
				row_mask.field(FR_JOLLY2).set_focusdirty(false);
			}
		}
		if (k == K_TAB && f.focusdirty())
		{
			row_mask.reset(FR_JOLLY2);
			row_mask.field(FR_JOLLY2).set_focusdirty(false);
		}
	}
  return true;
}

bool TGestione_preventivo_msk::pe_costo_handler(TMask_field& f, KEY k)
{
  TMask& row_mask = f.mask();
	const bool last_level = row_mask.field(FR_JOLLY3).enabled();

	if (last_level)
	{
		if (k == K_TAB && f.focusdirty())
		{
			row_mask.reset(FR_JOLLY2);
			row_mask.field(FR_JOLLY2).set_focusdirty(false);
		}
	}
  return true;
}

//metodo che setta gli handler sui campi di riga
void TGestione_preventivo_msk::user_set_row_handler(TMask& rm, short field, int index)
{
  switch(index)
  {
  case 5101: rm.set_handler(field, pe_codart_handler); break;
  case 5102: rm.set_handler(field, pe_qta_handler); break;
  default  : TDocumento_mask::user_set_row_handler(rm, field, index); break;
  }
}

//metodo che setta l'handler di bolla
void TGestione_preventivo_msk::user_set_handler( short fieldid, int index)
{
  switch(index)
  {
  case 5101: set_field_handler(fieldid, pe_data_handler); break;
  default  : TDocumento_mask::user_set_handler(fieldid, index); break;
  }
}

bool TGestione_preventivo_msk::new_revision(const char* codnum, long& ndoc) const
{
	const TCodice_numerazione& num = cached_numerazione(codnum);
	const int revlen = num.revision_len();

	if (revlen > 0)
	{
		real fattore = exp10(revlen); fattore.round(0);
		const long divisore = fattore.integer();
		const long newndoc = ndoc + 1;

    // Obfuscated C contest: forse bastava scrivere questo
    // if (newndoc/divisore != ndoc/divisore) 
    if (((newndoc / divisore) - (ndoc / divisore)) != 0)  
			return false;
		else
		{
			ndoc = newndoc;
			return true;
		}
	}
	return false;
}

TGestione_preventivo_msk::TGestione_preventivo_msk(const char* tipodoc) : TDocumento_mask(tipodoc), _clipboard_row(-1)
{
  //TConfig* configpe = new TConfig(CONFIG_DITTA, "pe"); // A cosa serve?
  sfield(F_SHEET).set_notify( ss_notify );
	_rule = color_rules().add(new TColor_rule("Livello", "", _numexpr, COLOR_BLUE, FOCUS_COLOR));

  TFilename pn; doc().tipo().profile_name(pn);
  TConfig prof(pn, "MAIN");

	_field_costo = prof.get("FieldCosto", NULL, -1, ANAMAG_ULTCOS1);
}

                            //////////////////////////////////////////
                            ////    CLASSE TGESTIONE_PREVENTIVO_APP    ////
                            //////////////////////////////////////////

//ridefinisco il metodo get_mask delle TMotore_application
TMask* TGestione_preventivo_app::get_mask( int mode )
{
  if (mode == MODE_INS || mode == MODE_MOD)
  {
    TString4 tipodoc;
    if (mode == MODE_MOD)
      tipodoc = get_relation()->curr().get(DOC_TIPODOC); // Lo prendo dalla relazione (Gelai)
    else
      tipodoc = TMotore_application::get_mask(MODE_QUERY)->get(F_TIPODOC);
    if (_doc_masks.objptr(tipodoc) == NULL)
    {
      TGestione_preventivo_msk* m = new TGestione_preventivo_msk(tipodoc);
      _doc_masks.add(tipodoc, m);
      const TTipo_documento& tdoc = m->doc().tipo();
      const TString_array& handlers = tdoc.handlers();
	    FOR_EACH_ARRAY_ROW(handlers, i, row)
	    {
        m->user_set_handler( row->get_int( 0 ), row->get_int( 1 ) );
	    }
			TSheet_field & sh = m->sfield(F_SHEET);
			const int y = m->sh_y() - 1;

			m->add_button(F_LOAD_COSTS, sh.page(), "Costi", 66, y - 2, 4, 1, "");
			m->set_handler(F_LOAD_COSTS, TGestione_preventivo_msk::pe_update_costi_handler);
			m->add_button(F_REVISION, sh.page(), "Rev.", 73, y - 2, 4, 1, "");
			m->set_handler(F_REVISION, TGestione_preventivo_msk::pe_new_revision_handler);

			m->add_button(F_SHRINK_ALL, sh.page(), "--", 61, y - 1, 1, 1, "", BMP_DARROWU);
			m->set_handler(F_SHRINK_ALL, TGestione_preventivo_msk::pe_espandi_handler);
			m->add_button(F_ZOOM_ALL, sh.page(), "++", 66, y -1, 1, 1, "", BMP_DARROWD);
			m->set_handler(F_ZOOM_ALL, TGestione_preventivo_msk::pe_espandi_handler);
			m->add_button(F_SHRINK_ROW, sh.page(), "-", 71, y - 1, 1, 1, "", BMP_SARROWU);
			m->set_handler(F_SHRINK_ROW, TGestione_preventivo_msk::pe_espandi_riga_handler);
			m->add_button(F_ZOOM_ROW, sh.page(), "+", 76, y -1, 1, 1, "", BMP_SARROWD);
			m->set_handler(F_ZOOM_ROW, TGestione_preventivo_msk::pe_espandi_riga_handler);

			TBoolean_field & fk = m->add_boolean(F_USEK, sh.page(), "", 2, y);
			
			fk.set_field(DOC_USEK);
//			m->set_handler(F_USEK, TGestione_preventivo_msk::pe_usek_handler);

			TToken_string * fk_false = fk.message(0, true);
			TToken_string * fk_true = fk.message(1, true);

			*fk_false << "CLEAR," << F_K;
			*fk_true << "ENABLE," << F_K;

			TEdit_field & k = m->add_string(F_K, sh.page(), "K ", 5, y, 25);
			
			k.set_field(DOC_K);
			m->add_number(F_LIVELLO, sh.page(), "Livello Succ. ", 33, y, 1);
			m->add_boolean(F_DETTAGLIO, sh.page(), "Dettaglia", 53, y);
			m->add_button(F_COPIA_TUTTO, sh.page(), "~Tutto", 66, y, 1, 1, "", BMP_FILECHK);
			m->set_handler(F_COPIA_TUTTO, TGestione_preventivo_msk::pe_copia_handler);
			m->add_button(F_COPIA, sh.page(), "~Copia", 71, y, 1, 1, "", BMP_COPY);
			m->set_handler(F_COPIA, TGestione_preventivo_msk::pe_copia_handler);
			m->add_button(F_INCOLLA, sh.page(), "~Incolla", 76, y, 1, 1, "", BMP_PASTE);
			m->set_handler(F_INCOLLA, TGestione_preventivo_msk::pe_incolla_handler);
			sh.enable_column(sh.cid2index(FR_JOLLY1), false);
			sh.enable_column(sh.cid2index(FR_JOLLY4), false);
    }
  } 
  return TMotore_application::get_mask(mode);
}

bool TGestione_preventivo_app::last_doc(char provv, int anno, const char* codnum,
                                        long& ndoc, TDate& ddoc) const
{
	bool ok = TMotore_application::last_doc(provv, anno, codnum, ndoc, ddoc);
	const TCodice_numerazione& num = cached_numerazione(codnum);
	const int revlen = num.revision_len();

	if (revlen > 0)
	{
		real fattore = exp10(revlen); fattore.round(0);
		const long divisore = fattore.integer();
		
		if (ok)
			ndoc /= divisore;
		else
			ndoc = 1;
		ndoc++;
		ndoc *= divisore;
		return true;
	}
	return ok;
}
//ridefinisco il metodo write delle TMotore_application
int TGestione_preventivo_app::write( const TMask& m )
{   
	TGestione_preventivo_msk & mask = (TGestione_preventivo_msk &) m;
	TDocumento& doc = mask.doc();
	const int rows = doc.physical_rows();
	const TString k = doc.get(DOC_K);

	for (int i = rows ; i > 0; i--)
	{
		TRiga_documento & row = (TRiga_documento &)doc[i];
		const int level = row.get_int(RDOC_LEVEL);
		const TString & row_k = row.get(RDOC_K); 

		if ((row_k.blank()) && (i == rows || level >= doc[i + 1].get_int(RDOC_LEVEL)))
			row.put(RDOC_K, k);
	}
	mask.update_costi_ricavi();
  return TMotore_application::write(m);
}

//ridefinisco il metodo rewrite delle TMotore_application
int TGestione_preventivo_app::rewrite( const TMask& m )
{
	TGestione_preventivo_msk & mask = (TGestione_preventivo_msk &) m;
	TDocumento& doc = ((TDocumento_mask&) m).doc();
	const int rows = doc.physical_rows();
	const TString k = doc.get(DOC_K);

	for (int i = rows ; i > 0; i--)
	{
		TRiga_documento & row = (TRiga_documento &)doc[i];
		const int level = row.get_int(RDOC_LEVEL);
		const TString & row_k = row.get(RDOC_K); 

		if ((row_k.blank()) && (i == rows || level >= doc[i + 1].get_int(RDOC_LEVEL)))
			row.put(RDOC_K, k);
	}
	mask.update_costi_ricavi();
	return TMotore_application::rewrite(m);
}

//ridefinisco il metodo read della TMotore_application
int TGestione_preventivo_app::read(TMask& m)
{
  const int err = TMotore_application::read(m);

	if (err == NOERR)
	{
		TDocumento_mask& mask = (TDocumento_mask&) m;
		TDocumento& doc = mask.doc();
		const TString k = doc.get(DOC_K);
		TSheet_field & sh = mask.sfield(F_SHEET);

		for (int i = 1 ; i <= doc.physical_rows(); i++)
			if (k == doc[i].get(RDOC_K))
			{
				doc[i].zero(RDOC_K);
				TToken_string & r = sh.row(i - 1);
				r.add("", sh.cid2index(FR_JOLLY2));
			}
	}
	return err;
}

//ridefinisco il metodo init_insert_mode della TMotore_application
void TGestione_preventivo_app::init_insert_mode(TMask &m)
{
	m.disable(F_REVISION);
  m.enable(F_LOAD_COSTS);
	return TMotore_application::init_insert_mode(m);
}

void TGestione_preventivo_app::init_modify_mode(TMask &m)
{
  TGestione_preventivo_msk& mask = (TGestione_preventivo_msk&) m;
  const TDocumento& d = mask.doc();
  const TCodice_numerazione& num = d.codice_numerazione();
  mask.enable(F_REVISION, !d.bloccato() && num.revision_len() > 0);
  mask.enable(F_LOAD_COSTS, !d.bloccato());

  return TMotore_application::init_modify_mode(m);
}

int pe0400( int argc, char* argv[])
{
  TGestione_preventivo_app a;

  a.run( argc, argv, TR("Gestione Preventivi"));
  return 0;
}