/*	
 *		def_implement_functions.h - macros for making implementation declarations
 *									for MAXscript functions
 *
 *	This will typically be used along with protocol definition files in 
 *  the Value subclasses that actually implement those protocols.
 *
 *  see def_abstract_functionss.h for more info.
 *
 *	
 *	Copyright © John Wainwright 1996
 *
 */

/* def_generic macro for implementation declaration in Value subclasses */

#ifdef def_generic
#	undef def_generic
#	undef def_node_generic
#	undef def_mapped_generic
#	undef def_visible_generic
#	undef def_struct_generic
#	undef use_generic
#	undef def_primitive
#	undef def_mapped_primitive
#	undef def_lazy_primitive
#	undef def_visible_lazy_primitive
#	undef def_visible_primitive
#	undef def_struct_primitive
#	undef def_property
#	undef def_property_alias
#	undef def_2_prop_path
#	undef def_2_prop_path_alias
#	undef def_nested_prop
#	undef def_nested_prop_alias
#endif
#ifdef def_prop_getter
#	undef def_prop_getter
#	undef def_prop_setter
#endif

#define def_generic(fn, name)	\
	Value* fn##_vf(Value** arglist, int arg_count)
#define def_visible_generic(fn, name) def_generic(fn, name)
#define def_struct_generic(fn, name) def_generic(fn, name)
#define def_node_generic(fn, name) def_generic(fn, name)
#define def_mapped_generic(fn, name) def_generic(fn, name)
#define use_generic(fn, name) def_generic(fn, name)

#define def_primitive(fn, name)		// nothing for implementation...
#define def_lazy_primitive(fn, name) def_primitive(fn, name)
#define def_visible_lazy_primitive(fn, name) def_primitive(fn, name)
#define def_visible_primitive(fn, name) def_primitive(fn, name)
#define def_mapped_primitive(fn, name) def_primitive(fn, name)
#define def_struct_primitive(fn, _struct, name) def_primitive(fn, name)

#define def_property(p)										\
			Value*	get_##p(Value** arg_list, int count);	\
			Value*	set_##p(Value** arg_list, int count)
#define def_prop_getter(p)									\
			Value*	get_##p(Value** arg_list, int count)
#define def_prop_setter(p)									\
			Value*	set_##p(Value** arg_list, int count)
#define def_property_alias(p, real_p)
#define def_2_prop_path(p1, p2)										\
			Value*	get_##p1##_##p2(Value** arg_list, int count);	\
			Value*	set_##p1##_##p2(Value** arg_list, int count)
#define def_2_prop_path_alias(p1, p2, real_p1, real_p2)
#define def_nested_prop(p1)											\
			Value*	get_nested_##p1(Value** arg_list, int count);	\
			Value*	set_nested_##p1(Value** arg_list, int count)

#define def_backpatched_setter(_prop, _superclass)					\
			Value*													\
			set_##_prop(Value** arg_list, int count)				\
			{														\
				_superclass::set_##_prop(arg_list, count);			\
				back_patch();										\
				return arg_list[0];									\
			};

#define def_local_prop_alias(p, real_p)																\
			Value*	get_##p(Value** arg_list, int count) { return get_##real_p(arg_list, count); }   \
			Value*	set_##p(Value** arg_list, int count) { return set_##real_p(arg_list, count); }   \

#ifdef def_time_fn
#	undef def_time_fn
#endif
#define def_time_fn(_fn)	\
	Value* _fn##_vf(Value** arglist, int arg_count)

#undef def_name
#define def_name(name)	n_##name = Name::intern(_T(#name));		

#undef def_marker
#define def_marker(var, str)

/* ---------------------------- utility macros ---------------------*/

#define _def_num_bin_op(_class, _conv, _op_fn, _op, _member)	\
	Value*														\
	_class::_op_fn##_vf(Value** arg_list, int count)			\
	{															\
		one_value_local(widened);								\
		Value *arg, *result;									\
		arg = arg_list[0];										\
		if (tag != arg->tag && (vl.widened = widen_to(arg, arg_list)))	\
			result = vl.widened->_op_fn##_vf(arg_list, 1);		\
		else													\
			result = _class::intern(_member _op (arg_list[0])->_conv()); \
		pop_value_locals();										\
		return result;											\
	}

