#ifndef __EXPCTRL__H
#define __EXPCTRL__H

#include "ctrl.h"
#include "units.h"
#include "exprlib.h"

#define EXPR_POS_CONTROL_CNAME		GetString(IDS_DB_POSITION_EXPR)
#define EXPR_P3_CONTROL_CNAME		GetString(IDS_DB_POINT3_EXPR)
#define EXPR_FLOAT_CONTROL_CNAME	GetString(IDS_DB_FLOAT_EXPR)
#define EXPR_SCALE_CONTROL_CNAME	GetString(IDS_DB_SCALE_EXPR)
#define EXPR_ROT_CONTROL_CNAME		GetString(IDS_DB_ROTATION_EXPR)

#define EXPR_TICKS		0
#define EXPR_SECS		1
#define EXPR_FRAMES		2
#define EXPR_NTIME		3

class ExprControl;

// for the debug floater window
class ExprDebug : public TimeChangeCallback {
public:
	HWND hWnd;
	ExprControl *ec;
	TimeValue t;
	static int winX, winY;

	ExprDebug(HWND hParent, ExprControl *exprControl);
	~ExprDebug();

	void Invalidate();
	void SetTime(TimeValue tm)	{ t = tm; }
	void Update();
	void Init(HWND hWnd);

	void TimeChanged(TimeValue t)	{ SetTime(t); Update(); }
};

// scalar variables
class SVar {
public:
	TSTR	name;
	int		regNum;	// register number variable is assigned to
	int		refID;	// < 0 means constant
	float	val;	// value, if constant
	int		subNum;
	int		offset;	// tick offset
};

class VVar {
public:
	TSTR	name;
	int		regNum;	// reg num this var is assigned to
	int		refID;	// < 0 means constant
	Point3	val;	// value, if const
	int 	subNum;
	int		offset;	// tick offset
};

MakeTab(SVar);
MakeTab(VVar);

class VarRef {
public:
	VarRef()	{ client = NULL; refCt = 0; }
	VarRef(ReferenceTarget *c)	{ client = c; refCt = 1; }
	ReferenceTarget *client;
	int				refCt;
};

MakeTab(VarRef);

class ExprControl : public StdControl 
{
public:
	int			type;
	Expr		expr;
	int			timeSlots[4];
	int			sRegCt;
	int			vRegCt;
	int			curIndex;
	Point3		curPosVal;
	float		curFloatVal;
	Interval	ivalid;
	Interval	range;
	HWND		hParams;
	IObjParam *	ip;
	SVarTab		sVars;
	VVarTab		vVars;
	VarRefTab	refTab;
	TSTR		desc;
	HFONT		hFixedFont;
	HWND		hDlg;
	ExprDebug	*edbg;
	static int	winX, winY;

	void	updRegCt(int val, int type);
	BOOL	dfnVar(int type, TCHAR *buf, int slot, int offset);
	int		getVarCount(int type) { return type == SCALAR_VAR ? sVars.Count() : vVars.Count(); }
	TCHAR * getVarName(int type, int i);
	int		getVarOffset(int type, int i);
	int		getRegNum(int type, int i);
	float	getScalarValue(int i);
	Point3	getVectorValue(int i);
	BOOL	assignScalarValue(int i, float val);
	BOOL	assignVectorValue(int i, Point3 &val);
	BOOL	assignController(int type, int i, ReferenceTarget *client, int subNum);
	void	deleteAllVars();

	ExprControl(int type, ExprControl &ctrl);
	ExprControl(int type, BOOL loading);
	~ExprControl();

	// Animatable methods
	int TrackParamsType() { return TRACKPARAMS_WHOLE; }
	
	void DeleteThis() { delete this; }
	int IsKeyable() { return 0; }		
	BOOL IsAnimated() {return TRUE;}
	Interval GetTimeRange(DWORD flags) { return range; }
	void EditTimeRange(Interval range,DWORD flags);
	void Hold();
	void MapKeys( TimeMap *map, DWORD flags );

	void BeginEditParams( IObjParam *ip, ULONG flags,Animatable *prev );
	void EndEditParams( IObjParam *ip, ULONG flags,Animatable *next );

	void EditTrackParams(
			TimeValue t,	// The horizontal position of where the user right clicked.
			ParamDimensionBase *dim,
			TCHAR *pname,
			HWND hParent,
			IObjParam *ip,
			DWORD flags);

