// NeL - MMORPG Framework <http://dev.ryzom.com/projects/nel/>
// Copyright (C) 2010  Winch Gate Property Limited
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as
// published by the Free Software Foundation, either version 3 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program.  If not, see <http://www.gnu.org/licenses/>.

// attrib_dlg.cpp : implementation file
//



#include "std_afx.h"
#include "object_viewer.h"
#include "attrib_dlg.h"
#include "editable_range.h"
#include "color_edit.h"
#include "basis_edit.h"
#include "value_blender_dlg.h"
#include "value_gradient_dlg.h"
#include "value_from_emitter_dlg.h"
#include "bin_op_dlg.h"
#include "edit_user_param.h"
#include "edit_spinner.h"
#include "edit_follow_path.h"
#include "scheme_bank_dlg.h"
#include "scheme_manager.h"
#include "choose_name.h"
#include "curve_edit.h"


#include "nel/3d/ps_attrib_maker.h"
#include "nel/3d/ps_float.h"
#include "nel/3d/ps_int.h"
#include "nel/3d/ps_color.h"
#include "nel/3d/ps_plane_basis.h"
#include "nel/3d/ps_plane_basis_maker.h"




/*static char trace_buf[200];
#define NL_TRACE sprintf(trace_buf, "%d", __LINE__); \
				::MessageBox(NULL, trace_buf, NULL, MB_OK);
*/

/////////////////////////////////////////////////////////////////////
// WRAPPERS to set / retrieve the NbCycles parameter of a scheme   //
/////////////////////////////////////////////////////////////////////
static float NbCyclesReader(void *lParam) { return ((CAttribDlg *) lParam)->getSchemeNbCycles(); }
static void NbCyclesWriter(float value, void *lParam) { ((CAttribDlg *) lParam)->setSchemeNbCycles(value); }



///////////////////////////////////////////
// GENERAL INTERFACE FOR BLENDER EDITION //
///////////////////////////////////////////


/**  T is the type to be edited (color, float, etc..),
 *   even if it is unused
 */

template <typename T> 
class CValueBlenderDlgClientT : public IValueBlenderDlgClient
{
	public:
		std::string Id; // the Id of each of the dialog (it will be followed by %1 or %2)
						 // must be filled by the user
		// the scheme being used. Must be set by the user
		NL3D::CPSValueBlendFuncBase<T> *SchemeFunc;

	protected:
		virtual CEditAttribDlg *createDialog(uint index, CParticleWorkspace::CNode *ownerNode)
		{
			std::string id = Id;
			if (index == 0) id += "%1"; else id += "%2";
			_ValueInfos[index].ValueIndex = index;
			_ValueInfos[index].SchemeFunc = SchemeFunc;
			_ValueInfos[index].OwnerNode = ownerNode;
			return newDialog(id, &_ValueInfos[index]);									
		}


		// construct a dialog with the given wrapper and id
		virtual CEditAttribDlg *newDialog(const std::string &id, IPSWrapper<T> *wrapper) = 0;

		// inherited from IPSWrapper<T>
		struct COneValueInfo : public IPSWrapper<T>
		{
			// value 0 or 1 being edited
			uint ValueIndex;
			// the scheme being edited
			NL3D::CPSValueBlendFuncBase<T> *SchemeFunc;

			virtual T get(void) const 
			{ 
				T t1, t2;
				SchemeFunc->getValues(t1, t2);
				return ValueIndex == 0 ? t1 : t2;
			}			
			virtual void set(const T &value)
			{
				T t1, t2;
				SchemeFunc->getValues(t1, t2);
				if (ValueIndex == 0 ) t1 = value; else t2 = value;
				SchemeFunc->setValues(t1, t2);
			}
		};

		COneValueInfo _ValueInfos[2];
};


////////////////////////////////////////////
// GENERAL INTERFACE FOR GRADIENT EDITION //
////////////////////////////////////////////

/** This template generate an interface that is used with the gradient edition dialog
 *  T is the type to be edited (color, floet, etc..)
 */
 

template <typename T> 
class CValueGradientDlgClientT : public IValueGradientDlgClient, public IPSWrapper<T>
{
public:

	std::string Id; // the Id of each of the dialog (it will be followed by %1 or %2)
						 // must be filled by the user
	// the gradient being edited, must be filled by the instancier
	NL3D::CPSValueGradientFunc<T> *Scheme;
	// the gradient dialog, must be filled by the instancier
	CValueGradientDlg *GradDlg;
	// the difault value for new values creation. Must be filled by the instancier
	T DefaultValue;

	/// a function that can display a value in a gradient, with the given offset. Deriver must define this
	virtual void displayValue(CDC *dc, uint index, sint x, sint y)  = 0;

	
	/// inherited from IPSWrapper
	virtual T get(void) const { return Scheme->getValue(_CurrentEditedIndex); }
	virtual void set(const T &v)
	{
		T *tab = new T[Scheme->getNumValues()];
		Scheme->getValues(tab);
		tab[_CurrentEditedIndex] = v;
		Scheme->setValues(tab, Scheme->getNumValues(), Scheme->getNumStages());
		delete[] tab;
		GradDlg->invalidateGrad();
	}
	
	/** must provide a dialog for the edition of one value (only ONE exist at a time)
	 * \param index the index of the value in the dialog
	 * \grad the dlg that called this method (deriver can ask a redraw then)
	 */
	virtual CEditAttribDlg *createDialog(uint index, CValueGradientDlg *grad, CParticleWorkspace::CNode *ownerNode)
	{					
		OwnerNode = ownerNode;
		_CurrentEditedIndex = index;
		return newDialog(Id, this);
	}

	/// create a new dialog with given id and wrapper
	virtual  CEditAttribDlg *newDialog(const std::string &id, IPSWrapper<T> *wrapper) = 0;
	

	/// a function that can add, remove, or insert a new element in the gradient
	virtual void modifyGradient(TAction action, uint index)
	{
		
		T *tab = new T[Scheme->getNumValues() + 1]; // +1 is for the add / insert case
		Scheme->getValues(tab);

		switch(action)
		{
			case IValueGradientDlgClient::Add:
				tab[Scheme->getNumValues()] = DefaultValue;
				Scheme->setValues(tab, Scheme->getNumValues() + 1, Scheme->getNumStages());
			break;
			case IValueGradientDlgClient::Insert:
				::memmove(tab + (index + 1), tab + index, sizeof(T) * (Scheme->getNumValues() - index));
				tab[index] = DefaultValue;
				Scheme->setValues(tab, Scheme->getNumValues() + 1, Scheme->getNumStages());
			break;
			case IValueGradientDlgClient::Delete:
				::memmove(tab + index, tab + index + 1, sizeof(T) * (Scheme->getNumValues() - index - 1));
				Scheme->setValues(tab, Scheme->getNumValues() - 1, Scheme->getNumStages());
			break;
			case IValueGradientDlgClient::Up:
				nlassert(index > 0);
				std::swap(tab[index], tab[index - 1]);
				Scheme->setValues(tab, Scheme->getNumValues(), Scheme->getNumStages());
			break;
			case IValueGradientDlgClient::Down:
				nlassert(index <  Scheme->getNumValues() - 1);
				std::swap(tab[index], tab[index + 1]);
				Scheme->setValues(tab, Scheme->getNumValues(), Scheme->getNumStages());
			break;
		}

		delete[] tab;
	}		
	virtual uint32 getSchemeSize(void) const { return Scheme->getNumValues(); }

	// get the number of interpolation step
	uint32 getNbSteps(void) const
	{
		return Scheme->getNumStages();
	}

	// set the number of interpolation steps
	void setNbSteps(uint32 value)
	{
		Scheme->setNumStages(value);	
	}

protected:
		// index of the value OF the current dialog that exist
		uint32 _CurrentEditedIndex;	
};



