#ifndef _RUSHADER_D3D_BASE_H_
#define _RUSHADER_D3D_BASE_H_

// Rune system core
#include "../../Direct3D/Engine/RuEngineD3D.h"

// Shader base
#include "../../Engine/Base/RuShaderBase.h"

#pragma unmanaged

// ************************************************************************************************************************************************************

/* 
	NOTES
	-----
	Base vertex shader constants takes up a total of 30 registers (120 constants). The breakdown:

	[  0 -   2] 4x3 World transform
	[  3 -   5] 4x3 View transform
	[  6 -   9] 4x4 Projection transform
	[ 10 -  10] Ambient color
	[ 11 -  19] SH9 coefficients
	[ 20 -  20] Constant terms for SH9 and other purposes, defined as: (3.0, 1.0, 0.5, 0.0)
	[ 21 -  21] Specular direction (In view space?)
	[ 22 -  22] Specular color
	[ 23 -  23] (Self-illumination term, ?, ?, ?)
	[ 24 -  24] Reserved (for additional lighting)
	[ 25 -  25] Global settings, defined as: (Fog Constant, Fog Start, Fog End, Tween Factor)
	[ 26 -  26] Global diffuse color modifier, defined as (R, G, B, A)
	[ 27 -  29] Reserved for use by subclasses

	Base pixel shader constants breakdown:

	[  0 -   0] Constants, defined as: (1.0, 0.5, 0.0, 0.05)
	[  1 -   1] Fog Color

*/
class CRuShader_Base : public IRuShader
{
protected:
	CRuEngineD3D*							m_engine;
	IRuVertexDeclaration*					m_vertexDecl;
	IDirect3DVertexShader9*					m_vertexShader;
	IDirect3DPixelShader9*					m_pixelShader;
	ID3DXConstantTable*						m_vertexShaderConstantTable;
	ID3DXConstantTable*						m_pixelShaderConstantTable;

	INT32									m_numBaseConstants;
	float									m_shaderConstants[4 * 256];
	INT32									m_numBasePixelShaderConstants;
	float									m_pixelShaderConstants[4 * 32];

	// Inheritance options
	BOOL									m_override_Fog;
	BOOL									m_override_AlphaTest;
	BOOL									m_override_AlphaBlend;
	BOOL									m_override_ZTest;
	BOOL									m_override_ZWrite;

	// Material options
	BOOL									m_2Sided;									//!< 2-sided geometry?
	BOOL									m_faceted;									//!< Faceted?
	BOOL									m_wire;										//!< Wireframe?
	BOOL									m_alphaTestEnabled;							//!< Enable alpha test?
	UINT32									m_alphaTestThreshold;						//!< Alpha test threshold (>=)
	RuAlphaTestMode							m_alphaTestMode;
	RuAlphaMode								m_material_AlphaMode;
	BOOL									m_material_Translucent;						//!< The material is translucent
	RuZTestMode								m_material_ZTestMode;
	RuZWriteMode							m_material_ZWriteMode;						//!< Material z write mode
	REAL									m_material_Alpha;
	REAL									m_material_AlphaModifier;
	REAL									m_material_EffectiveAlpha;

	BOOL									m_fx_ReverseCull;
	BOOL									m_fx_StencilEnable;
	UINT8									m_fx_StencilID;

public:
											CRuShader_Base(CRuEngineD3D *engine);
	virtual									~CRuShader_Base();

	virtual const IRuVertexDeclaration*		GetVertexDeclaration() const;

	virtual INT32							GetShaderClass() const;
	virtual INT32							GetShaderPriority() const;

	// Rendering setup
	virtual void							BeginPass(INT32 passNumber);
	virtual void							EndPass(INT32 passNumber);

	// Shader parameter setup
	virtual void							SetDefaultParameters();
	virtual void							SetParameters(const CRuParameterCache *paramCache);

protected:
	void									SetZTestMode(D3DCMPFUNC defaultValue);
	void									SetZWriteMode(BOOL defaultValue);
};

// ************************************************************************************************************************************************************

/*
	[ 27 -  30] Texture transform 0
*/
class CRuShader_Static_Base : public CRuShader_Base
{
protected:
	CRuMatrix4x4							m_identityMatrix;

public:
											CRuShader_Static_Base(CRuEngineD3D *engine);
	virtual									~CRuShader_Static_Base();

	// Shader capabilities query
	virtual const char*						GetShaderName() const;
	virtual INT32							GetShaderPriority() const;
	virtual DWORD							GetCaps() const;

	// Multi-Pass
	virtual INT32							GetNumPasses() const;

	// Shader texture placement query
	virtual INT32							GetNumSamplers(INT32 passNumber) const;
	virtual RuTextureChannel				GetTextureChannel(INT32 passNumber, INT32 samplerNumber) const;

	// Shader parameter setup
	virtual void							SetDefaultParameters();
	virtual void							SetParameters(const CRuParameterCache *paramCache);
};

// ************************************************************************************************************************************************************

/*
	[ 27 -  30] Texture transform 0
*/
class CRuShader_Static_Colored_Base : public CRuShader_Base
{
protected:
	CRuMatrix4x4							m_identityMatrix;

public:
											CRuShader_Static_Colored_Base(CRuEngineD3D *engine);
	virtual									~CRuShader_Static_Colored_Base();

	// Shader capabilities query
	virtual const char*						GetShaderName() const;
	virtual INT32							GetShaderPriority() const;
	virtual DWORD							GetCaps() const;