	// Reference methods
	int NumRefs() { return StdControl::NumRefs() + refTab.Count(); }
	ReferenceTarget* GetReference(int i);
	void SetReference(int i, RefTargetHandle rtarg);
	RefResult NotifyRefChanged(Interval, RefTargetHandle, PartID&, RefMessage);
	void RefDeleted();

	IOResult Save(ISave *isave);
	IOResult Load(ILoad *iload);

	// Control methods
	void Copy(Control *from);
	RefTargetHandle Clone(RemapDir& remap=NoRemap()) { 
		ExprControl *ctrl = new ExprControl(this->type, *this);
		CloneControl(ctrl,remap);
		return ctrl; 
		}		
	BOOL IsLeaf() { return TRUE; }
	void GetValueLocalTime(TimeValue t, void *val, Interval &valid, GetSetMethod method=CTRL_ABSOLUTE);	
	void SetValueLocalTime(TimeValue t, void *val, int commit, GetSetMethod method) {}
	void Extrapolate(Interval range,TimeValue t,void *val,Interval &valid,int type);
	void *CreateTempValue();
	void DeleteTempValue(void *val);
	void ApplyValue(void *val, void *delta);
	void MultiplyValue(void *val, float m);

	void GetAbsoluteControlValue(INode *node,TimeValue t,Point3 *pt,Interval &iv);
};


class ExprPosControl : public ExprControl 
{
public:
	ExprPosControl(ExprPosControl &ctrl) : ExprControl(EXPR_POS_CONTROL_CLASS_ID, ctrl) {}
	ExprPosControl(BOOL loading=FALSE) : ExprControl(EXPR_POS_CONTROL_CLASS_ID, loading) {}
	~ExprPosControl() {}

	RefTargetHandle Clone(RemapDir& remap=NoRemap()) { return(new ExprPosControl(*this)); }		
	void GetClassName(TSTR& s) { s = EXPR_POS_CONTROL_CNAME; }
	Class_ID ClassID() { return Class_ID(EXPR_POS_CONTROL_CLASS_ID,0); }  
	SClass_ID SuperClassID() { return CTRL_POSITION_CLASS_ID; }  		
};

class ExprP3Control : public ExprControl 
{
public:
	ExprP3Control(ExprP3Control &ctrl) : ExprControl(EXPR_P3_CONTROL_CLASS_ID, ctrl) {}
	ExprP3Control(BOOL loading=FALSE) : ExprControl(EXPR_P3_CONTROL_CLASS_ID, loading) {}
	~ExprP3Control() {}

	RefTargetHandle Clone(RemapDir& remap=NoRemap()) { return(new ExprP3Control(*this)); }		
	void GetClassName(TSTR& s) { s = EXPR_P3_CONTROL_CNAME; }
	Class_ID ClassID() { return Class_ID(EXPR_P3_CONTROL_CLASS_ID,0); }  
	SClass_ID SuperClassID() { return CTRL_POINT3_CLASS_ID; }  		
};

class ExprFloatControl : public ExprControl 
{
public:
	ExprFloatControl(ExprFloatControl &ctrl) : ExprControl(EXPR_FLOAT_CONTROL_CLASS_ID, ctrl) {}
	ExprFloatControl(BOOL loading=FALSE) : ExprControl(EXPR_FLOAT_CONTROL_CLASS_ID, loading) {}
	~ExprFloatControl() {}

	RefTargetHandle Clone(RemapDir& remap=NoRemap()) { return(new ExprFloatControl(*this)); }		
	void GetClassName(TSTR& s) { s = EXPR_FLOAT_CONTROL_CNAME; }
	Class_ID ClassID() { return Class_ID(EXPR_FLOAT_CONTROL_CLASS_ID,0); }  
	SClass_ID SuperClassID() { return CTRL_FLOAT_CLASS_ID; }  		
};

class ExprScaleControl : public ExprControl 
{
public:
	ExprScaleControl(ExprScaleControl &ctrl) : ExprControl(EXPR_SCALE_CONTROL_CLASS_ID, ctrl) {}
	ExprScaleControl(BOOL loading=FALSE) : ExprControl(EXPR_SCALE_CONTROL_CLASS_ID, loading) {}
	~ExprScaleControl() {}

	RefTargetHandle Clone(RemapDir& remap=NoRemap()) { return(new ExprScaleControl(*this)); }		
	void GetClassName(TSTR& s) { s = EXPR_SCALE_CONTROL_CNAME; }
	Class_ID ClassID() { return Class_ID(EXPR_SCALE_CONTROL_CLASS_ID,0); }  
	SClass_ID SuperClassID() { return CTRL_SCALE_CLASS_ID; }  		
};