/////////////////////////////////////////////////////////////////////////////
// CAttribDlg dialog


//*************************************************************************************************************
CAttribDlg::CAttribDlg(const std::string &valueID, CParticleWorkspace::CNode *ownerNode, bool enableConstantValue /* = true*/)
	 : _CstValueDlg(NULL),
	   _Node(ownerNode),
	   _FirstDrawing(true),
	   _EnableConstantValue(enableConstantValue),
	   _DisableMemoryScheme(false),
	   _SchemeEditionDlg(NULL), 
	   _NbCycleEnabled(true), 
	   _NbCyclesDlg(NULL), 
	   _ValueID(valueID), 
	   _SrcInputEnabled(true)
{
	//{{AFX_DATA_INIT(CAttribDlg)
	m_AttribName = _T("");
	m_Clamp = FALSE;
	//}}AFX_DATA_INIT	
}


//*************************************************************************************************************
void CAttribDlg::closeEditWindow()
{
	childPopupClosed(NULL);
}


//*************************************************************************************************************
BOOL CAttribDlg::EnableWindow( BOOL bEnable)
{
	if (_CstValueDlg)
	{
		_CstValueDlg->EnableWindow(bEnable);
	}

	if (_NbCyclesDlg)
	{
		_NbCyclesDlg->EnableWindow(bEnable);
	}
	m_UseScheme.EnableWindow(bEnable);
	m_AttrBitmap.EnableWindow(bEnable);

	if (useScheme())
	{
		m_Scheme.EnableWindow(bEnable);
		m_SchemeInput.EnableWindow(hasSchemeCustomInput() ? bEnable : FALSE);
		m_EditScheme.EnableWindow(bEnable);
		m_GetScheme.EnableWindow(bEnable);
		m_PutScheme.EnableWindow(bEnable);
		m_ClampCtrl.EnableWindow(bEnable);
	}
	else
	{		
		m_ClampCtrl.EnableWindow(FALSE);
	}

	UpdateData(FALSE);

	return CDialog::EnableWindow(bEnable);
}

//*************************************************************************************************************
CAttribDlg::~CAttribDlg()	
{
	if (_NbCyclesDlg)
	{
		_NbCyclesDlg->DestroyWindow();
		delete _NbCyclesDlg;
	}
	if (_CstValueDlg)
	{
		_CstValueDlg->DestroyWindow();		
		delete _CstValueDlg;	
	}
}

//*************************************************************************************************************
void CAttribDlg::update()
{
	_FirstDrawing = true;
	if (useScheme())
	{
		
		schemeValueUpdate();
	}
	else
	{
		
		nlassert(_EnableConstantValue);
		cstValueUpdate();
	}
}

//*************************************************************************************************************
void CAttribDlg::init(HBITMAP bitmap, sint x, sint y, CWnd *pParent)
{

	Create(IDD_ATTRIB_DLG, pParent);
	RECT r, ro;
	GetClientRect(&r);
	
	m_AttrBitmap.SendMessage(BM_SETIMAGE, IMAGE_BITMAP, (LPARAM) bitmap);
	MoveWindow(x, y, r.right, r.bottom);

	m_NbCyclePos.GetWindowRect(&r);
	GetWindowRect(&ro);

	if (_NbCycleEnabled)
	{
		_NbCyclesDlg = new CEditableRangeFloat(_ValueID + "%%NB_CYCLE_INFO", _Node, 0.1f, 10.1f);
		_NbCyclesDlg->init(r.left - ro.left, r.top - ro.top, this);
	}

	// fill the combo box with the list of available scheme
	m_Scheme.InitStorage(getNumScheme(), 32); // 32 char per string pre-allocated

	for (uint k = 0; k < getNumScheme(); ++k)
	{	

		m_Scheme.InsertString(k, getSchemeName(k).c_str());

	}

	update();	

	if (!_EnableConstantValue)
	{
		m_UseScheme.ShowWindow(SW_HIDE);
	}

	if (!_NbCyclesDlg)
	{
		GetDlgItem(IDC_INPUT_MULTIPLIER_TXT)->ShowWindow(SW_HIDE);
		GetDlgItem(IDC_CLAMP_ATTRIB)->ShowWindow(SW_HIDE);		
	}

	if (!_SrcInputEnabled)
	{
		GetDlgItem(IDC_SRC_INPUT_TXT)->ShowWindow(SW_HIDE);
		GetDlgItem(IDC_EDIT_INPUT)->ShowWindow(SW_HIDE);
		GetDlgItem(IDC_SCHEME_INPUT)->ShowWindow(SW_HIDE);
	}
	inputValueUpdate();
	ShowWindow(SW_SHOW);
}

//*************************************************************************************************************
void CAttribDlg::DoDataExchange(CDataExchange* pDX)
{
	CDialog::DoDataExchange(pDX);
	//{{AFX_DATA_MAP(CAttribDlg)
	DDX_Control(pDX, IDC_PUT_SCHEME, m_PutScheme);
	DDX_Control(pDX, IDC_GET_SCHEME, m_GetScheme);
	DDX_Control(pDX, IDC_EDIT_INPUT, m_EditUserParam);
	DDX_Control(pDX, IDC_SCHEME_INPUT, m_SchemeInput);
	DDX_Control(pDX, IDC_CONSTANT_VALUE_POS, m_CstValuePos);
	DDX_Control(pDX, IDC_ATTRIB_NB_CYCLES, m_NbCyclePos);
	DDX_Control(pDX, IDC_ATTR_BITMAP, m_AttrBitmap);
	DDX_Control(pDX, IDC_CLAMP_ATTRIB, m_ClampCtrl);
	DDX_Control(pDX, IDC_EDIT_SCHEME, m_EditScheme);
	DDX_Control(pDX, IDC_USE_SCHEME, m_UseScheme);
	DDX_Control(pDX, IDC_SCHEME, m_Scheme);
	DDX_Check(pDX, IDC_CLAMP_ATTRIB, m_Clamp);
	//}}AFX_DATA_MAP
}

//*************************************************************************************************************
void CAttribDlg::inputValueUpdate(void)
{
	if (useScheme() && getSchemeInput().InputType == NL3D::CPSInputType::attrUserParam)
	{
		m_EditUserParam.EnableWindow(TRUE);
	}
	else
	{
		m_EditUserParam.EnableWindow(FALSE);
	}
}

//*************************************************************************************************************
void CAttribDlg::cstValueUpdate()
{
	if (!_FirstDrawing && !useScheme()) return;	

	m_ClampCtrl.EnableWindow(FALSE);
	if (_NbCyclesDlg)
	{
		_NbCyclesDlg->EnableWindow(FALSE);
		_NbCyclesDlg->emptyDialog();
	}
	GetDlgItem(IDC_INPUT_MULTIPLIER_TXT)->EnableWindow(FALSE);
	m_EditScheme.EnableWindow(FALSE);
	m_PutScheme.EnableWindow(FALSE);
	m_GetScheme.EnableWindow(FALSE);

	m_EditScheme.ShowWindow(SW_HIDE);
	m_GetScheme.ShowWindow(SW_HIDE);
	m_PutScheme.ShowWindow(SW_HIDE);

	m_Scheme.EnableWindow(FALSE);
	m_Scheme.ShowWindow(SW_HIDE);
	m_SchemeInput.EnableWindow(FALSE);
	m_SchemeInput.ShowWindow(SW_HIDE);

	if (!_FirstDrawing) resetCstValue();			



	m_UseScheme.SetCurSel(0);
	_CstValueDlg = createConstantValueDlg();
	CRect r, ro;
	m_CstValuePos.GetWindowRect(&r);
	GetWindowRect(&ro);
	_CstValueDlg->init(r.left - ro.left, r.top - ro.top, this);
	UpdateData(FALSE);

	_FirstDrawing = false;
}