	// Multi-Pass
	virtual INT32							GetNumPasses() const;

	// Shader texture placement query
	virtual INT32							GetNumSamplers(INT32 passNumber) const;
	virtual RuTextureChannel				GetTextureChannel(INT32 passNumber, INT32 samplerNumber) const;

	// Shader parameter setup
	virtual void							SetDefaultParameters();
	virtual void							SetParameters(const CRuParameterCache *paramCache);
};

// ************************************************************************************************************************************************************

/*
	[ 27 -  30] Texture transform 0
*/
class CRuShader_Skin_Base : public CRuShader_Base
{
protected:
	// Skinning parameters
	INT32									m_maxSubFrames;								// Maximum number of bones the shader is able to support
	INT32									m_numSubFrames;								// Number of bones being used
	CRuMatrix4x4*							m_skinTransforms;							// Skin transform matrices
	UINT16*									m_indexMap;									// Matrix palette indices

	CRuMatrix4x4							m_identityMatrix;

public:
											CRuShader_Skin_Base(CRuEngineD3D *engine);
	virtual									~CRuShader_Skin_Base();

	// Shader capabilities query
	virtual const char*						GetShaderName() const;
	virtual INT32							GetShaderPriority() const;
	virtual DWORD							GetCaps() const;

	// Multi-Pass
	virtual INT32							GetNumPasses() const;

	// Rendering setup
	virtual void							BeginPass(INT32 passNumber);

	// Shader texture placement query
	virtual INT32							GetNumSamplers(INT32 passNumber) const;
	virtual RuTextureChannel				GetTextureChannel(INT32 passNumber, INT32 samplerNumber) const;

	// Shader parameter setup
	virtual void							SetDefaultParameters();
	virtual void							SetParameters(const CRuParameterCache *paramCache);
};

// ************************************************************************************************************************************************************

extern IDirect3DVertexShader9*				RuD3DAssembleVertexShader(const char *vsString);
extern IDirect3DPixelShader9*				RuD3DAssemblePixelShader(const char *psString);
extern IDirect3DVertexShader9*				RuD3DCompileVertexShader(const char *vsString, const char *entryFunction, const char *profile, ID3DXConstantTable **constantTableOut);
extern IDirect3DPixelShader9*				RuD3DCompilePixelShader(const char *psString, const char *entryFunction, const char *profile, ID3DXConstantTable **constantTableOut);

// ************************************************************************************************************************************************************

inline const IRuVertexDeclaration *CRuShader_Base::GetVertexDeclaration() const
{
	return m_vertexDecl;
}

// ************************************************************************************************************************************************************

inline const char *CRuShader_Static_Base::GetShaderName() const
{
	return "CRuShader_Static_Base";
}

inline INT32 CRuShader_Static_Base::GetShaderPriority() const
{
	return 1000;
}

inline DWORD CRuShader_Static_Base::GetCaps() const
{
	return 0x00000000;
}

inline INT32 CRuShader_Static_Base::GetNumPasses() const
{
	return 1;
}

inline INT32 CRuShader_Static_Base::GetNumSamplers(INT32 passNumber) const
{
	return 1;
}

inline RuTextureChannel CRuShader_Static_Base::GetTextureChannel(INT32 passNumber, INT32 samplerNumber) const
{
	if(passNumber == 0 && samplerNumber == 0)
		return ruTEXCHANNEL_DIFFUSEMAP;

	return ruTEXCHANNEL_INVALID;
}

// ************************************************************************************************************************************************************

inline const char *CRuShader_Static_Colored_Base::GetShaderName() const
{
	return "CRuShader_Static_Colored_Base";
}

inline INT32 CRuShader_Static_Colored_Base::GetShaderPriority() const
{
	return 1000;
}

inline DWORD CRuShader_Static_Colored_Base::GetCaps() const
{
	return 0x00000000;
}

inline INT32 CRuShader_Static_Colored_Base::GetNumPasses() const
{
	return 1;
}

inline INT32 CRuShader_Static_Colored_Base::GetNumSamplers(INT32 passNumber) const
{
	return 1;
}

inline RuTextureChannel CRuShader_Static_Colored_Base::GetTextureChannel(INT32 passNumber, INT32 samplerNumber) const
{
	if(passNumber == 0 && samplerNumber == 0)
		return ruTEXCHANNEL_DIFFUSEMAP;

	return ruTEXCHANNEL_INVALID;
}

// ************************************************************************************************************************************************************

inline const char *CRuShader_Skin_Base::GetShaderName() const
{
	return "ruSHADER_SKIN_BASE";
}

inline INT32 CRuShader_Skin_Base::GetShaderPriority() const
{
	return 1000;
}

inline DWORD CRuShader_Skin_Base::GetCaps() const
{
	return 0x00000000;
}

inline INT32 CRuShader_Skin_Base::GetNumPasses() const
{
	return 1;
}

inline INT32 CRuShader_Skin_Base::GetNumSamplers(INT32 passNumber) const
{
	return 1;
}

inline RuTextureChannel CRuShader_Skin_Base::GetTextureChannel(INT32 passNumber, INT32 samplerNumber) const
{
	if(passNumber == 0 && samplerNumber == 0)
		return ruTEXCHANNEL_DIFFUSEMAP;

	return ruTEXCHANNEL_INVALID;
}

// ************************************************************************************************************************************************************

#pragma managed

#endif
