#ifndef _RUSHADER_D3D_TERRAIN_H_
#define _RUSHADER_D3D_TERRAIN_H_

// 3D shader core
#include "../../Direct3D/Shader/RuShader_D3D_Base.h"

#pragma unmanaged

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

class CRuShader_Terrain_Base : public CRuShader_Base
{
public:
											CRuShader_Terrain_Base(CRuEngineD3D *engine);
	virtual									~CRuShader_Terrain_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;

	// Instancing helper
	virtual void							Instance(INT32 streamIndex, IRuMesh *mesh, UINT32 morphTargetIndex, void *buffer) const;
};

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

class CRuShader_Terrain : public CRuShader_Terrain_Base
{
protected:
	IDirect3DPixelShader9*					m_pixelShaders_1_1[5];					//!< Pixel shaders, stored in the order of the number of layers they support
	IDirect3DPixelShader9*					m_pixelShaders_2_0[7];					//!< Pixel shaders, stored in the order of the number of layers they support

	// Intrinsic settings
	BOOL									m_maxLayersPerPass;						//!< Maximum number of texture layers per blend pass
	INT32									m_maxTextureLayers;						//!< Maximum number of texture layers allowed

	// Shader settings
	INT32									m_numTextureLayers;
	INT32									m_numPasses;

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

	// 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);
	virtual void							EndPass(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							SetParameters(const CRuParameterCache *paramCache);
};

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

class CRuShader_Terrain_Highlight : public CRuShader_Terrain_Base
{
public:
											CRuShader_Terrain_Highlight(CRuEngineD3D *engine);
	virtual									~CRuShader_Terrain_Highlight();

	// 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);
	virtual void							EndPass(INT32 passNumber);

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

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

class CRuShader_Terrain_Brush : public CRuShader_Terrain_Base
{
public:
											CRuShader_Terrain_Brush(CRuEngineD3D *engine);
	virtual									~CRuShader_Terrain_Brush();

	// 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);
	virtual void							EndPass(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							SetParameters(const CRuParameterCache *paramCache);
};

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

class CRuShader_Terrain_Packed_Base : public CRuShader_Base
{
public:
											CRuShader_Terrain_Packed_Base(CRuEngineD3D *engine);
	virtual									~CRuShader_Terrain_Packed_Base();

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

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

	// Instancing helper
	virtual void							Instance(INT32 streamIndex, IRuMesh *mesh, UINT32 morphTargetIndex, void *buffer) const;
};

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

class CRuShader_Terrain_Packed : public CRuShader_Terrain_Packed_Base
{
protected:
	IDirect3DVertexShader9*					m_vertexShaders[2];						//!< Vertex shaders
	IDirect3DPixelShader9*					m_pixelShaders_1_1[5];					//!< Pixel shaders, stored in the order of the number of layers they support
	IDirect3DPixelShader9*					m_pixelShaders_2_0[7];					//!< Pixel shaders, stored in the order of the number of layers they support

	// Intrinsic settings
	BOOL									m_maxLayersPerPass;						//!< Maximum number of texture layers per blend pass
	INT32									m_maxTextureLayers;						//!< Maximum number of texture layers allowed

	// Shader settings
	INT32									m_numTextureLayers;
	INT32									m_numPasses;

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

	// 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);
	virtual void							EndPass(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							SetParameters(const CRuParameterCache *paramCache);
};

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

class CRuShader_Terrain_Packed_Flat : public CRuShader_Terrain_Packed_Base
{
protected:
	IDirect3DVertexShader9*					m_vertexShaders[2];						//!< Vertex shaders
	IDirect3DPixelShader9*					m_pixelShaders_1_1[5];					//!< Pixel shaders, stored in the order of the number of layers they support
	IDirect3DPixelShader9*					m_pixelShaders_2_0[7];					//!< Pixel shaders, stored in the order of the number of layers they support

