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


#ifndef	__GPUSHADER__
#define __GPUSHADER__


		#include "gpu/gpu_types.h"
		#include "gpu/gpu_shader_object.h"
		#include "core/render_data.h"
		#include "core/shader_tree_compiler.h"


namespace GS {

	namespace Core {
		class Item;
		class Camera;
		class Light;
		struct Skin;
	}
	namespace GPU {
		class Renderer;
		struct DisplayList;
		struct Material;
		struct DrawContext;

//
struct	ShaderInputLocation
{
		NPLACEMENT_NEW(Renderer)
virtual	~ShaderInputLocation() {}
};

//
struct	ShaderInput
{
		NPLACEMENT_NEW(Renderer)

		String			name;

		Core::ShaderInput::Type type;
		Core::ShaderInput::Semantic semantic;
		Core::ShaderInput::DataType data_type;

		Render::sTexture parm_t;		///< 4B texture
		Vector4			parm_v;			///< 16B vector

		int				index;

		AutoPtr <ShaderInputLocation> location;

		void			SetValue(float v) { parm_v.Set(v); }
		void			SetValue(float x, float y) { parm_v.Set(x, y); }
		void			SetValue(float x, float y, float z) { parm_v.Set(x, y, z); }
		void			SetValue(const Vector2 &v) { parm_v.Set(v.x, v.y); }
		void			SetValue(const Vector4 &v) { parm_v = v; }
		void			SetValue(Render::Texture *t) { parm_t = t; }

		void			Set(Core::ShaderInput *input)
		{
			index = -1;

			name = input->name;
			parm_v = input->parm_v;
			data_type = input->data_type;

			semantic = input->semantic;
			type = input->type;
		}

		ShaderInput()
		{
			index = -1;
			data_type = Core::ShaderInput::NoData;
		}
};

/*!
	@short	GPU shader.
*/
struct	Shader : public Render::Shader
{
		Renderer		&renderer;

		/// Get the input list index for a given semantic.
static	uint			GetSemanticInputList(Core::ShaderInput::Semantic);

		AutoPtr <ShaderObject> vertex, pixel;
		Array <ShaderInput> input_list[Core::ShaderInput::CategoryLast];	///< Attributes and uniforms list are split per category.

		/// Get a program parameter from its semantic.
		ShaderInput		*GetInput(Core::ShaderInput::Semantic) const;
		/// Get a program parameter from its name.
		ShaderInput		*GetInput(const char *) const;

		/// Set vertex stream inputs.
		void			SetVertexStreamInputs(DisplayList &);
		/// Set skin inputs.
		void			SetSkinInputs(DisplayList &, Core::Skin &);
		/// Set program inputs.
		void			SetConstantInputs();
		/// Set texture inputs.
		void			SetTextureInputs();
		/// Set renderer inputs.
		void			SetRendererInputs(Renderer &, Material * = 0);
		/// Set transformation inputs.
		void			SetTransformInputs(const Matrix4 &v_pm, const Matrix4 &v_m, const Matrix4 &v_im, const Matrix4 *i_m, const Matrix4 *i_im, uint count = 1);
		/// Set previous transformation inputs.
		void			SetPreviousTransformInputs(const Matrix4 &v_pm, const Matrix4 &v_im, const Matrix4 *i_m, uint count = 1);
		/// Set material opacity inputs.
		void			SetMaterialOpacityInputs(Material &, float opacity);
		/// Set material inputs.
		void			SetMaterialInputs(Material &);
		/// Set light inputs.
		void			SetLightInputs(Renderer &, Core::Camera &, Core::Light &);

		/// Commit inputs.
virtual	void			CommitInputs() = 0;

		/// New GPU shader location.
virtual	ShaderInputLocation *NewGPUShaderLocation() = 0;
		/// Retrieve the location of any program parameter from its declaration name.
virtual	bool			GetLocation(const char *name, ShaderInputLocation &, Core::ShaderInput::Type = Core::ShaderInput::Uniform) = 0;

/*!
	@name	Uniform accessors.
	@{
*/
virtual	void			Set(const ShaderInputLocation &, const Render::Texture &, int unit) = 0;
virtual	void			Set(const ShaderInputLocation &, const Matrix3 *, int n = 1) = 0;
virtual	void			Set(const ShaderInputLocation &, const Matrix4 *, int n = 1) = 0;
virtual	void			Set(const ShaderInputLocation &, const float *, int n = 1) = 0;
virtual	void			Set(const ShaderInputLocation &, const int *, int n = 1) = 0;

		void			Set(const ShaderInputLocation &l, int v)			{	Set(l, &v);	}
		void			Set(const ShaderInputLocation &l, float v)			{	Set(l, &v);	}
		void			Set(const ShaderInputLocation &l, const Matrix3 &m)	{	Set(l, &m);	}
		void			Set(const ShaderInputLocation &l, const Matrix4 &m)	{	Set(l, &m);	}
/// @}

/*!
	@name	Attribute accessors.
	@{
*/
virtual	void			Set(const ShaderInputLocation &, int elem_count, Types::ValueType, bool normalized, size_t stride, const void *offset) = 0;
/// @}

		/// Create program.
virtual	bool			Create(Render::ResourceFactory &, const Core::Shader &);

		/// Free program.
virtual	void			Free() = 0;

		Shader(Renderer &r, const char *name) : Render::Shader(name), renderer(r) {}
};

	} // GPU
} // GS


#endif	// __GPUSHADER__