//*************************************************************************************************************
void CAttribDlg::schemeValueUpdate()
{
	//if (!_FirstDrawing && useScheme()) return;		
	if (_CstValueDlg)
	{
		_CstValueDlg->DestroyWindow();
		delete _CstValueDlg;
		_CstValueDlg = NULL;
	}	
	m_EditScheme.EnableWindow(TRUE);
	m_GetScheme.EnableWindow(TRUE);
	m_PutScheme.EnableWindow(TRUE);
	m_EditScheme.ShowWindow(SW_SHOW);
	m_GetScheme.ShowWindow(SW_SHOW);
	m_PutScheme.ShowWindow(SW_SHOW);	
	m_Scheme.EnableWindow(TRUE);
	m_Scheme.ShowWindow(SW_SHOW);
	m_SchemeInput.EnableWindow(TRUE);
	m_SchemeInput.ShowWindow(SW_SHOW);
	m_UseScheme.SetCurSel(1);
	sint k = getCurrentScheme();

	if (k == -1) // unknow scheme ...
	{
		setCurrentScheme(0);
		k = 0;
	}
	m_Scheme.SetCurSel(k);
	if (hasSchemeCustomInput())
	{
		m_SchemeInput.EnableWindow();
		m_SchemeInput.SetCurSel((uint) getSchemeInput().InputType);
		inputValueUpdate();
		if (_NbCyclesDlg)
		{
			_NbCyclesDlg->EnableWindow(TRUE);
		}
		m_ClampCtrl.EnableWindow(isClampingSupported());
		GetDlgItem(IDC_INPUT_MULTIPLIER_TXT)->EnableWindow(isClampingSupported());
	}
	else
	{
		m_SchemeInput.EnableWindow(FALSE);
		m_SchemeInput.SetCurSel(0);
		if (_NbCyclesDlg)
		{
			_NbCyclesDlg->EnableWindow(FALSE);
		}
		m_ClampCtrl.EnableWindow(FALSE);
		GetDlgItem(IDC_INPUT_MULTIPLIER_TXT)->EnableWindow(FALSE);
	}	

	if (_NbCyclesDlg)
	{	
		_NbCyclesWrapper.OwnerNode = _Node;
		_NbCyclesDlg->setWrapper(&_NbCyclesWrapper);
		_NbCyclesWrapper.Dlg = this;	
		_NbCyclesDlg->updateRange();
		_NbCyclesDlg->updateValueFromReader();
	}	
	if (isClampingSupported())
	{
		m_Clamp = isSchemeClamped();
	}
	UpdateData(FALSE);
	_FirstDrawing = false;
}

//*************************************************************************************************************
void CAttribDlg::OnSelchangeUseScheme() 
{
	if (m_UseScheme.GetCurSel() == 0)
	{
		cstValueUpdate();
	}
	else
	{
		schemeValueUpdate();
	}
}



//*************************************************************************************************************
void CAttribDlg::OnSelchangeScheme() 
{
	UpdateData();
	setCurrentScheme(m_Scheme.GetCurSel());	
	schemeValueUpdate();
}

//*************************************************************************************************************
void CAttribDlg::OnEditScheme() 
{
	_SchemeEditionDlg  = editScheme();
	if (_SchemeEditionDlg)
	{
		EnableWindow(FALSE);
	}
}

//*************************************************************************************************************
void CAttribDlg::childPopupClosed(CWnd *child)
{
	EnableWindow(TRUE);
	if (!_SchemeEditionDlg) return;
	_SchemeEditionDlg->DestroyWindow();
	delete _SchemeEditionDlg;
	_SchemeEditionDlg = NULL;	
}

//*************************************************************************************************************
void CAttribDlg::OnGetScheme() 
{
	CSchemeBankDlg sbd(getCurrentSchemePtr()->getType(), this);
	if (sbd.DoModal() == IDOK && sbd.getSelectedScheme())
	{
		setCurrentSchemePtr(sbd.getSelectedScheme()->clone());	
		_FirstDrawing = true;
		schemeValueUpdate();
	}
}

//*************************************************************************************************************
void CAttribDlg::OnPutScheme() 
{
	CChooseName cn("new scheme", this);
	if (cn.DoModal() == IDOK)
	{
		SchemeManager.insertScheme(cn.getName(), getCurrentSchemePtr()->clone());
	}
}

//*************************************************************************************************************
void CAttribDlg::OnSelchangeSchemeInput() 
{
	UpdateData();
	NL3D::CPSInputType it;
	it.InputType = (NL3D::CPSInputType::TInputType) m_SchemeInput.GetCurSel(); 
	if (it.InputType == NL3D::CPSInputType::attrUserParam)
	{
		it.UserParamNum = 0;
	}	
	setSchemeInput(it);
	inputValueUpdate();
}

//*************************************************************************************************************
void CAttribDlg::OnClampAttrib() 
{
	UpdateData();
	clampScheme(m_Clamp ? true : false /* avoid performance warning */);
}

//*************************************************************************************************************
void CAttribDlg::OnEditInput() 
{
	switch (getSchemeInput().InputType)
	{
		case NL3D::CPSInputType::attrUserParam:
		{
			CEditUserParam ep(getSchemeInput().UserParamNum);
			if (ep.DoModal() == IDOK)
			{
				NL3D::CPSInputType it =  getSchemeInput();
				it.UserParamNum = ep.getUserParamIndex();
				setSchemeInput(it);
			}
		} 
		break;
	}	
}

BEGIN_MESSAGE_MAP(CAttribDlg, CDialog)
	//{{AFX_MSG_MAP(CAttribDlg)
	ON_CBN_SELCHANGE(IDC_USE_SCHEME, OnSelchangeUseScheme)
	ON_CBN_SELCHANGE(IDC_SCHEME, OnSelchangeScheme)
	ON_BN_CLICKED(IDC_EDIT_SCHEME, OnEditScheme)
	ON_CBN_SELCHANGE(IDC_SCHEME_INPUT, OnSelchangeSchemeInput)
	ON_BN_CLICKED(IDC_CLAMP_ATTRIB, OnClampAttrib)
	ON_BN_CLICKED(IDC_EDIT_INPUT, OnEditInput)
	ON_BN_CLICKED(IDC_GET_SCHEME, OnGetScheme)
	ON_BN_CLICKED(IDC_PUT_SCHEME, OnPutScheme)
	ON_WM_DESTROY()
	ON_WM_CLOSE()
	//}}AFX_MSG_MAP
END_MESSAGE_MAP()

/////////////////////////////////////////////////////////////////////////////
// CAttribDlg message handlers