	// Intrinsic settings
	BOOL									m_maxLayersPerPass;						//!< Maximum number of texture layers per blend pass
	INT32									m_maxTextureLayers;						//!< Maximum number of texture layers allowed

	// Shader settings
	INT32									m_numTextureLayers;
	INT32									m_numPasses;

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

	// 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);
	virtual void							EndPass(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							SetParameters(const CRuParameterCache *paramCache);
};

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

class CRuShader_Terrain_Packed_Projection : public CRuShader_Terrain_Packed_Base
{
protected:

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

	// 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);
	virtual void							EndPass(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							SetParameters(const CRuParameterCache *paramCache);
};

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

class CRuShader_Terrain_Packed_Backdrop : public CRuShader_Terrain_Packed_Base
{
public:
											CRuShader_Terrain_Packed_Backdrop(CRuEngineD3D *engine);
	virtual									~CRuShader_Terrain_Packed_Backdrop();

	// 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);
	virtual void							EndPass(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							SetParameters(const CRuParameterCache *paramCache);
};

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

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

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

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

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

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

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

	return ruTEXCHANNEL_INVALID;
}

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

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

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

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

inline INT32 CRuShader_Terrain::GetNumPasses() const
{
	return m_numPasses;
}

inline INT32 CRuShader_Terrain::GetNumSamplers(INT32 passNumber) const
{
	// Fog pass - no samplers
	if(m_maxLayersPerPass == 3 && passNumber == m_numPasses - 1)
	{
		return 0;
	}

	INT32 numTexturesRemaining = m_numTextureLayers - (passNumber * m_maxLayersPerPass);

	// If the number of textures remaining is greater than the maximum number of layers per pass, then return the maximum number of samplers
	if(numTexturesRemaining >= m_maxLayersPerPass)
	{
		return m_maxLayersPerPass + (m_maxLayersPerPass / 3);
	}
	else
	{
		// If the number of textures remaining can be rendered in one pass with the 1-to-3 layer shader, use it
		if(numTexturesRemaining <= 3)
			return numTexturesRemaining + 1;
		// Otherwise use the 4-to-6 layer shader
		else
			return numTexturesRemaining + 2;
	}

	return 0;
}

inline RuTextureChannel CRuShader_Terrain::GetTextureChannel(INT32 passNumber, INT32 samplerNumber) const
{
	// Modify the sampler number
	INT32 baseSamplerNumber = passNumber * (m_maxLayersPerPass + m_maxLayersPerPass / 3);
	samplerNumber += baseSamplerNumber;

	switch(samplerNumber)
	{
		case 0:
			return ruTEXCHANNEL_TERRAIN_ALPHA_0;
		case 1:
			return ruTEXCHANNEL_TERRAIN_LAYER_0;
		case 2:
			return ruTEXCHANNEL_TERRAIN_LAYER_1;
		case 3:
			return ruTEXCHANNEL_TERRAIN_LAYER_2;
		case 4:
			return ruTEXCHANNEL_TERRAIN_ALPHA_1;
		case 5:
			return ruTEXCHANNEL_TERRAIN_LAYER_3;
		case 6:
			return ruTEXCHANNEL_TERRAIN_LAYER_4;
		case 7:
			return ruTEXCHANNEL_TERRAIN_LAYER_5;
		case 8:
			return ruTEXCHANNEL_TERRAIN_ALPHA_2;
		case 9:
			return ruTEXCHANNEL_TERRAIN_LAYER_6;
		case 10:
			return ruTEXCHANNEL_TERRAIN_LAYER_7;
		case 11:
			return ruTEXCHANNEL_TERRAIN_LAYER_8;
		case 12:
			return ruTEXCHANNEL_TERRAIN_ALPHA_3;
		case 13:
			return ruTEXCHANNEL_TERRAIN_LAYER_9;
		case 14:
			return ruTEXCHANNEL_TERRAIN_LAYER_10;
		case 15:
			return ruTEXCHANNEL_TERRAIN_LAYER_11;
	}

	return ruTEXCHANNEL_INVALID;
}

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

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

inline INT32 CRuShader_Terrain_Highlight::GetShaderPriority() const
{
	return 2000;
}

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

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

inline INT32 CRuShader_Terrain_Highlight::GetNumSamplers(INT32 passNumber) const
{
	return 0;
}

inline RuTextureChannel CRuShader_Terrain_Highlight::GetTextureChannel(INT32 passNumber, INT32 samplerNumber) const
{
	return ruTEXCHANNEL_INVALID;
}

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

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

inline INT32 CRuShader_Terrain_Brush::GetShaderPriority() const
{
	return 3000;
}

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

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

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

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

