/* -----------------------------------------------------------------------------
	GSFramework
	Copyright 2001-2013 Emmanuel Julien. All Rights Reserved.
----------------------------------------------------------------------------- */


#ifndef	__NMOTION__
#define	__NMOTION__


		#include "motion/quaternion_channel.h"
		#include "motion/motion_channel.h"
		#include "core/path_kdtree.h"
		#include "container/nlist.h"
		#include "memory/nshared_ptr.h"
		#include "memory/nauto_ptr.h"


namespace GS {
	namespace Automation { class Player; }
	namespace Core {

/*!
	@short	Motion.
	A motion holds together a group of channels.
	@author Emmanuel Julien (ejulien@gsworks.fr)
*/
class	Motion : public SharedObject
{
		friend class Automation::Player;

protected:
	
struct SDataPoint
{
	Time t;
	Variant data;
};

		ArrayList <SDataPoint *> data_point;		///< data per point.
		AutoList <MotionChannel *> channel_list;

		bool			use_quaternion;
		QuaternionChannel quaternion;

public:
	
		NPLACEMENT_NEW(Motion)

		String			name;

		AutoPtr <PathKdtree> quadtree;

		/// Clone a motion, optionally specify the range to clone.
		void			Clone(const Motion &, const TimeRange &, bool enforce_loop);

		/// Sample time from position
		bool			GetClosestPoint(const Vector4 &p, Vector4 &closest, float *closest_t = 0);

		void			EvaluateData(const Time &t, Variant &sample);
		void			EvaluatePosition(const Time &t, Vector4 &sample, Curve::LoopMode loop_mode = Curve::Reset);
		void			EvaluateRotation(const Time &t, Vector4 &sample, Curve::LoopMode loop_mode = Curve::Reset);

		/// Get transformation channels.
		void			GetTransformationChannels(MotionChannel *t[3], MotionChannel *r[3], MotionChannel *s[3]);

		bool			HasKey(const TimeRange &t) const;
		void			MoveKey(const TimeRange &t, const Time &offset) const;
		void			DeleteKey(const TimeRange &t);

		/// Add a new channel controlling a given property to the motion.
		MotionChannel	*AddChannel(MotionChannel::Type);
		/// Add several channels at once.
		void			AddChannels(uint channel_count);

		const AutoList <MotionChannel *> &GetChannelList() const { return channel_list; }

		const QuaternionChannel &GetQuaternion() const { return quaternion; }
		QuaternionChannel &GetQuaternion() { return quaternion; }

		bool			GetUseQuaternion() const;
		void			SetUseQuaternion(bool use) { use_quaternion = use; }

		MotionChannel	*GetChannel(MotionChannel::Type) const;
		MotionChannel	*GetChannel(uint index) const;

		/// Return the motion duration.
		Time			GetDuration() const;
		/// Return the motion time range.
		TimeRange		GetTimeRange() const;

		/// Return the memory footprint in bytes of the channel.
		size_t			MemoryFootPrint() const;
		/*!
			@short	Optimize motion.
			Remove keys influencing output below a given threshold.

			@note	This function calls Optimize() for all of the motion channels.
		*/
		uint			Optimize(float threshold = DefaultCurveOptimizationThreshold);

		bool			FromMetaTag(NML::Tag &);
		NML::Tag		*AsMetaTag();

		~Motion() {
			ArrayListDeleteAllPtr(SDataPoint *, data_point)
		}
};

	} // Core
} // GS


#endif // __NMOTION__