////////////////////////////////////
// CAttribDlgFloat implementation //
////////////////////////////////////

	//////////////////////////////////////////////////////////
	// FLOAT BLENDER EDITION INTERFACE						//
	//////////////////////////////////////////////////////////
	class CFloatBlenderDlgClient : public CValueBlenderDlgClientT<float>
	{
		public:
			CEditAttribDlg *newDialog(const std::string &id, IPSWrapperFloat *wrapper) 
			{ 
				CEditableRangeFloat *erf = new CEditableRangeFloat(id, wrapper->OwnerNode, MinRange, MaxRange);				
				erf->setWrapper(wrapper);
				BoundChecker.duplicateBoundChecker(*erf);
				return erf;
			}
			CBoundCheckerFloat BoundChecker;
			float MinRange, MaxRange;
	};

	//////////////////////////////////////////////////////////
	// FLOAT GRADIENT EDITION INTERFACE						//
	//////////////////////////////////////////////////////////

	//*************************************************************************************************************
	class CFloatGradientDlgWrapper : public CValueGradientDlgClientT<float>
	{
	public:	
		/// a function that can display a value in a gradient, with the given offset. Deriver must define this
		void displayValue(CDC *dc, uint index, sint x, sint y)
		{		
			
			CString out;
			out.Format("%g",  Scheme->getValue(index) );
			dc->TextOut(x + 10, y + 4, out);
		}
		CEditAttribDlg *newDialog(const std::string &id, IPSWrapperFloat *wrapper) 
		{ 
			CEditableRangeFloat *erf = new CEditableRangeFloat(id, wrapper->OwnerNode, MinRange, MaxRange);
			erf->setWrapper(wrapper);
			BoundChecker.duplicateBoundChecker(*erf);
			return erf;
		}
		CBoundCheckerFloat BoundChecker;
		float MinRange, MaxRange;
	};



	//*************************************************************************************************************
	CAttribDlgFloat::CAttribDlgFloat(const std::string &valueID, CParticleWorkspace::CNode *node, float minRange, float maxRange)
				:  CAttribDlgT<float>(valueID, node), _MinRange(minRange), _MaxRange(maxRange)
	{
		_CstValueId = valueID;			
	}

	//*************************************************************************************************************
	CEditAttribDlg *CAttribDlgFloat::createConstantValueDlg()
	{
		CEditableRangeFloat *erf = new CEditableRangeFloat(_CstValueId, _Node, _MinRange, _MaxRange);
		erf->setWrapper(_Wrapper);		
		duplicateBoundChecker(*erf);
		return erf;
	}

	//*************************************************************************************************************
	uint CAttribDlgFloat::getNumScheme(void) const
	{	
		return _DisableMemoryScheme ? 3 : 5;		
	}

	//*************************************************************************************************************
	std::string CAttribDlgFloat::getSchemeName(uint index) const
	{
		const char *types[] = { "value blender", "values gradient", "curve", "value computed from emitter", "binary operator"};
		nlassert(index < 5);
		return std::string(types[index]);		
	}

	//*************************************************************************************************************
	CWnd *CAttribDlgFloat::editScheme(void)
	{
		NL3D::CPSAttribMaker<float> *scheme = _SchemeWrapper->getScheme();	

		if (dynamic_cast<NL3D::CPSFloatBlender *>(scheme)) 
		{				
			CFloatBlenderDlgClient *myInterface = new CFloatBlenderDlgClient;
			this->duplicateBoundChecker(myInterface->BoundChecker);
			myInterface->MinRange = _MinRange;
			myInterface->MaxRange = _MaxRange;
			myInterface->Id = _CstValueId+ std::string("%%FLOAT_BLENDER");
			myInterface->SchemeFunc = & ((NL3D::CPSValueBlenderSample<float, 64> *) scheme)->_F;			
			CValueBlenderDlg *vb = new CValueBlenderDlg(myInterface, true, this, this, _Node);
			vb->init(this);
			return vb;
					
		}
		if (dynamic_cast<NL3D::CPSFloatGradient *>(scheme)) 
		{
			CFloatGradientDlgWrapper *wrapper = new CFloatGradientDlgWrapper;
			this->duplicateBoundChecker(wrapper->BoundChecker);
			wrapper->MinRange = _MinRange;
			wrapper->MaxRange = _MaxRange;
			wrapper->Scheme = &(((NL3D::CPSFloatGradient *) (_SchemeWrapper->getScheme()) )->_F);
			CValueGradientDlg *gd = new CValueGradientDlg(wrapper, _Node, true, this, this);
			wrapper->GradDlg = gd;
			wrapper->DefaultValue = 0.f;
			wrapper->Id = _CstValueId+ std::string("%%FLOAT GRADIENT");			
			gd->init(this);
			return gd;
		}
		if (dynamic_cast<NL3D::CPSFloatMemory *>(scheme)) 
		{
			CAttribDlgFloat *adf = new CAttribDlgFloat(_CstValueId, _Node, _MinRange, _MaxRange);
			this->duplicateBoundChecker(*adf);
			CValueFromEmitterDlgT<float> *vfe = new CValueFromEmitterDlgT<float>( (NL3D::CPSFloatMemory *)(scheme),
																				  adf,
																				  this,
																				  m_AttrBitmap.GetBitmap()
																				 );			
			vfe->init(this);
			return vfe;
		}
		if (dynamic_cast<NL3D::CPSFloatBinOp *>(scheme)) 
		{
			CAttribDlgFloat *ad[2] = { NULL, NULL};
			for (uint k = 0; k <2; ++k)
			{
				ad[k] = new CAttribDlgFloat(_CstValueId, _Node, _MinRange, _MaxRange);
				this->duplicateBoundChecker(*ad[k]);
			}
			CBinOpDlgT<float> *bod = new CBinOpDlgT<float>( (NL3D::CPSFloatBinOp *)(scheme),
															(CAttribDlgT<float> **) ad,
															this,
															m_AttrBitmap.GetBitmap());	
			bod->init(this);
			return bod;
		}

		if (dynamic_cast<NL3D::CPSFloatCurve *>(scheme)) 
		{
			CurveEdit *ce = new CurveEdit(&(dynamic_cast<NL3D::CPSFloatCurve *>(scheme)->_F), _Node, this, this);
			ce->init(this);
			return ce;			
		}

		return NULL;
	}

	//*************************************************************************************************************
	sint CAttribDlgFloat::getCurrentScheme(void) const
	{

		const NL3D::CPSAttribMaker<float> *scheme = _SchemeWrapper->getScheme();		
		if (dynamic_cast<const NL3D::CPSFloatBlender *>(scheme))  return 0;		
		if (dynamic_cast<const NL3D::CPSFloatGradient *>(scheme)) return 1;		
		if (dynamic_cast<const NL3D::CPSFloatCurve *>(scheme)) return 2;
		if (dynamic_cast<const NL3D::CPSFloatMemory *>(scheme)) return 3;		
		if (dynamic_cast<const NL3D::CPSFloatBinOp *>(scheme)) return 4;
		
		return -1;
	}


	//*************************************************************************************************************
	void CAttribDlgFloat::setCurrentScheme(uint index)
	{
		nlassert(index < 5);


		NL3D::CPSAttribMaker<float> *scheme = NULL;

		switch (index)
		{
			case 0:
				scheme = new NL3D::CPSFloatBlender(_MinRange, _MaxRange);
			break;
			case 1:	
			{
				static const float values[2] = { 0.1f, 1.f };
				scheme = new NL3D::CPSFloatGradient(values, 2, 16, 1.f);	
			}
			break;
			case 2:
			{				
				NL3D::CPSFloatCurve *curve = new NL3D::CPSFloatCurve;
				curve->_F.setNumSamples(128);
				curve->_F.addControlPoint(NL3D::CPSFloatCurveFunctor::CCtrlPoint(0, 0.5f));
				curve->_F.addControlPoint(NL3D::CPSFloatCurveFunctor::CCtrlPoint(1, 0.5f));				
				scheme = curve;
			}
			break;
			case 3:
				scheme = new NL3D::CPSFloatMemory;
				((NL3D::CPSAttribMakerMemory<float> *) scheme)->setScheme(new NL3D::CPSFloatBlender(_MinRange, _MaxRange));
			break;
			case 4 :
				scheme = new NL3D::CPSFloatBinOp;
				((NL3D::CPSFloatBinOp *) scheme)->setArg(0, new NL3D::CPSFloatBlender);
				((NL3D::CPSFloatBinOp *) scheme)->setArg(1, new NL3D::CPSFloatBlender);
			break;

			default:	
			break;
		}

		if (scheme)
		{
			_SchemeWrapper->setSchemeAndUpdateModifiedFlag(scheme);
		}
	}