class ExprRotControl : public ExprControl 
{
public:
	ExprRotControl(ExprRotControl &ctrl) : ExprControl(EXPR_ROT_CONTROL_CLASS_ID, ctrl) {}
	ExprRotControl(BOOL loading=FALSE) : ExprControl(EXPR_ROT_CONTROL_CLASS_ID, loading) {}
	~ExprRotControl() {}

	RefTargetHandle Clone(RemapDir& remap=NoRemap()) { return(new ExprRotControl(*this)); }		
	void GetClassName(TSTR& s) { s = EXPR_ROT_CONTROL_CNAME; }
	Class_ID ClassID() { return Class_ID(EXPR_ROT_CONTROL_CLASS_ID,0); }  
	SClass_ID SuperClassID() { return CTRL_ROTATION_CLASS_ID; }  		
};

//********************************************************
// EXPRESSION CONTROL
//********************************************************
static Class_ID exprPosControlClassID(EXPR_POS_CONTROL_CLASS_ID,0); 
class ExprPosClassDesc:public ClassDesc 
{
	public:
	int 			IsPublic() { return 1; }
	void *			Create(BOOL loading) { return new ExprPosControl(loading); }
	const TCHAR *	ClassName() { return EXPR_POS_CONTROL_CNAME; }
	SClass_ID		SuperClassID() { return CTRL_POSITION_CLASS_ID; }
	Class_ID		ClassID() { return exprPosControlClassID; }
	const TCHAR* 	Category() { return _T("");  }
};
static ExprPosClassDesc exprPosCD;
ClassDesc* GetExprPosCtrlDesc(); 

static Class_ID exprP3ControlClassID(EXPR_P3_CONTROL_CLASS_ID,0); 
class ExprP3ClassDesc:public ClassDesc 
{
	public:
	int 			IsPublic() { return 1; }
	void *			Create(BOOL loading) { return new ExprP3Control(loading); }
	const TCHAR *	ClassName() { return EXPR_P3_CONTROL_CNAME; }
	SClass_ID		SuperClassID() { return CTRL_POINT3_CLASS_ID; }
	Class_ID		ClassID() { return exprP3ControlClassID; }
	const TCHAR* 	Category() { return _T("");  }
};
static ExprP3ClassDesc exprP3CD;
ClassDesc* GetExprP3CtrlDesc();

static Class_ID exprFloatControlClassID(EXPR_FLOAT_CONTROL_CLASS_ID,0); 
class ExprFloatClassDesc:public ClassDesc 
{
	public:
	int 			IsPublic() { return 1; }
	void *			Create(BOOL loading) { return new ExprFloatControl(loading); }
	const TCHAR *	ClassName() { return EXPR_FLOAT_CONTROL_CNAME; }
	SClass_ID		SuperClassID() { return CTRL_FLOAT_CLASS_ID; }
	Class_ID		ClassID() { return exprFloatControlClassID; }
	const TCHAR* 	Category() { return _T("");  }
};
static ExprFloatClassDesc exprFloatCD;
ClassDesc* GetExprFloatCtrlDesc();

static Class_ID exprScaleControlClassID(EXPR_SCALE_CONTROL_CLASS_ID,0); 
class ExprScaleClassDesc:public ClassDesc 
{
	public:
	int 			IsPublic() { return 1; }
	void *			Create(BOOL loading) { return new ExprScaleControl(loading); }
	const TCHAR *	ClassName() { return EXPR_SCALE_CONTROL_CNAME; }
	SClass_ID		SuperClassID() { return CTRL_SCALE_CLASS_ID; }
	Class_ID		ClassID() { return exprScaleControlClassID; }
	const TCHAR* 	Category() { return _T("");  }
};
static ExprScaleClassDesc exprScaleCD;
ClassDesc* GetExprScaleCtrlDesc();

static Class_ID exprRotControlClassID(EXPR_ROT_CONTROL_CLASS_ID,0); 
class ExprRotClassDesc:public ClassDesc 
{
	public:
	int 			IsPublic() { return 1; }
	void *			Create(BOOL loading) { return new ExprRotControl(loading); }
	const TCHAR *	ClassName() { return EXPR_ROT_CONTROL_CNAME; }
	SClass_ID		SuperClassID() { return CTRL_ROTATION_CLASS_ID; }
	Class_ID		ClassID() { return exprRotControlClassID; }
	const TCHAR* 	Category() { return _T("");  }
};
static ExprFloatClassDesc exprRotCD;
ClassDesc* GetExprRotCtrlDesc();


#endif