	return ruTEXCHANNEL_INVALID;
}

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

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

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

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

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

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

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

inline INT32 CRuShader_Terrain_Packed::GetShaderPriority() const
{
	return 999;
}

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

inline INT32 CRuShader_Terrain_Packed::GetNumPasses() const
{
	return m_numPasses;
}

inline INT32 CRuShader_Terrain_Packed::GetNumSamplers(INT32 passNumber) const
{
	// Fog pass - no samplers
	if(m_maxLayersPerPass == 3 && passNumber == m_numPasses - 1)
	{
		return 0;
	}

	INT32 numTexturesRemaining = m_numTextureLayers - (passNumber * m_maxLayersPerPass);

	// If the number of textures remaining is greater than the maximum number of layers per pass, then return the maximum number of samplers
	if(numTexturesRemaining >= m_maxLayersPerPass)
	{
		return m_maxLayersPerPass + (m_maxLayersPerPass / 3);
	}
	else
	{
		// If the number of textures remaining can be rendered in one pass with the 1-to-3 layer shader, use it
		if(numTexturesRemaining <= 3)
			return numTexturesRemaining + 1;
		// Otherwise use the 4-to-6 layer shader
		else
			return numTexturesRemaining + 2;
	}

	return 0;
}

inline RuTextureChannel CRuShader_Terrain_Packed::GetTextureChannel(INT32 passNumber, INT32 samplerNumber) const
{
	// Modify the sampler number
	INT32 baseSamplerNumber = passNumber * (m_maxLayersPerPass + m_maxLayersPerPass / 3);
	samplerNumber += baseSamplerNumber;

	switch(samplerNumber)
	{
		case 0:
			return ruTEXCHANNEL_TERRAIN_ALPHA_0;
		case 1:
			return ruTEXCHANNEL_TERRAIN_LAYER_0;
		case 2:
			return ruTEXCHANNEL_TERRAIN_LAYER_1;
		case 3:
			return ruTEXCHANNEL_TERRAIN_LAYER_2;
		case 4:
			return ruTEXCHANNEL_TERRAIN_ALPHA_1;
		case 5:
			return ruTEXCHANNEL_TERRAIN_LAYER_3;
		case 6:
			return ruTEXCHANNEL_TERRAIN_LAYER_4;
		case 7:
			return ruTEXCHANNEL_TERRAIN_LAYER_5;
		case 8:
			return ruTEXCHANNEL_TERRAIN_ALPHA_2;
		case 9:
			return ruTEXCHANNEL_TERRAIN_LAYER_6;
		case 10:
			return ruTEXCHANNEL_TERRAIN_LAYER_7;
		case 11:
			return ruTEXCHANNEL_TERRAIN_LAYER_8;
		case 12:
			return ruTEXCHANNEL_TERRAIN_ALPHA_3;
		case 13:
			return ruTEXCHANNEL_TERRAIN_LAYER_9;
		case 14:
			return ruTEXCHANNEL_TERRAIN_LAYER_10;
		case 15:
			return ruTEXCHANNEL_TERRAIN_LAYER_11;
	}

	return ruTEXCHANNEL_INVALID;
}

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

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

inline INT32 CRuShader_Terrain_Packed_Flat::GetShaderPriority() const
{
	return 100;
}

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

inline INT32 CRuShader_Terrain_Packed_Flat::GetNumPasses() const
{
	return m_numPasses;
}

inline INT32 CRuShader_Terrain_Packed_Flat::GetNumSamplers(INT32 passNumber) const
{
	// Fog pass - no samplers
	if(m_maxLayersPerPass == 3 && passNumber == m_numPasses - 1)
	{
		return 0;
	}

	INT32 numTexturesRemaining = m_numTextureLayers - (passNumber * m_maxLayersPerPass);

	// If the number of textures remaining is greater than the maximum number of layers per pass, then return the maximum number of samplers
	if(numTexturesRemaining >= m_maxLayersPerPass)
	{
		return m_maxLayersPerPass + (m_maxLayersPerPass / 3);
	}
	else
	{
		// If the number of textures remaining can be rendered in one pass with the 1-to-3 layer shader, use it
		if(numTexturesRemaining <= 3)
			return numTexturesRemaining + 1;
		// Otherwise use the 4-to-6 layer shader
		else
			return numTexturesRemaining + 2;
	}

	return 0;
}

inline RuTextureChannel CRuShader_Terrain_Packed_Flat::GetTextureChannel(INT32 passNumber, INT32 samplerNumber) const
{
	// Modify the sampler number
	INT32 baseSamplerNumber = passNumber * (m_maxLayersPerPass + m_maxLayersPerPass / 3);
	samplerNumber += baseSamplerNumber;

	switch(samplerNumber)
	{
		case 0:
			return ruTEXCHANNEL_TERRAIN_ALPHA_0;
		case 1:
			return ruTEXCHANNEL_TERRAIN_LAYER_0;
		case 2:
			return ruTEXCHANNEL_TERRAIN_LAYER_1;
		case 3:
			return ruTEXCHANNEL_TERRAIN_LAYER_2;
		case 4:
			return ruTEXCHANNEL_TERRAIN_ALPHA_1;
		case 5:
			return ruTEXCHANNEL_TERRAIN_LAYER_3;
		case 6:
			return ruTEXCHANNEL_TERRAIN_LAYER_4;
		case 7:
			return ruTEXCHANNEL_TERRAIN_LAYER_5;
		case 8:
			return ruTEXCHANNEL_TERRAIN_ALPHA_2;
		case 9:
			return ruTEXCHANNEL_TERRAIN_LAYER_6;
		case 10:
			return ruTEXCHANNEL_TERRAIN_LAYER_7;
		case 11:
			return ruTEXCHANNEL_TERRAIN_LAYER_8;
		case 12:
			return ruTEXCHANNEL_TERRAIN_ALPHA_3;
		case 13:
			return ruTEXCHANNEL_TERRAIN_LAYER_9;
		case 14:
			return ruTEXCHANNEL_TERRAIN_LAYER_10;
		case 15:
			return ruTEXCHANNEL_TERRAIN_LAYER_11;
	}

	return ruTEXCHANNEL_INVALID;
}

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

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

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

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

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

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

inline RuTextureChannel CRuShader_Terrain_Packed_Projection::GetTextureChannel(INT32 passNumber, INT32 samplerNumber) const
{
	return ruTEXCHANNEL_TERRAIN_PROJECTION;
}

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

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

inline INT32 CRuShader_Terrain_Packed_Backdrop::GetShaderPriority() const
{
	return 998;
}

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

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

inline INT32 CRuShader_Terrain_Packed_Backdrop::GetNumSamplers(INT32 passNumber) const
{
	return 0;
}

inline RuTextureChannel CRuShader_Terrain_Packed_Backdrop::GetTextureChannel(INT32 passNumber, INT32 samplerNumber) const
{
	return ruTEXCHANNEL_INVALID;
}

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

#pragma managed

#endif