////////////////////////////////////
// CAttribDlgUInt implementation //
////////////////////////////////////


	class CUIntBlenderDlgClient : public CValueBlenderDlgClientT<uint32>
	{
		public:
			CEditAttribDlg *newDialog(const std::string &id, IPSWrapperUInt *wrapper) 
			{ 
				CEditableRangeUInt *eru = new CEditableRangeUInt(id, wrapper->OwnerNode, MinRange, MaxRange);
				eru->setWrapper(wrapper);
				BoundChecker.duplicateBoundChecker(*eru);
				return eru;
			}
			CBoundCheckerUInt BoundChecker;
			uint32 MinRange, MaxRange;
	};

	//////////////////////////////////////////////////////////
	// UINT GRADIENT EDITION INTERFACE						//
	//////////////////////////////////////////////////////////


	//*************************************************************************************************************
	class CUIntGradientDlgWrapper : public CValueGradientDlgClientT<uint32>
	{
	public:	
		/// a function that can display a value in a gradient, with the given offset. Deriver must define this
		void displayValue(CDC *dc, uint index, sint x, sint y)
		{		
			
			CString out;
			out.Format("%d",  Scheme->getValue(index) );
			dc->TextOut(x + 10, y + 4, out);
		}
		CEditAttribDlg *newDialog(const std::string &id, IPSWrapperUInt *wrapper) 
		{ 
			CEditableRangeUInt *eru = new CEditableRangeUInt(id, wrapper->OwnerNode, MinRange, MaxRange);
			eru->setWrapper(wrapper);
			BoundChecker.duplicateBoundChecker(*eru);
			return eru;
		}
		CBoundCheckerUInt BoundChecker;
		uint32 MinRange, MaxRange;
	};


	//*************************************************************************************************************
	CAttribDlgUInt::CAttribDlgUInt(const std::string &valueID, CParticleWorkspace::CNode *node, uint32 minRange, uint32 maxRange)
				:  CAttribDlgT<uint32>(valueID, node), _MinRange(minRange), _MaxRange(maxRange)			  
	{
		_CstValueId = valueID;			
	}

	//*************************************************************************************************************
	CEditAttribDlg *CAttribDlgUInt::createConstantValueDlg()
	{
		CEditableRangeUInt *erf = new CEditableRangeUInt(_CstValueId, _Node, _MinRange, _MaxRange);
		erf->setWrapper(_Wrapper);
		duplicateBoundChecker(*erf);
		return erf;
	}

	//*************************************************************************************************************
	uint CAttribDlgUInt::getNumScheme(void) const
	{

		return _DisableMemoryScheme ? 2 : 4;
	}

	//*************************************************************************************************************
	std::string CAttribDlgUInt::getSchemeName(uint index) const
	{
		const char *types[] = { "value blender", "values gradient", "values computed from emitter", "binary operator" };
		nlassert(index < 4);
		return std::string(types[index]);		
	}

	//*************************************************************************************************************
	CWnd *CAttribDlgUInt::editScheme(void)
	{
		const NL3D::CPSAttribMaker<uint32> *scheme = _SchemeWrapper->getScheme();	

		if (dynamic_cast<const NL3D::CPSUIntBlender *>(scheme)) 
		{				
			CUIntBlenderDlgClient *myInterface = new CUIntBlenderDlgClient ;
			duplicateBoundChecker(myInterface->BoundChecker);
			myInterface->MinRange = _MinRange;
			myInterface->MaxRange = _MaxRange;
			myInterface->Id = _CstValueId+ std::string("%%UINT_BLENDER");
			myInterface->SchemeFunc = & ((NL3D::CPSValueBlenderSample<uint32, 64> *) scheme)->_F;
			
			CValueBlenderDlg *vb = new CValueBlenderDlg(myInterface, true, this, this, _Node);
			vb->init(this);
			return vb;
		}
		if (dynamic_cast<const NL3D::CPSUIntGradient *>(scheme)) 
		{
			CUIntGradientDlgWrapper *wrapper = new CUIntGradientDlgWrapper;
			this->duplicateBoundChecker(wrapper->BoundChecker);
			wrapper->MinRange = _MinRange;
			wrapper->MaxRange = _MaxRange;
			wrapper->Scheme = &(((NL3D::CPSUIntGradient *) (_SchemeWrapper->getScheme()) )->_F);
			CValueGradientDlg *gd = new CValueGradientDlg(wrapper, _Node, true, this, this);
			wrapper->GradDlg = gd;
			wrapper->DefaultValue = 0;
			wrapper->Id = _CstValueId+ std::string("%%UINT GRADIENT");
			gd->init(this);
			return gd;
		}
		if (dynamic_cast<const NL3D::CPSUIntMemory *>(scheme)) 
		{

			CAttribDlgUInt *adu = new CAttribDlgUInt(_CstValueId, _Node, _MinRange, _MaxRange);
			this->duplicateBoundChecker(*adu);
			CValueFromEmitterDlgT<uint32> *vfe = new CValueFromEmitterDlgT<uint32>( (NL3D::CPSUIntMemory *)(scheme),
																					adu,
																					this,
																					m_AttrBitmap.GetBitmap());			
			vfe->init(this);
			return vfe;
		}
		if (dynamic_cast<const NL3D::CPSUIntBinOp *>(scheme)) 
		{
			CAttribDlgUInt *ad[2] = { NULL, NULL};
			for (uint k = 0; k <2; ++k)
			{
				ad[k] = new CAttribDlgUInt(_CstValueId, _Node, _MinRange, _MaxRange);
				this->duplicateBoundChecker(*ad[k]);
			}
			CBinOpDlgT<uint32> *bod = new CBinOpDlgT<uint32>( (NL3D::CPSUIntBinOp *)(scheme),
																(CAttribDlgT<uint32> **) ad,
																this,
																m_AttrBitmap.GetBitmap());	
			bod->init(this);
			return bod;
		}
		return NULL;
	}

	//*************************************************************************************************************
	sint CAttribDlgUInt::getCurrentScheme(void) const
	{
		const NL3D::CPSAttribMaker<uint32> *scheme = _SchemeWrapper->getScheme();	

		if (dynamic_cast<const NL3D::CPSUIntBlender *>(scheme))  return 0;		
		if (dynamic_cast<const NL3D::CPSUIntGradient *>(scheme)) return 1;		
		if (dynamic_cast<const NL3D::CPSUIntMemory *>(scheme))   return 2;		
		if (dynamic_cast<const NL3D::CPSUIntBinOp *>(scheme))	 return 3;		
		return -1;
	}

	//*************************************************************************************************************
	void CAttribDlgUInt::setCurrentScheme(uint index)
	{
		nlassert(index < 4);


		NL3D::CPSAttribMaker<uint32> *scheme = NULL;

		switch (index)
		{
			case 0 :
				scheme = new NL3D::CPSUIntBlender(_MinRange, _MaxRange);
			break;
			case 1 :
				scheme = new NL3D::CPSUIntGradient;
			break;
			case 2 :
				scheme = new NL3D::CPSUIntMemory;
				((NL3D::CPSAttribMakerMemory<uint32> *) scheme)->setScheme(new NL3D::CPSUIntBlender(_MinRange, _MaxRange) );
			break;
			case 3 :
				scheme = new NL3D::CPSUIntBinOp;
				((NL3D::CPSUIntBinOp *) scheme)->setArg(0, new NL3D::CPSUIntBlender);
				((NL3D::CPSUIntBinOp *) scheme)->setArg(1, new NL3D::CPSUIntBlender);
			break;
			default:	
			break;
		}

		if (scheme)
		{
			_SchemeWrapper->setSchemeAndUpdateModifiedFlag(scheme);
		}
	}