#define _def_bin_op(_class, _conv, _op_fn, _op, _member)		\
	Value*														\
	_class::_op_fn##_vf(Value** arg_list, int count)			\
	{															\
		one_value_local(widened);								\
		Value *arg, *result;									\
		arg = arg_list[0];										\
		if (tag != arg->tag && (vl.widened = widen_to(arg, arg_list)))	\
			result = vl.widened->_op_fn##_vf(arg_list, 1);		\
		else													\
			result = new _class (_member _op (arg_list[0])->_conv()); \
		pop_value_locals();										\
		return result;											\
	}

#define _def_rel_op(_class, _conv, _op_fn, _rel_op, _member)	\
	Value*														\
	_class::_op_fn##_vf(Value** arg_list, int count)			\
	{															\
		Value *cmpnd = arg_list[0], *result;					\
		if (comparable(cmpnd))									\
			result = (_member _rel_op cmpnd->_conv()) ?			\
						&true_value : &false_value;				\
		else													\
			throw IncompatibleTypes(this, cmpnd);				\
		return result;											\
	}

#define def_eq_op(_class, _conv, _member)						\
	Value*														\
	_class::eq_vf(Value** arg_list, int count)					\
	{															\
		Value *cmpnd = arg_list[0], *result;					\
		if (comparable(cmpnd))									\
			result = (_member == cmpnd->_conv()) ?				\
						&true_value : &false_value;				\
		else													\
			result = &false_value;								\
		return result;											\
	}
#define def_ne_op(_class, _conv, _member)						\
	Value*														\
	_class::ne_vf(Value** arg_list, int count)					\
	{															\
		Value *cmpnd = arg_list[0], *result;					\
		if (comparable(cmpnd))									\
			result = (_member == cmpnd->_conv()) ?				\
						&false_value : &true_value;				\
		else													\
			result = &true_value;								\
		return result;											\
	}


#define _def_num_un_op(_class, _op_fn, _op, _member)			\
	Value*														\
	_class::_op_fn##_vf(Value** arg_list, int count)			\
	{															\
		return _class::intern(_op _member);						\
	}

#define _def_un_op(_class, _op_fn, _op, _member)				\
	Value*														\
	_class::_op_fn##_vf(Value** arg_list, int count)			\
	{															\
		return new _class (_op _member);						\
	}

#define def_bin_op(_class, _conv, _op_fn, _op)					\
			_def_num_bin_op(_class, _conv, _op_fn, _op, value)
#define def_rel_op(_class, _conv, _op_fn, _rel_op)				\
			_def_rel_op(_class, _conv, _op_fn, _rel_op, value)
#define def_un_op(_class, _op_fn, _op)							\
			_def_num_un_op(_class, _op_fn, _op, value)

#define def_math_fn(_fn)										\
	Value*														\
	_fn##_cf(Value** arg_list, int count)						\
	{															\
		check_arg_count(_fn, 1, count);							\
		return Float::intern((float)_fn(arg_list[0]->to_float()));	\
	}

#define def_angle_trig_fn(_fn)									\
	Value*														\
	_fn##_cf(Value** arg_list, int count)						\
	{															\
		check_arg_count(_fn, 1, count);							\
		return Float::intern(RadToDeg((float)_fn(arg_list[0]->to_float()))); \
	}

#define def_float_trig_fn(_fn)									\
	Value*														\
	_fn##_cf(Value** arg_list, int count)						\
	{															\
		check_arg_count(_fn, 1, count);							\
		return Float::intern((float)_fn(DegToRad(arg_list[0]->to_float()))); \
	}

#define def_math_bin_fn(_fn)									\
	Value*														\
	_fn##_cf(Value** arg_list, int count)						\
	{															\
		check_arg_count(_fn, 2, count);							\
		return Float::intern((float)_fn(arg_list[0]->to_float(), \
							  arg_list[1]->to_float()));		\
	}

