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


#ifndef	__NLIGHT__
#define	__NLIGHT__


		#include "core/item.h"
		#include "core/render_data.h"
		#include "color/color.h"
		#include "geometry/frustum.h"
		#include "reflection/nenum_string.h"
		#include "memory/nauto_ptr.h"


namespace GS {
	namespace Core {

/*!
	@short	Light.
	@author Emmanuel Julien (ejulien@gsworks.fr)
*/
class	Light : public Item
{
public:

///
struct	RenderData
{
		Render::sTexture projection_texture;

	struct	ShadowRenderData
	{
	virtual	~ShadowRenderData() {}
	};

		/// Shadow data.
	struct	ShadowData
	{
		AutoPtr <ShadowRenderData> render_data;

		float		slice_distance;
		Vector4		crop_region;

		Matrix4		imatrix, pmatrix;
	};

		Array <ShadowData> shadow_data;
};

static	Reflection::Property serializable[];

		/// Light model.
enum	Model
{
		Model_None = 0,

		Model_Point,
		Model_Linear,
		Model_Spot,

		Model_Last
};
static	Reflection::Enum::Dict model_dict[];

		/// Light falloff.
enum	Falloff
{
		Falloff_Linear		 = 0,					///< Linear.
		Falloff_InvDist,							///< Inverse distance.
		Falloff_InvDist2							///< Inverse squared distance.
};
static	Reflection::Enum::Dict falloff_dict[];

		/// Light shadow method.
enum	Shadow
{
		Shadow_None			= 0,
		Shadow_ProjectionMap,
		Shadow_Map
};
static	Reflection::Enum::Dict shadow_dict[];

		Model		model;
		Frustum		frustum;

/*!
	@name	Shadow section.
	@{
*/
		Shadow		shadow;
		bool		shadow_cast_all;
		float		shadow_range,
					shadow_bias,
					shadow_distribution,
					z_near;
/// @}

/*!
	@name	Volumetric section.
	@{
*/
		bool		volumetric;
		float		volumetric_sample_step,
					volumetric_range,
					volumetric_thickness;
/// @}

/*!
	@name	Light section.
	@{
*/
		Falloff		falloff;

		float		range,
					volume_range,				///< Volume range (mainly used as an optimization hint).
					clip_distance;

		Color		diffuse_color,
					specular_color,
					shadow_color;

		float		diffuse_intensity,
					specular_intensity,
					cone_angle,
					edge_angle;

		String		projection_texture;			///< Projection texture.
/// @}

		/// Set the spot cone and edge angles.
		void			SetSpotAngle(float cone = Units::Deg(40), float edge = Units::Deg(10))
		{
			cone_angle = cone;
			edge_angle = edge;
		}

		/// Sample lighting contributed by this light to a location in space.
		bool			SampleColor(const Vector4 &p, const Vector4	&n, Color &diff, Color &spec, Vector4 *view = 0, float gloss = 0.8);
		/*!
			@short	Sample energy this light contribute to a location in space.

			This function can sample both the diffuse and specular lightning
			contribution from a given light to a given position in space.
			You can specify whether the specular should be computed using
			accurate Phong model or using the halfway vector (faster/less
			accurate).
			You can ask that specular reflection be evaluated using the
			Cook-Torrance model by calling the function with the parameter
			'cooktorrance' set to true.	The default specular reflection uses
			the Phong model.

			@note	The default specular model used is the halfway vector one.
			@note	Sampling the specular contribution requires a view vector
					and a glossiness value.

			@return		True if the light contribute to the surface.
		*/
		bool		SampleEnergy(const Vector4 &p,const Vector4	&n, float *diff = 0, float *spec = 0, Vector4 *view = 0, float gloss = 0.8, bool halfway = false, bool cooktorrance = false);

virtual	void		ComputeMatrix();
		void		ComputeProjectionMatrix(Matrix4 &m_) const;
		void		ComputeFrustrum(Frustum &f, float z_near = -1, float z_far = -1) const;

		float		GetNearClippingPlane() const { return z_near; }
		float		GetFarClippingPlane() const { return volume_range; }
		void		SetNearClippingPlane(float z) { z_near = z; }
		void		SetFarClippingPlane(float z) { volume_range = z; }

		void		RenderSetup(ResourceFactories * = 0);

		AutoPtr <RenderData> render_data;

		void		SetDefaults();

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

		Light();
};

typedef	Light * pLight;

	} // Core
} // GS


#endif	// __NLIGHT__