////////////////////////////////////
// CAttribDlgInt implementation //
////////////////////////////////////

	class CIntBlenderDlgClient : public CValueBlenderDlgClientT<sint32>
	{
		public:
			CEditAttribDlg *newDialog(const std::string &id, IPSWrapper<sint32> *wrapper) 
			{ 
				CEditableRangeInt *eri = new CEditableRangeInt(id, wrapper->OwnerNode, MinRange, MaxRange);
				eri->setWrapper(wrapper);
				BoundChecker.duplicateBoundChecker(*eri);
				return eri;
			}
			CBoundCheckerInt BoundChecker;
			sint32 MinRange, MaxRange;
	};


	//////////////////////////////////////////////////////////
	// INT GRADIENT EDITION INTERFACE						//
	//////////////////////////////////////////////////////////


	//*************************************************************************************************************
	class CIntGradientDlgWrapper : public CValueGradientDlgClientT<sint32>
	{
	public:	
		/// a function that can display a value in a gradient, with the given offset. Deriver must define this
		void displayValue(CDC *dc, uint index, sint x, sint y)
		{		
			
			CString out;
			out.Format("%d",  Scheme->getValue(index) );
			dc->TextOut(x + 10, y + 4, out);
		}
		CEditAttribDlg *newDialog(const std::string &id, IPSWrapper<sint32> *wrapper) 
		{ 
			CEditableRangeInt *eri = new CEditableRangeInt(id, wrapper->OwnerNode, MinRange, MaxRange);
			eri->setWrapper(wrapper);
			BoundChecker.duplicateBoundChecker(*eri);
			return eri;
		}
		CBoundCheckerInt BoundChecker;
		sint32 MinRange, MaxRange;
	};


	//*************************************************************************************************************
	CAttribDlgInt::CAttribDlgInt(const std::string &valueID, CParticleWorkspace::CNode *node, sint32 minRange, sint32 maxRange)
				:  CAttribDlgT<sint32>(valueID, node), _MinRange(minRange), _MaxRange(maxRange)			  
	{
		_CstValueId = valueID;		
	}

	//*************************************************************************************************************
	CEditAttribDlg *CAttribDlgInt::createConstantValueDlg()
	{
		CEditableRangeInt *erf = new CEditableRangeInt(_CstValueId, _Node, _MinRange, _MaxRange);
		erf->setWrapper(_Wrapper);
		duplicateBoundChecker(*erf);
		return erf;
	}

	//*************************************************************************************************************
	uint CAttribDlgInt::getNumScheme(void) const
	{
		return _DisableMemoryScheme ? 2 : 4;
	}

	//*************************************************************************************************************
	std::string CAttribDlgInt::getSchemeName(uint index) const
	{

		const char *types[] = { "value exact blender", "values gradient", "values computed from emitter", "binary operator" };
		nlassert(index < 4);
		return std::string(types[index]);
	}

	//*************************************************************************************************************
	CWnd *CAttribDlgInt::editScheme(void)
	{
		const NL3D::CPSAttribMaker<sint32> *scheme = _SchemeWrapper->getScheme();	

		if (dynamic_cast<const NL3D::CPSIntBlender *>(scheme)) 
		{				
			CIntBlenderDlgClient *myInterface = new CIntBlenderDlgClient;
			this->duplicateBoundChecker(myInterface->BoundChecker);
			myInterface->MinRange = _MinRange;
			myInterface->MaxRange = _MaxRange;
			myInterface->Id = _CstValueId+ std::string("%%INT_BLENDER");
			myInterface->SchemeFunc = & ((NL3D::CPSValueBlenderSample<sint32, 64> *) scheme)->_F;
			
			CValueBlenderDlg *vb = new CValueBlenderDlg(myInterface, true, this, this, _Node);
			vb->init(this);
			return vb;
		}
		if (dynamic_cast<const NL3D::CPSIntGradient *>(scheme)) 
		{
			CIntGradientDlgWrapper *wrapper = new CIntGradientDlgWrapper;
			this->duplicateBoundChecker(wrapper->BoundChecker);
			wrapper->MinRange = _MinRange;
			wrapper->MaxRange = _MaxRange;
			wrapper->Scheme = &(((NL3D::CPSIntGradient *) (_SchemeWrapper->getScheme()) )->_F);
			CValueGradientDlg *gd = new CValueGradientDlg(wrapper, _Node, true, this, this);		
			wrapper->GradDlg = gd;
			wrapper->DefaultValue = 0;
			wrapper->Id = _CstValueId+ std::string("%%INT GRADIENT");
			gd->init(this);
			return gd;						
		}
		if (dynamic_cast<const NL3D::CPSIntMemory *>(scheme)) 
		{
			CAttribDlgInt *adi = new CAttribDlgInt(_CstValueId, _Node, _MinRange, _MaxRange);
			this->duplicateBoundChecker(*adi);
			CValueFromEmitterDlgT<sint32> *vfe = new CValueFromEmitterDlgT<sint32>((NL3D::CPSIntMemory *) _SchemeWrapper->getScheme(),
																			adi, 
																			this,
																			m_AttrBitmap.GetBitmap() );			
			vfe->init(this);
			return vfe;
		}
	/*	if (dynamic_cast<const NL3D::CPSIntMemory *>(scheme)) 
		{
			CValueFromEmitterDlgT<float, CAttribDlgFloat> vfe( (NL3D::CPSFloatMemory *)(scheme), std::string("UINT SCHEME"), m_AttrBitmap.GetBitmap());			
			vfe.DoModal();
		}*/
		if (dynamic_cast<const NL3D::CPSIntBinOp *>(scheme)) 
		{
			CAttribDlgInt *ad[2] = { NULL, NULL};
			for (uint k = 0; k <2; ++k)
			{
				ad[k] = new CAttribDlgInt(_CstValueId, _Node, _MinRange, _MaxRange);
				this->duplicateBoundChecker(*ad[k]);
			}
			CBinOpDlgT<sint32> *bod = new CBinOpDlgT<sint32>( (NL3D::CPSIntBinOp *)(scheme),
															  (CAttribDlgT<sint32> **) ad,
															  this,
															  m_AttrBitmap.GetBitmap());	
			bod->init(this);
			return bod;
		}
		return NULL;
	}

	//*************************************************************************************************************
	sint CAttribDlgInt::getCurrentScheme(void) const
	{
		const NL3D::CPSAttribMaker<sint32> *scheme = _SchemeWrapper->getScheme();	

		if (dynamic_cast<const NL3D::CPSIntBlender *>(scheme)) return 0;		
		if (dynamic_cast<const NL3D::CPSIntGradient *>(scheme)) return 1;		
		if (dynamic_cast<const NL3D::CPSIntMemory *>(scheme)) return 2;		
		if (dynamic_cast<const NL3D::CPSIntBinOp *>(scheme)) return 3;		
		return -1;
	}


	//*************************************************************************************************************
	void CAttribDlgInt::setCurrentScheme(uint index)
	{
		nlassert(index < 4);


		NL3D::CPSAttribMaker<sint32> *scheme = NULL;

		switch (index)
		{
			case 0 :
				scheme = new NL3D::CPSIntBlender;
			break;
			case 1 :
				scheme = new NL3D::CPSIntGradient;
			break;
			case 2 :
				scheme = new NL3D::CPSIntMemory;
				((NL3D::CPSAttribMakerMemory<sint32> *) scheme)->setScheme(new NL3D::CPSIntBlender(_MinRange, _MaxRange)); 
			break;
			case 3 :
				scheme = new NL3D::CPSIntBinOp;
				((NL3D::CPSIntBinOp *) scheme)->setArg(0, new NL3D::CPSIntBlender);
				((NL3D::CPSIntBinOp *) scheme)->setArg(1, new NL3D::CPSIntBlender);
			break;
			default:	
			break;
		}

		if (scheme)
		{
			_SchemeWrapper->setSchemeAndUpdateModifiedFlag(scheme);
		}
	}