#define def_angle_trig_bin_fn(_fn)								\
	Value*														\
	_fn##_cf(Value** arg_list, int count)						\
	{															\
		check_arg_count(_fn, 2, count);							\
		return Float::intern(RadToDeg((float)_fn(arg_list[0]->to_float(), \
							  arg_list[1]->to_float())));		\
	}

#define def_num_prop_accessors(_class, _prop, _member, _type, _conv ) \
	Value*															\
	_class::get_##_prop(Value** arg_list, int count)				\
	{																\
		return _type::intern(_member);								\
	}																\
	Value*															\
	_class::set_##_prop(Value** arg_list, int count)				\
	{																\
		Value*  val = arg_list[0];									\
		_member = val->_conv();										\
		return val;													\
	}

#define def_prop_accessors(_class, _prop, _member, _type, _conv ) \
	Value*															\
	_class::get_##_prop(Value** arg_list, int count)				\
	{																\
		return new _type (_member);									\
	}																\
	Value*															\
	_class::set_##_prop(Value** arg_list, int count)				\
	{																\
		Value*  val = arg_list[0];									\
		_member = val->_conv();										\
		return val;													\
	}

#define def_fn_prop_accessors(_class, _prop, _getter, _setter)		\
	Value*															\
	_class::get_##_prop(Value** arg_list, int count)				\
	{																\
		return _getter;												\
	}																\
	Value*															\
	_class::set_##_prop(Value** arg_list, int count)				\
	{																\
		Value*  val = arg_list[0];									\
		_setter;													\
		return val;													\
	}

#define def_fn_prop_getter(_class, _prop, _getter)					\
	Value*															\
	_class::get_##_prop(Value** arg_list, int count)				\
	{																\
		return _getter;												\
	}																

#define def_fn_prop_setter(_class, _prop, _setter)					\
	Value*															\
	_class::set_##_prop(Value** arg_list, int count)				\
	{																\
		Value*  val = arg_list[0];									\
		_setter;													\
		return val;													\
	}

#define def_float_prop_accessors(_class, _prop, _member)				\
			def_num_prop_accessors(_class, _prop, _member, Float, to_float)

#define def_int_prop_accessors(_class, _prop, _member)					\
			def_num_prop_accessors(_class, _prop, _member, Integer, to_int)

#define def_point3_prop_accessors(_class, _prop, _member)				\
			def_fn_prop_accessors(_class, _prop, new Point3Value (_member), _member = val->to_point3())

#define def_angle_prop_accessors(_class, _prop, _member)				\
			def_fn_prop_accessors(_class, _prop, Float::intern(RadToDeg(_member)), _member = DegToRad(val->to_float()))

#define def_time_bin_op(_op_fn, _op)							\
			_def_bin_op(MSTime, to_timevalue, _op_fn, _op, time)
#define def_time_rel_op(_op_fn, _rel_op)						\
			_def_rel_op(MSTime, to_timevalue, _op_fn, _rel_op, time)
#define def_time_un_op(_op_fn, _op)								\
			_def_un_op(MSTime, _op_fn, _op, time)

#define def_quat_bin_op(_class, _conv, _op_fn, _op)				\
			_def_bin_op(_class, _conv, _op_fn, _op, q)
#define def_quat_rel_op(_class, _conv, _op_fn, _rel_op)			\
			_def_rel_op(_class, _conv, _op_fn, _rel_op, q)
#define def_quat_un_op(_class, _op_fn, _op)						\
			_def_un_op(_class, _op_fn, _op, q)
				
#define def_new_quat_fn(_fn, _arg_count, _call)				\
	Value*													\
	QuatValue::_fn##_vf(Value** arg_list, int count)		\
	{														\
		check_arg_count(_fn, _arg_count, count + 1);		\
		return new QuatValue (_call);						\
	}

