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


#ifndef	__NEMITTER__
#define	__NEMITTER__


		#include "core/item.h"
		#include "core/renderable.h"
		#include "geometry/curve.h"
		#include "sort/sort.h"


namespace GS {
	namespace Core {

/*!
	@short	Particle class.
	@author	Emmanuel Julien (ejulien@gsworks.fr)
*/
struct	Particle
{
		Time		time;

		Vector4		position,
					velocity;

		float		size;
		float		angle;
		Color		color;

/*!
	@name	Parameter scaler.
	@{
*/
		float		opacity_scale,
					size_scale;
/// @}

		/// Is the particle alive.
		bool		IsAlive() const { return time.toSec() < 0 ? false : true; }

		Particle()
		{	opacity_scale = 1;	}
};

/*
	@short	Particle model.
	Models the particle attributes over its lifetime.
	@author	Emmanuel Julien (ejulien@gsworks.fr)
*/
struct	ParticleModel : public SharedObject
{
	struct	RenderData
	{
		Render::sMaterial material;
	};

		String		name;
		String		material;

		Time		time_to_live;		///< Time to live.
		float		damping;			///< Particle velocity damping.
		Vector4		gravity;			///< Particle gravity.

		Curve		angle_curve,
					size_curve;

		Curve		red_curve,
					green_curve,
					blue_curve,
					opacity_curve;

		AutoPtr <RenderData> render_data;

		/// Setup render data.
		void		RenderSetup(ResourceFactories * = 0);

		/*!
			@short	Set template color.
			This function does not clear the color and opacity curves.
			It only set their default value member, curves with keys will animate
			as usual.
		*/
		void		SetColor(const Color &);
		/// Add color curve point.
		void		AddColorPoint(const Time &t, const Color &);

/*!
	@name	Serialization.
	@{
*/
		bool		FromMetaTag(NML::Tag &);
		NML::Tag	*AsMetaTag() const;
/// @}

		ParticleModel();
};

typedef SharedPtr <ParticleModel> sParticleModel;

/*!
	@short	Emitter class.
	Note: Emitters emit particle along the Z axis.
	@author Emmanuel Julien (ejulien@gsworks.fr)
*/
class	Emitter : public Item, public Renderable
{
public:

struct	RenderData
{
		sParticleModel particle_model;
};

enum	Model
{
		Model_Spray		=	0
};

/*!
	@name	Emitter model.
	@{
*/
protected:

		Model		model;

public:

/*!
	@name	Renderable interface.
	@{
*/
		/// Compute the renderable minmax.
virtual	void		ComputeRenderableMinMax(MinMax &mm) { ComputeMinMax(mm); }
		/// Get the renderable primitive list for this renderable.
virtual	uint		GetRenderablePrimitiveList(const Camera &view, const Camera &default_view, Stack <Render::Primitive *> &list, Renderable::Context context = Renderable::Context_Default, bool cull = true);
/// @}

		uint		pool_size;					///< Particle pool size.
		float		birth_rate;					///< Rate in particle/second.
		float		birth_speed_min,
					birth_speed_max;			///< Speed in m.s.
		float		spray_angle;

/*!
	@name	Model scale.
	@{
*/
		float		birth_rate_scale,
					birth_opacity_scale,
					birth_speed_scale,
					birth_size_scale;
/// @}

		String		particle_model;

		/// Model particle.
		void		ModelParticle(Particle &, const Item &, const Time &emitter_time);

		/// Get emitter model.
		Model		GetModel() const { return model; }
		/// Set as spray emitter.
		void		SetSprayModel(float angle = Units::Deg(45.f));
/// @}

		AutoPtr <RenderData> render_data;

protected:

		Array <Particle> particle_pool;

		uint		alive_count;
		bool		is_seen;

		Time		time;
		Time		birth_time;

		Array <Sort <float, uint>::Entry> sort_array;

public:

		void		RenderSetup(ResourceFactories * = 0);

		bool		Setup();
		void		Update(const Time &dt);
		void		Free();

		void		ComputeMinMax(MinMax &) const;

		/// Sort emitter particles against a given view matrix.
		void		Sort(const Matrix4 &view_matrix);
		/*!
			@short	Get alive particle count.
			@note	The emitter must have been sorted beforehand.
			@see	Sort().
		*/
		uint		GetParticleCount() const { return alive_count; }
		/*
			@short	Get alive particle.
			@note	Particles are returned back to front.
		*/
		Particle	*GetParticle(uint n) const { return n < particle_pool.GetCount() ? &particle_pool[sort_array[alive_count - 1 - n].o] : 0; }

/*!
	@name	Serialization.
	@{
*/
		bool		FromMetaTag(NML::Tag &);
		NML::Tag	*AsMetaTag() const;
/// @}

		Emitter();
};

	} // Core
} // GS


#endif	// __NEMITTER__