///////////////////////
// CRGBA attributes  //
///////////////////////
	
	class CRGBABlenderDlgClient : public CValueBlenderDlgClientT<NLMISC::CRGBA>
	{
		public:
			CEditAttribDlg *newDialog(const std::string &id, IPSWrapper<NLMISC::CRGBA> *wrapper) 
			{ 
				CColorEdit *ce = new CColorEdit;
				ce->setWrapper(wrapper);
				return ce;
			}
	};


	//////////////////////////////////////////////////////////
	// COLOR GRADIENT EDITION INTERFACE						//
	//////////////////////////////////////////////////////////


	//*************************************************************************************************************
	class CColorGradientDlgWrapper : public CValueGradientDlgClientT<CRGBA>
	{
	public:	
		/// a function that can display a value in a gradient, with the given offset. Deriver must define this
		void displayValue(CDC *dc, uint index, sint x, sint y)
		{		
			CRGBA col = Scheme->getValue(index);

			RECT r;

			r.left = x + 10;
			r.top = y + 10;
			r.right = x + 53;
			r.bottom = y + 29;

			CBrush b;
			b.CreateSolidBrush(RGB(col.R, col.G, col.B));
			dc->FillRect(&r, &b);	
			b.DeleteObject();

			b.CreateSolidBrush(RGB(0, 0, 0));
			CGdiObject *old = dc->SelectObject(&b);	
			r.top = y + 10; r. bottom = y + 29;
			r.right = x + 53; r.left = x + 10;
			dc->FrameRect(&r, &b);
			dc->SelectObject(old);
			b.DeleteObject();
		}
		CEditAttribDlg *newDialog(const std::string &id, IPSWrapper<NLMISC::CRGBA> *wrapper) 
		{ 
			CColorEdit *ce = new CColorEdit;
			ce->setWrapper(wrapper);
			return ce;
		}
	};



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

	//*************************************************************************************************************
	CAttribDlgRGBA::CAttribDlgRGBA(const std::string &valueID, CParticleWorkspace::CNode *node)  : CAttribDlgT<CRGBA>(valueID, node)
	{
	}

	//*************************************************************************************************************
	uint CAttribDlgRGBA::getNumScheme(void) const
	{
		return _DisableMemoryScheme ? 3 : 5;
	}

	//*************************************************************************************************************
	std::string CAttribDlgRGBA::getSchemeName(uint index) const
	{
		const char *types[] = { "color sampled blender", "color gradient", "color exact blender", "values computed from emitter", "binary operator" };
		nlassert(index < 5);
		return std::string(types[index]);
	}


	//*************************************************************************************************************
	CWnd *CAttribDlgRGBA::editScheme(void)
	{	
		const NL3D::CPSAttribMaker<CRGBA> *scheme = _SchemeWrapper->getScheme();	

		if (dynamic_cast<const NL3D::CPSColorBlender *>(scheme)) 
		{			
			CRGBABlenderDlgClient *myInterface = new CRGBABlenderDlgClient;
			myInterface->Id = std::string("RGBA_BLENDER");
			myInterface->SchemeFunc = & ((NL3D::CPSValueBlenderSample<CRGBA, 64> *) scheme)->_F;			
			CValueBlenderDlg *vb = new CValueBlenderDlg(myInterface, true, this, this, _Node);
			vb->init(this);
			return vb;
		}
		if (dynamic_cast<const NL3D::CPSColorGradient *>(scheme)) 
		{
			CColorGradientDlgWrapper *wrapper = new CColorGradientDlgWrapper;
			wrapper->Scheme = &(((NL3D::CPSColorGradient *) (_SchemeWrapper->getScheme()) )->_F);
			CValueGradientDlg *gd = new CValueGradientDlg(wrapper, _Node, true, this, this);		
			wrapper->GradDlg = gd;
			wrapper->DefaultValue = CRGBA::White;
			wrapper->Id = std::string("RGBA_GRADIENT");
			gd->init(this);
			return gd;			
		}
		if (dynamic_cast<const NL3D::CPSColorBlenderExact *>(scheme)) 
		{
			return NULL;
		}

		if (dynamic_cast<const NL3D::CPSColorMemory *>(scheme)) 
		{
			CAttribDlgRGBA *ad = new CAttribDlgRGBA(_CstValueId, _Node);			
			CValueFromEmitterDlgT<CRGBA> *vfe = new CValueFromEmitterDlgT<CRGBA>( (NL3D::CPSColorMemory *)(scheme),
																					ad,
																					this,
																					m_AttrBitmap.GetBitmap());			
			vfe->init(this);
			return vfe;
		}

		if (dynamic_cast<const NL3D::CPSColorBinOp *>(scheme)) 
		{
			CAttribDlgRGBA *ad[2] = { NULL, NULL};
			for (uint k = 0; k <2; ++k)
			{
				ad[k] = new CAttribDlgRGBA(_CstValueId, _Node);	
			}
			CBinOpDlgT<CRGBA> *bod = new CBinOpDlgT<CRGBA>( (NL3D::CPSColorBinOp *)(scheme),
															(CAttribDlgT<CRGBA> **) ad,
															this,
															m_AttrBitmap.GetBitmap());	
			bod->init(this);
			return bod;
		}		
		return NULL;
	}

	//*************************************************************************************************************
	void CAttribDlgRGBA::setCurrentScheme(uint index)
	{
		nlassert(index < 5);

		NL3D::CPSAttribMaker<CRGBA> *scheme = NULL;

		switch (index)
		{
			case 0 :
				scheme = new NL3D::CPSColorBlender;
			break;
			case 1 :
				scheme = new NL3D::CPSColorGradient(NL3D::CPSColorGradient::_DefaultGradient, 2, 16, 1.f);
			break;
			case 2 :
				scheme = new NL3D::CPSColorBlenderExact;
			break;
			case 3 :
				scheme = new NL3D::CPSColorMemory;
				((NL3D::CPSAttribMakerMemory<CRGBA> *) scheme)->setScheme(new NL3D::CPSColorBlender);
			break;
			case 4 :
				scheme = new NL3D::CPSColorBinOp;
				((NL3D::CPSColorBinOp *) scheme)->setArg(0, new NL3D::CPSColorBlender);
				((NL3D::CPSColorBinOp *) scheme)->setArg(1, new NL3D::CPSColorBlender);
			break;
			default:	
			break;
		}

		if (scheme)
		{
			_SchemeWrapper->setSchemeAndUpdateModifiedFlag(scheme);
		}
	}

	//*************************************************************************************************************
	sint CAttribDlgRGBA::getCurrentScheme(void) const
	{
		const NL3D::CPSAttribMaker<CRGBA> *scheme = _SchemeWrapper->getScheme();	

		if (dynamic_cast<const NL3D::CPSColorBlender *>(scheme)) return 0;		
		if (dynamic_cast<const NL3D::CPSColorGradient *>(scheme)) return 1;		
		if (dynamic_cast<const NL3D::CPSColorBlenderExact *>(scheme)) return 2;		
		if (dynamic_cast<const NL3D::CPSColorMemory *>(scheme)) return 3;		
		if (dynamic_cast<const NL3D::CPSColorBinOp *>(scheme)) return 4;		
		return -1;
	}

	//*************************************************************************************************************
	CEditAttribDlg *CAttribDlgRGBA::createConstantValueDlg()
	{
		CColorEdit *ce = new CColorEdit;
		ce->setWrapper(_Wrapper);
	
		return ce;
	}