#define def_mut_quat_fn(_fn, _arg_count, _call)				\
	Value*													\
	QuatValue::_fn##_vf(Value** arg_list, int count)		\
	{														\
		check_arg_count(_fn, _arg_count, count + 1);		\
		_call;												\
		return this;										\
	}

#define def_new_mat_fn(_fn, _arg_count, _call)				\
	Value*													\
	Matrix3Value::_fn##_vf(Value** arg_list, int count)		\
	{														\
		check_arg_count(_fn, _arg_count, count + 1);		\
		return new Matrix3Value (_call);						\
	}

#define def_mut_mat_fn(_fn, _arg_count, _call)				\
	Value*													\
	Matrix3Value::_fn##_vf(Value** arg_list, int count)		\
	{														\
		check_arg_count(_fn, _arg_count, count + 1);		\
		_call;												\
		return this;										\
	}

#define def_mat_primitive(_fn, _arg_count, _call)			\
	Value*													\
	_fn##_cf(Value** arg_list, int count)					\
	{														\
		check_arg_count(_fn, _arg_count, count);			\
		return new Matrix3Value (_call);					\
	}

#define def_mat_bin_op(_class, _conv, _op_fn, _op)			\
			_def_bin_op(_class, _conv, _op_fn, _op, m)
#define def_mat_rel_op(_class, _conv, _op_fn, _rel_op)		\
			_def_rel_op(_class, _conv, _op_fn, _rel_op, m)
#define def_mat_un_op(_class, _op_fn, _op)					\
			_def_un_op(_class, _op_fn, _op, m)
				
#define def_bool_node_fns(name, getter, setter)													\
	Value* node_get_##name(ReferenceTarget* obj, Value* prop, TimeValue t, Interval& valid)		\
	{																							\
		return ((INode*)obj)->getter() ? &true_value : &false_value;							\
	}																							\
	void node_set_##name(ReferenceTarget* obj, Value* prop, TimeValue t, Value* val)			\
	{																							\
		((INode*)obj)->setter(val->to_bool());													\
		MAXScript_interface->NodeInvalidateRect((INode*)obj);									\
		needs_redraw_set();																		\
	}

#define def_bool_node_getter(name, getter)														\
	Value* node_get_##name(ReferenceTarget* obj, Value* prop, TimeValue t, Interval& valid)		\
	{																							\
		return ((INode*)obj)->getter() ? &true_value : &false_value;							\
	}																							\
	void node_set_##name(ReferenceTarget* obj, Value* prop, TimeValue t, Value* val)			\
	{																							\
		throw RuntimeError (_T("Property not settable: "), #name);									\
	}
																							
#define def_ulong_node_fns(name, getter, setter)												\
	Value* node_get_##name(ReferenceTarget* obj, Value* prop, TimeValue t, Interval& valid)		\
	{																							\
		return Integer::intern((int)((INode*)obj)->getter());									\
	}																							\
	void node_set_##name(ReferenceTarget* obj, Value* prop, TimeValue t, Value* val)			\
	{																							\
		((INode*)obj)->setter((ULONG)val->to_int());											\
		MAXScript_interface->NodeInvalidateRect((INode*)obj);									\
		needs_redraw_set();																		\
	}

#define def_color_node_fns(name, getter, setter)												\
	Value* node_get_##name(ReferenceTarget* obj, Value* prop, TimeValue t, Interval& valid)		\
	{																							\
		DWORD rgb = ((INode*)obj)->getter();													\
		return ColorValue::intern(GetRValue(rgb) / 255.0f, GetGValue(rgb) / 255.0f, GetBValue(rgb) / 255.0f);	\
	}																							\
	void node_set_##name(ReferenceTarget* obj, Value* prop, TimeValue t, Value* val)			\
	{																							\
		AColor c;																				\
		c = val->to_acolor();																	\
		((INode*)obj)->setter(RGB((BYTE)(c.r * 255), (BYTE)(c.g * 255), (BYTE)(c.b * 255)));	\
		MAXScript_interface->NodeInvalidateRect((INode*)obj);									\
		needs_redraw_set();																		\
	}