/////////////////////////////
// plane basis attributes  //
/////////////////////////////

	//////////////////////////////////////////////////////////
	// PLANE BASIS GRADIENT EDITION INTERFACE				//
	//////////////////////////////////////////////////////////


	class CPlaneBasisGradientDlgWrapper : public CValueGradientDlgClientT<NL3D::CPlaneBasis>
	{
	public:	
		/// a function that can display a value in a gradient, with the given offset. Deriver must define this
		void displayValue(CDC *dc, uint index, sint x, sint y)
		{		
				
			NLMISC::CRGBA c1[] ={ NLMISC::CRGBA::Black, NLMISC::CRGBA::Black, NLMISC::CRGBA::Black };
			NLMISC::CRGBA c2[] ={ NLMISC::CRGBA::Green, NLMISC::CRGBA::Green, NLMISC::CRGBA::Red };

		
			// read plane basis
			NL3D::CPlaneBasis pb =  Scheme->getValue(index);

			CPoint center(x + 20, y + 25);
			
			NLMISC::CMatrix m;			
			m.setRot(pb.X, pb.Y, pb.X ^ pb.Y);
			DrawBasisInDC(center, 12, m, *dc, c2);
		
		}
		CEditAttribDlg *newDialog(const std::string &id, IPSWrapper<NL3D::CPlaneBasis> *wrapper) 
		{ 
			CBasisEdit *be = new CBasisEdit;
			be->setWrapper(wrapper);
			return be;
		}
	};
	
	//*************************************************************************************************************
	CAttribDlgPlaneBasis::CAttribDlgPlaneBasis(const std::string &valueID, CParticleWorkspace::CNode *node)  : CAttribDlgT<NL3D::CPlaneBasis>(valueID, node)
	{
	}

	uint CAttribDlgPlaneBasis::getNumScheme(void) const
	{
		return _DisableMemoryScheme ? 3 : 5;
	}

	std::string CAttribDlgPlaneBasis::getSchemeName(uint index) const
	{
		const char *types[] = { "basis gradient", "follow path", "spinner", "values computed from emitter", "binary operator" };
		nlassert(index < 5);
		return std::string(types[index]);		
	}

	//*************************************************************************************************************
	CWnd *CAttribDlgPlaneBasis::editScheme(void)
	{	
		NL3D::CPSAttribMaker<NL3D::CPlaneBasis> *scheme = _SchemeWrapper->getScheme();	
		if (dynamic_cast<NL3D::CPSPlaneBasisGradient *>(scheme)) 
		{
			CPlaneBasisGradientDlgWrapper *wrapper = new CPlaneBasisGradientDlgWrapper;
			wrapper->Scheme = &(((NL3D::CPSPlaneBasisGradient *) (_SchemeWrapper->getScheme()) )->_F);
			CValueGradientDlg *gd = new CValueGradientDlg(wrapper, _Node, true, this, this);		
			wrapper->GradDlg = gd;
			wrapper->DefaultValue = NL3D::CPlaneBasis(NLMISC::CVector::K);
			wrapper->Id = std::string("PLANE_BASIS_GRADIENT");
			gd->init(this);
			return gd;			
		}

		if (dynamic_cast<NL3D::CPSPlaneBasisFollowSpeed *>(scheme)) 
		{
			CEditFollowPath *efp = new CEditFollowPath((NL3D::CPSPlaneBasisFollowSpeed *) scheme, _Node, this, this);
			efp->init(this);
			return efp;
		}

		if (dynamic_cast<NL3D::CPSPlaneBasisMemory *>(scheme)) 
		{
			CAttribDlgPlaneBasis *ad = new CAttribDlgPlaneBasis(_CstValueId, _Node);			
			CValueFromEmitterDlgT<NL3D::CPlaneBasis> *vfe = new CValueFromEmitterDlgT<NL3D::CPlaneBasis>
															( (NL3D::CPSPlaneBasisMemory *)(scheme),
															  ad,
															  this,
															  m_AttrBitmap.GetBitmap());			
			vfe->init(this);
			return vfe;
		}

		if (dynamic_cast<NL3D::CPSPlaneBasisBinOp *>(scheme)) 
		{
			CAttribDlgPlaneBasis *ad[2] = { NULL, NULL};
			for (uint k = 0; k <2; ++k)
			{
				ad[k] = new CAttribDlgPlaneBasis(_CstValueId, _Node);				
			}
			CBinOpDlgT<NL3D::CPlaneBasis> *bod = new CBinOpDlgT<NL3D::CPlaneBasis>( (NL3D::CPSPlaneBasisBinOp *)(scheme),
																					(CAttribDlgT<NL3D::CPlaneBasis> **) ad,
																					this,
																					m_AttrBitmap.GetBitmap());	
			bod->init(this);
			return bod;
		}
		
		if (dynamic_cast<NL3D::CPSBasisSpinner *>(scheme)) 
		{
			CEditSpinner *es = new CEditSpinner(static_cast<NL3D::CPSBasisSpinner *>(scheme), _Node, this, this);
			es->init(this);
			return es;
		}
		return NULL;
	}

	//*************************************************************************************************************
	void CAttribDlgPlaneBasis::setCurrentScheme(uint index)
	{
		nlassert(index < 5);

		NL3D::CPSAttribMaker<NL3D::CPlaneBasis> *scheme = NULL;

		switch (index)
		{	
			case 0:	
				scheme = new NL3D::CPSPlaneBasisGradient;
			break;
			case 1:	
				scheme = new NL3D::CPSPlaneBasisFollowSpeed;
			break;
			case 2:
				scheme = new NL3D::CPSBasisSpinner;
				static_cast<NL3D::CPSBasisSpinner *>(scheme)->_F.setNumSamples(16);
			break;
			case 3:	
				scheme = new NL3D::CPSPlaneBasisMemory;
				((NL3D::CPSAttribMakerMemory<NL3D::CPlaneBasis> *) scheme)->setScheme(new NL3D::CPSPlaneBasisFollowSpeed);
				if (_Node)
				{
					_Node->setModified(true);
				}
			break;
			case 4 :
				scheme = new NL3D::CPSPlaneBasisBinOp;
				((NL3D::CPSPlaneBasisBinOp *) scheme)->setArg(0, new NL3D::CPSPlaneBasisFollowSpeed);
				((NL3D::CPSPlaneBasisBinOp *) scheme)->setArg(1, new NL3D::CPSPlaneBasisFollowSpeed);
			break;			
			default:	
			break;
		}

		if (scheme)
		{
			_SchemeWrapper->setSchemeAndUpdateModifiedFlag(scheme);
		}
	}

	//*************************************************************************************************************
	sint CAttribDlgPlaneBasis::getCurrentScheme(void) const
	{
		const NL3D::CPSAttribMaker<NL3D::CPlaneBasis> *scheme = _SchemeWrapper->getScheme();	

		if (dynamic_cast<const NL3D::CPSPlaneBasisGradient *>(scheme)) return 0;		
		if (dynamic_cast<const NL3D::CPSPlaneBasisFollowSpeed *>(scheme)) return 1;		
		if (dynamic_cast<const NL3D::CPSBasisSpinner *>(scheme)) return 2;		
		if (dynamic_cast<const NL3D::CPSPlaneBasisMemory *>(scheme)) return 3;		
		if (dynamic_cast<const NL3D::CPSPlaneBasisBinOp *>(scheme)) return 4;		
		
		return -1;
	}

	//*************************************************************************************************************
	CEditAttribDlg *CAttribDlgPlaneBasis::createConstantValueDlg()
	{
		CBasisEdit *ce = new CBasisEdit;
		ce->setWrapper(_Wrapper);
		return ce;
	}

	//*************************************************************************************************************
	void CAttribDlg::OnDestroy() 
	{
		if (_SchemeEditionDlg)
		{
			CWnd *wnd = _SchemeEditionDlg;
			_SchemeEditionDlg = NULL;
			wnd ->DestroyWindow();
			delete wnd ;		
		}
		CDialog::OnDestroy();
			
	}

	//*************************************************************************************************************
	void CAttribDlg::OnClose() 
	{	
		CDialog::OnClose();
	}