///////////////////////////////////////////////////////////////////////////////
/// @file DDirect3D9-RenderStates.cpp
///
/// @brief class for Direct3D9 driver renderstate changes
///
/// @author Lightning, Assassin, ibutsu
///
/// This file is the intellectual property of Novus Delta, LLC.. Usage of the
/// contents of this file is subject to the Destiny3D Member License which
/// can be found at http://www.destiny3d.com.  Any other usage is prohibited.
///
/// This file is distributed "AS IS" without warranty of any kind.  Novus
/// Delta, LLC. does not guarantee the fitness of the contents of this file
/// for any particular purpose.
///
/// Copyright (C) 2001-2003 Novus Delta, LLC. All Rights Reserved.
///
/// <hr>
///                                 Change History
/// <hr>
///
/// @date Jan 2002
/// @author Lightning
/// @remarks Creation
///
/// @date Dec 2003
/// @author ibutsu
/// @remarks d3d9 port
///
/// @date Jul 2004
/// @author poly-gone
/// @remarks Two sided stencil support
///
///////////////////////////////////////////////////////////////////////////////

//needed global headers
#include <d3dx9.h>
#include "DDirect3D9.hpp"
#include "deGlobalTypes.hpp"
#include "deDriver_priv.hpp"
#include "deBitmap.hpp"

//#define ENABLE_NULL_TEXTURE_WARNING
#ifdef ENABLE_NULL_TEXTURE_WARNING
#	define NULLTEXWARNING(x,y) DEBUGMSG1(x,y)
#else
#	define NULLTEXWARNING(x,y)
#endif

#define TEXTURESTRUCTID 0x01005354		//"TS",0,1

D3DTEXTUREOP GetD3DTextureOp(IdeRenderTexture::TextureOp Operation)
{
	//get the D3D equivilant of the renderstate texture operation
	switch(Operation)
	{
		case IdeRenderTexture::TEXTUREOP_DISABLE:
			return D3DTOP_DISABLE;

		case IdeRenderTexture::TEXTUREOP_SELECTARG1:
			return D3DTOP_SELECTARG1;

		case IdeRenderTexture::TEXTUREOP_SELECTARG2:
			return D3DTOP_SELECTARG2;

		case IdeRenderTexture::TEXTUREOP_MODULATE:
			return D3DTOP_MODULATE;

		case IdeRenderTexture::TEXTUREOP_MODULATE2X:
			return D3DTOP_MODULATE2X;

		case IdeRenderTexture::TEXTUREOP_MODULATE4X:
			return D3DTOP_MODULATE4X;

		case IdeRenderTexture::TEXTUREOP_ADD:
			return D3DTOP_ADD;

		case IdeRenderTexture::TEXTUREOP_ADDSIGNED:
			return D3DTOP_ADDSIGNED;

		case IdeRenderTexture::TEXTUREOP_ADDSIGNED2X:
			return D3DTOP_ADDSIGNED2X;

		case IdeRenderTexture::TEXTUREOP_SUBTRACT:
			return D3DTOP_SUBTRACT;

		case IdeRenderTexture::TEXTUREOP_ADDSMOOTH:
			return D3DTOP_ADDSMOOTH;

		case IdeRenderTexture::TEXTUREOP_BLENDDIFFUSEALPHA:
			return D3DTOP_BLENDDIFFUSEALPHA;

		case IdeRenderTexture::TEXTUREOP_BLENDTEXTUREALPHA:
			return D3DTOP_BLENDTEXTUREALPHA;

		case IdeRenderTexture::TEXTUREOP_BLENDTEXTUREALPHAPM:
			return D3DTOP_BLENDTEXTUREALPHAPM;

		case IdeRenderTexture::TEXTUREOP_BLENDFACTORALPHA:
			return D3DTOP_BLENDFACTORALPHA;

		case IdeRenderTexture::TEXTUREOP_BLENDPREVIOUSALPHA:
			return D3DTOP_BLENDCURRENTALPHA;

		case IdeRenderTexture::TEXTUREOP_PREMODULATE:
			return D3DTOP_PREMODULATE;

		case IdeRenderTexture::TEXTUREOP_MODULATECOLOR_ADDALPHA:
			return D3DTOP_MODULATECOLOR_ADDALPHA;

		case IdeRenderTexture::TEXTUREOP_MODULATEALPHA_ADDCOLOR:
			return D3DTOP_MODULATEALPHA_ADDCOLOR;

		case IdeRenderTexture::TEXTUREOP_MODULATEINVCOLOR_ADDALPHA:
			return D3DTOP_MODULATEINVCOLOR_ADDALPHA;

		case IdeRenderTexture::TEXTUREOP_MODULATEINVALPHA_ADDCOLOR:
			return D3DTOP_MODULATEINVALPHA_ADDCOLOR;

		case IdeRenderTexture::TEXTUREOP_BUMPENVMAP:
			return D3DTOP_BUMPENVMAP;

		case IdeRenderTexture::TEXTUREOP_BUMPENVMAPLUMINANCE:
			return D3DTOP_BUMPENVMAPLUMINANCE;

		case IdeRenderTexture::TEXTUREOP_DOTPRODUCT3:
			return D3DTOP_DOTPRODUCT3;

		case IdeRenderTexture::TEXTUREOP_MULTIPLYADD:
			return D3DTOP_MULTIPLYADD;

		case IdeRenderTexture::TEXTUREOP_LERP:
			return D3DTOP_LERP;
	};

	//disabled if we don't know what it is
	return D3DTOP_DISABLE;
}

long GetD3DTextureArg(IdeRenderTexture::TextureArg Args)
{
	long Ret;
	
	Ret = 0;

	//see what flags are set and set the appropriate d3d flags
	if(Args & IdeRenderTexture::TEXTUREARG_PREVIOUS)
		Ret = D3DTA_CURRENT;
	else if(Args & IdeRenderTexture::TEXTUREARG_DIFFUSE)
		Ret = D3DTA_DIFFUSE;
	else if(Args & IdeRenderTexture::TEXTUREARG_SPECULAR)
		Ret = D3DTA_SPECULAR;
	else if(Args & IdeRenderTexture::TEXTUREARG_TEXTURE)
		Ret = D3DTA_TEXTURE;
	else if(Args & IdeRenderTexture::TEXTUREARG_TFACTOR)
		Ret = D3DTA_TFACTOR;
	else if(Args & IdeRenderTexture::TEXTUREARG_TEMPREG)
		Ret = D3DTA_TEMP;
	
	//set any modifying flags
	if(Args & IdeRenderTexture::TEXTUREARG_ALPHAREPLICATE)
		Ret = Ret | D3DTA_ALPHAREPLICATE;

	if(Args & IdeRenderTexture::TEXTUREARG_COMPLEMENT)
		Ret = Ret | D3DTA_COMPLEMENT;

	return Ret;
}

D3DTEXTUREADDRESS GetD3DTextureAddress(IdeRenderTexture::TextureAddress Address)
{
	//return the correct D3D type texture address
	switch(Address)
	{
		case IdeRenderTexture::TEXTUREADDRESS_WRAP:
			return D3DTADDRESS_WRAP;

		case IdeRenderTexture::TEXTUREADDRESS_MIRROR:
			return D3DTADDRESS_MIRROR;

		case IdeRenderTexture::TEXTUREADDRESS_CLAMP:
			return D3DTADDRESS_CLAMP;
	}

	//default to wrap
	return D3DTADDRESS_WRAP;
}

D3DTEXTUREFILTERTYPE GetD3DTextureFilter(IdeRenderTexture::TextureFilter Filter)
{
	//return the correct D3D type texture filter
	switch(Filter)
	{
	case IdeRenderTexture::TEXTUREFILTER_NONE:
		return D3DTEXF_NONE;
		
	case IdeRenderTexture::TEXTUREFILTER_POINT:
		return D3DTEXF_POINT;

	case IdeRenderTexture::TEXTUREFILTER_LINEAR:
		return D3DTEXF_LINEAR;

	case IdeRenderTexture::TEXTUREFILTER_ANISOTROPIC:
		return D3DTEXF_ANISOTROPIC;

	//default to point-sample
	default:
		return D3DTEXF_POINT;
	}
}

deBoolean deDirect3D9::SetTextureState(IdeRenderTexture *State)
{
	TextureEntry*					Entry;
	IdeBitmapProxy*					BitmapProxy;
	IdeBitmap*						Bitmap;
	LPDIRECT3DBASETEXTURE9			Texture;
	long							Stage;
	long							TempVal;
	IdeRenderTexture::TextureOp		ColorOp, AlphaOp;
	IdeRenderTexture::TextureArg	Arg1, Arg2, Arg3;
	
	//set texture information

	//lock the renderstate
	if(!State->Lock())
		return deFALSE;

	if (!m_CachedTexture->Lock())
		return deFALSE;

	//go thru all 8 texture stages
	// until we hit a no-op stage
	for(Stage = 0; Stage < 8; Stage++)
	{
		Texture = NULL;
		BitmapProxy = State->GetBitmap(Stage);
		if (BitmapProxy)
			Bitmap = BitmapProxy->GetBitmap();
		else
			Bitmap = NULL;
		if (Bitmap && Bitmap != m_pCurrentTarget)
		{
			#ifdef _DEBUG
			{
				char buffer[256];
				IdeResourceBase * rsc;
				rsc = (IdeResourceBase *)Bitmap->GetRscInterface(0);
				rsc->GetFilenameBuffer(buffer, 256);
			}
			#endif
			Entry = (TextureEntry*)Bitmap->GetDriverData(m_UniqueID);
			if(Entry)
			{
				//we have a texture entry, verify the ID, if valid then modify the pointer
				//otherwise, exit
				if(Entry->ID != TEXTURESTRUCTID)
				{
					State->Unlock();
					m_CachedTexture->Unlock();
					DEBUGRETURN(deFALSE, "SetTextureState: ID mismatch");
				}

			}
			else
			{
				if (!AddBitmap(Bitmap))
				{
					return deFALSE;
				}
				Entry = (TextureEntry*)Bitmap->GetDriverData(m_UniqueID);
				if (!Entry)
				{
					State->Unlock();
					m_CachedTexture->Unlock();
					DEBUGRETURN(deFALSE, "SetTextureState: Failure Loading Bitmap");
				}
			}
			
			//it is valid, modify the pointer. this way texture can be null and we still
			//send the right value to SetTexture
			Texture = Entry->Texture;
		}

		// I'd like to compare the pointer here, but there's a chance it can be the same after a deletion and re-creation
		// so we're gonna have to set the texture whether the pointers match or not
		if ((Bitmap != NULL) || (Bitmap == NULL && m_CachedTexture->GetBitmap(Stage) != NULL))
		{
			//set the texture
			if(m_D3DDevice->SetTexture(Stage, Texture) != D3D_OK)
			{
				goto texturestate_fail;
			}
			m_CachedTexture->SetBitmap(Stage, BitmapProxy);
		}

		//set a few renderstates

		ColorOp = State->GetColorOperation(Stage);
		AlphaOp = State->GetAlphaOperation(Stage);
		if (ColorOp != IdeRenderTexture::TEXTUREOP_DISABLE || AlphaOp != IdeRenderTexture::TEXTUREOP_DISABLE)
		{
			TempVal = State->GetTextureAddressU(Stage);
			if (TempVal != m_CachedTexture->GetTextureAddressU(Stage))
			{
				//set how it will look for U
				if(m_D3DDevice->SetSamplerState(Stage, D3DSAMP_ADDRESSU, GetD3DTextureAddress((IdeRenderTexture::TextureAddress)TempVal)
					) != D3D_OK)
				{
					goto texturestate_fail;
				}
				m_CachedTexture->SetTextureAddressU(Stage, (IdeRenderTexture::TextureAddress)TempVal);
			}

			TempVal = State->GetTextureAddressV(Stage);
			if (TempVal != m_CachedTexture->GetTextureAddressV(Stage))
			{
				//set how it will look for V
				if(m_D3DDevice->SetSamplerState(Stage, D3DSAMP_ADDRESSV, GetD3DTextureAddress((IdeRenderTexture::TextureAddress)TempVal)) != D3D_OK)
				{
					goto texturestate_fail;
				}
				m_CachedTexture->SetTextureAddressV(Stage, (IdeRenderTexture::TextureAddress)TempVal);
			}

			TempVal = State->GetTextureMinFilter(Stage);
			if (TempVal != m_CachedTexture->GetTextureMinFilter(Stage))
			{
				//set how it will minify (multiple texels in one pixel)
				if(m_D3DDevice->SetSamplerState(Stage, D3DSAMP_MINFILTER, GetD3DTextureFilter((IdeRenderTexture::TextureFilter)TempVal)) != D3D_OK)
				{
					goto texturestate_fail;
				}
				m_CachedTexture->SetTextureMinFilter(Stage, (IdeRenderTexture::TextureFilter)TempVal);
			}

			TempVal = State->GetTextureMagFilter(Stage);
			if (TempVal != m_CachedTexture->GetTextureMagFilter(Stage))
			{
				//set how it will magnify (one texel affecting multiple pixels)
				if(m_D3DDevice->SetSamplerState(Stage, D3DSAMP_MAGFILTER, GetD3DTextureFilter((IdeRenderTexture::TextureFilter)TempVal)) != D3D_OK)
				{
					goto texturestate_fail;
				}
				m_CachedTexture->SetTextureMagFilter(Stage, (IdeRenderTexture::TextureFilter)TempVal);
			}

			TempVal = State->GetTextureMipFilter(Stage);
			if (TempVal != m_CachedTexture->GetTextureMipFilter(Stage))
			{
				//set how it will blend mipmaps
				if(m_D3DDevice->SetSamplerState(Stage, D3DSAMP_MIPFILTER, GetD3DTextureFilter((IdeRenderTexture::TextureFilter)TempVal)) != D3D_OK)
				{
					goto texturestate_fail;
				}
				m_CachedTexture->SetTextureMipFilter(Stage, (IdeRenderTexture::TextureFilter)TempVal);
			}

			TempVal = State->GetTexCoordSourceCombined(Stage);
			if (TempVal != m_CachedTexture->GetTexCoordSourceCombined(Stage))
			{
				//set where it grabs texture coordinates from
				if (m_D3DDevice->SetTextureStageState(Stage, D3DTSS_TEXCOORDINDEX, TempVal) != D3D_OK)
				{
					goto texturestate_fail;
				}
				m_CachedTexture->SetTexCoordGen(Stage, (IdeRenderTexture::TextureCoordSource)(TempVal&0xffff0000), TempVal&0x0000ffff);
			}

			TempVal = State->GetTextureProjection(Stage);
			//set a texture projection state
			if (TempVal)
			{
				if (m_D3DDevice->SetTextureStageState(Stage, D3DTSS_TEXTURETRANSFORMFLAGS, D3DTTFF_COUNT2 ) != D3D_OK)
				{
					goto texturestate_fail;
				}
			}
			else
			{
				if (m_D3DDevice->SetTextureStageState(Stage, D3DTSS_TEXTURETRANSFORMFLAGS, D3DTTFF_DISABLE ) != D3D_OK)
				{
					goto texturestate_fail;
				}
			}
			m_CachedTexture->SetTextureProjection(Stage, TempVal ? deTRUE : deFALSE);

			TempVal = State->GetResultTarget(Stage);
			if (TempVal != m_CachedTexture->GetResultTarget(Stage))
			{
				//set where it stores its result to (either CURRENT or TEMP)
				if(m_D3DDevice->SetTextureStageState(Stage, D3DTSS_RESULTARG, GetD3DTextureArg((IdeRenderTexture::TextureArg)TempVal)) != D3D_OK)
				{
					goto texturestate_fail;
				}
				m_CachedTexture->SetResultTarget(Stage, (IdeRenderTexture::TextureArg)TempVal);
			}
			
			//only set the params if the texture is not disabled
			if(ColorOp == IdeRenderTexture::TEXTUREOP_DISABLE)
			{
				// if only color was disabled (alpha not) then we just carry through
				ColorOp = IdeRenderTexture::TEXTUREOP_SELECTARG1;
				if(m_D3DDevice->SetTextureStageState(Stage, D3DTSS_COLORARG1, GetD3DTextureArg(IdeRenderTexture::TEXTUREARG_PREVIOUS)) != D3D_OK)
				{
					goto texturestate_fail;
				}
			}
			else
			{
				Arg1 = State->GetColorArg(Stage, 1);
				Arg2 = State->GetColorArg(Stage, 2);
				Arg3 = State->GetColorArg(Stage, 3);

				if (ColorOp != IdeRenderTexture::TEXTUREOP_SELECTARG2)
				{
					if (Bitmap == NULL && Arg1 == IdeRenderTexture::TEXTUREARG_TEXTURE)
					{
						NULLTEXWARNING("Attempt to reference NULL texture in texture stage %d\n", Stage);
					}
					//set the texture argument 1
					if(m_D3DDevice->SetTextureStageState(Stage, D3DTSS_COLORARG1, GetD3DTextureArg(Arg1)) != D3D_OK)
					{
						goto texturestate_fail;
					}
					m_CachedTexture->SetColorArg(Stage, 1, Arg1);
				}

				if (ColorOp != IdeRenderTexture::TEXTUREOP_SELECTARG1)
				{
					if (Bitmap == NULL && Arg2 == IdeRenderTexture::TEXTUREARG_TEXTURE)
					{
						NULLTEXWARNING("Attempt to reference NULL texture in texture stage %d\n", Stage);
					}
					//set the texture argument 2
					if(m_D3DDevice->SetTextureStageState(Stage, D3DTSS_COLORARG2, GetD3DTextureArg(Arg2)) != D3D_OK)
					{
						goto texturestate_fail;
					}
					m_CachedTexture->SetColorArg(Stage, 2, Arg2);
				}
				
				//set the texture argument 3 if the mode uses it
				
				//only set arg 3 if it is used
				if(ColorOp == IdeRenderTexture::TEXTUREOP_MULTIPLYADD || ColorOp == IdeRenderTexture::TEXTUREOP_LERP)
				{
					if (Bitmap == NULL && Arg3 == IdeRenderTexture::TEXTUREARG_TEXTURE)
					{
						NULLTEXWARNING("Attempt to reference NULL texture in texture stage %d\n", Stage);
					}
					//now WHY did Microsoft name the 3rd #0? - Lightning
					// heh, pretty stupid if ya ask me - Assassin
					if(m_D3DDevice->SetTextureStageState(Stage, D3DTSS_COLORARG0, GetD3DTextureArg(Arg3)) != D3D_OK)
					{
						goto texturestate_fail;
					}
					m_CachedTexture->SetColorArg(Stage, 3, Arg3);
				}
			}

			//set the color operation
			if(m_D3DDevice->SetTextureStageState(Stage, D3DTSS_COLOROP, GetD3DTextureOp(ColorOp)) != D3D_OK)
			{
				goto texturestate_fail;
			}
			m_CachedTexture->SetColorOperation(Stage, ColorOp);
	
			//only set the params if the alpha is not disabled
			if(AlphaOp == IdeRenderTexture::TEXTUREOP_DISABLE)
			{
				// if only alpha was disabled (color not) then we just carry through
				AlphaOp = IdeRenderTexture::TEXTUREOP_SELECTARG1;
				if(m_D3DDevice->SetTextureStageState(Stage, D3DTSS_ALPHAARG1, GetD3DTextureArg(IdeRenderTexture::TEXTUREARG_PREVIOUS)) != D3D_OK)
				{
					goto texturestate_fail;
				}
			}
			else
			{
				Arg1 = State->GetColorArg(Stage, 1);
				Arg2 = State->GetColorArg(Stage, 2);
				Arg3 = State->GetColorArg(Stage, 3);
				
				if (AlphaOp != IdeRenderTexture::TEXTUREOP_SELECTARG2)
				{
					if (Bitmap == NULL && Arg1 == IdeRenderTexture::TEXTUREARG_TEXTURE)
					{
						NULLTEXWARNING("Attempt to reference NULL texture in texture stage %d\n", Stage);
					}
					//set the alpha argument 1
					if(m_D3DDevice->SetTextureStageState(Stage, D3DTSS_ALPHAARG1, GetD3DTextureArg(Arg1)) != D3D_OK)
					{
						goto texturestate_fail;
					}
					m_CachedTexture->SetAlphaArg(Stage, 1, Arg1);
				}

				if (AlphaOp != IdeRenderTexture::TEXTUREOP_SELECTARG1)
				{
					if (Bitmap == NULL && Arg2 == IdeRenderTexture::TEXTUREARG_TEXTURE)
					{
						NULLTEXWARNING("Attempt to reference NULL texture in texture stage %d\n", Stage);
					}
					//set the alpha argument 2
					if(m_D3DDevice->SetTextureStageState(Stage, D3DTSS_ALPHAARG2, GetD3DTextureArg(Arg2)) != D3D_OK)
					{
						goto texturestate_fail;
					}
					m_CachedTexture->SetAlphaArg(Stage, 2, Arg2);
				}

				//only set arg 3 if it is used
				if(AlphaOp == IdeRenderTexture::TEXTUREOP_MULTIPLYADD || AlphaOp == IdeRenderTexture::TEXTUREOP_LERP)
				{
					if (Bitmap == NULL && Arg3 == IdeRenderTexture::TEXTUREARG_TEXTURE)
					{
						NULLTEXWARNING("Attempt to reference NULL texture in texture stage %d\n", Stage);
					}
					//set the alpha argument 3
					if(m_D3DDevice->SetTextureStageState(Stage, D3DTSS_ALPHAARG0, GetD3DTextureArg(Arg3)) != D3D_OK)
					{
						goto texturestate_fail;
					}
					m_CachedTexture->SetAlphaArg(Stage, 3, Arg3);
				}
			}
			
			//set the alpha operation
			if(m_D3DDevice->SetTextureStageState(Stage, D3DTSS_ALPHAOP, GetD3DTextureOp(AlphaOp)) != D3D_OK)
			{
				goto texturestate_fail;
			}
			m_CachedTexture->SetAlphaOperation(Stage, AlphaOp);
		}
		else
		{
			m_CachedTexture->SetColorOperation(Stage, IdeRenderTexture::TEXTUREOP_DISABLE);
			m_CachedTexture->SetAlphaOperation(Stage, IdeRenderTexture::TEXTUREOP_DISABLE);
			if(m_D3DDevice->SetTextureStageState(Stage, D3DTSS_COLOROP, D3DTOP_DISABLE) != D3D_OK)
			{
				goto texturestate_fail;
			}
			if(m_D3DDevice->SetTextureStageState(Stage, D3DTSS_ALPHAOP, D3DTOP_DISABLE) != D3D_OK)
			{
				goto texturestate_fail;
			}
			break;
		}
	}

	//Unlock the renderstate
	if(!State->Unlock())
	{
		m_CachedTexture->Unlock();
		return deFALSE;
	}

	if (!m_CachedTexture->Unlock())
		return deFALSE;

	return deTRUE;

texturestate_fail:
	State->Unlock();
	m_CachedTexture->Unlock();
	return deFALSE;
}

deBoolean deDirect3D9::SetLightState(IdeRenderLight *State)
{
	//set light information
	D3DLIGHT9		Light;

	long LightID = State->GetID();
	if (LightID < 0 || LightID >= deD3D9_MAX_LIGHTS)
	{
		DEBUGMSG2("SetLightState: Bad Light ID: %d, Max ID: %d", LightID, deD3D9_MAX_LIGHTS);
		return deFALSE;
	}

	//make sure no one is modifying the light
	if(!State->Lock())
		return deFALSE;

	//if they are turning it on, set a bunch of params
	if(State->GetStatus())
	{
		//turning a light on, set up a bunch of params

		//set a few params
		Light.Falloff = State->GetFallOff();
		Light.Phi = State->GetPhi();
		Light.Range = State->GetRange();
		Light.Theta = State->GetTheta();

		//set the light type
		switch(State->GetLightType())
		{
			case State->LIGHTTYPE_POINT:
				Light.Type = D3DLIGHT_POINT;
				break;

			case State->LIGHTTYPE_SPOT:
				Light.Type = D3DLIGHT_SPOT;
				break;

			case State->LIGHTTYPE_DIRECTIONAL:
				Light.Type = D3DLIGHT_DIRECTIONAL;
				break;

			default:
				State->Unlock();
				DEBUGRETURN(deFALSE, "SetTextureState: Unknown Light Type");
		}

		//get color params

		//at least we use the same format for the float values for colors so this makes the copy quick
		State->GetAmbientColor((deColor *)&Light.Ambient);
		State->GetDiffuseColor((deColor *)&Light.Diffuse);
		State->GetSpecularColor((deColor *)&Light.Specular);

		//get Attenuation
		Light.Attenuation0 = State->GetAttenuation(0);
		Light.Attenuation1 = State->GetAttenuation(1);
		Light.Attenuation2 = State->GetAttenuation(2);

		//get direction and position
		State->GetDirection((deVertex *)&Light.Direction);
		State->GetPosition((deVertex *)&Light.Position);

		//set it
		if(m_D3DDevice->SetLight(LightID, &Light) != D3D_OK)
		{
			State->Unlock();
			return deFALSE;
		}

		//turn it on
		if(m_D3DDevice->LightEnable(LightID, TRUE) != D3D_OK)
		{
			State->Unlock();
			return deFALSE;
		}

		m_LightsEnabled[LightID] = deTRUE;
	}
	else
	{
		//just turn it off
		if(m_D3DDevice->LightEnable(LightID, FALSE) != D3D_OK)
		{
			State->Unlock();
			return deFALSE;
		}
		
		m_LightsEnabled[LightID] = deFALSE;
	}

	State->Unlock();
	return deTRUE;
}

deBoolean deDirect3D9::SetMaterialState(IdeRenderMaterial *State)
{
	D3DMATERIAL9	Material;

	//set the material to D3D

	//make sure no one is modifying it
	if(!State->Lock())
		return deFALSE;

	//D3D stores the floats the same way we do, just read into it
	State->GetAmbientColor((deColor *)&Material.Ambient);
	State->GetDiffuseColor((deColor *)&Material.Diffuse);
	State->GetEmissiveColor((deColor *)&Material.Emissive);
	State->GetSpecularColor((deColor *)&Material.Specular);

	//get the power of the material
	Material.Power = State->GetSpecularPower();

	//set it
	if(m_D3DDevice->SetMaterial(&Material) != D3D_OK)
	{
		State->Unlock();
		return deFALSE;
	}

	IdeRenderMaterial::MatSource_t source;
	source = State->GetAmbientSource();
	if (source != m_AmbientSource)
	{
		if (m_D3DDevice->SetRenderState(D3DRS_AMBIENTMATERIALSOURCE, ToD3DMatSource(source)) != D3D_OK)
			return deFALSE;
	}
	source = State->GetDiffuseSource();
	if (source != m_DiffuseSource)
	{
		if (m_D3DDevice->SetRenderState(D3DRS_DIFFUSEMATERIALSOURCE, ToD3DMatSource(source)) != D3D_OK)
			return deFALSE;
	}
	source = State->GetSpecularSource();
	if (source != m_SpecularSource)
	{
		if (m_D3DDevice->SetRenderState(D3DRS_SPECULARMATERIALSOURCE, ToD3DMatSource(source)) != D3D_OK)
			return deFALSE;
	}
	source = State->GetEmissiveSource();
	if (source != m_EmissiveSource)
	{
		if (m_D3DDevice->SetRenderState(D3DRS_EMISSIVEMATERIALSOURCE, ToD3DMatSource(source)) != D3D_OK)
			return deFALSE;
	}
	
	//Unlock it so others can modify it
	State->Unlock();

	return deTRUE;
}

deBoolean deDirect3D9::SetRenderState(const IdeRenderState *State)
{
	//go call a function to set the states for each render state and return
	//if it worked or not

	//check that the state is not null so John doesn't crash the driver
	if(!State)
		DEBUGRETURN(deFALSE, "SetRenderState: Trying to set NULL state");
	
	if (m_DeviceLost)
		return deTRUE;
	
	IdeRenderState::Type type = State->GetRSType();
	switch(type)
	{
	case IdeRenderState::TYPE_TEXTURE:
		return SetTextureState((IdeRenderTexture *)(State->GetRSInterface(type)));

	case IdeRenderState::TYPE_LIGHT:
		return SetLightState((IdeRenderLight *)(State->GetRSInterface(type)));

	case IdeRenderState::TYPE_MATERIAL:
		return SetMaterialState((IdeRenderMaterial *)(State->GetRSInterface(type)));
	}

	//if we got here, we don't know the type, fail
	return deFALSE;
}

deBoolean deDirect3D9::SetRenderState(RenderState State, unsigned long Value)
{
	if (m_DeviceLost)
		return deTRUE;
	
	HRESULT				Ret = D3DERR_INVALIDCALL;
	D3DRENDERSTATETYPE	D3DState;

	//set a renderstate based on what they want

	//first, make sure the device is valid
	if(!m_D3DDevice)
		return deFALSE;

	//check if the value they are setting is already set, if so, don't set it again
	if(m_RenderStatesValid && m_RenderStates[State] == Value)
		return deTRUE;

	//set the value
	m_RenderStates[State] = Value;
	
	//figure out which state they want and set the state and value appropriately
	switch(State)
	{
		case RENDER_ZENABLE:
			if (Value)
				Value = D3DZB_TRUE;
			else
				Value = D3DZB_FALSE;
			Ret = m_D3DDevice->SetRenderState(D3DRS_ZENABLE, Value);
			break;

		case RENDER_ZENABLEUSEW:
			if (Value)
				Value = D3DZB_USEW;
			else
				Value = D3DZB_FALSE;
			Ret = m_D3DDevice->SetRenderState(D3DRS_ZENABLE, Value);
			break;
			
			
		case RENDER_ZWRITESENABLE:
			Ret = m_D3DDevice->SetRenderState(D3DRS_ZWRITEENABLE, Value);
			break;

		case RENDER_FSAAENABLE:
			Ret = m_D3DDevice->SetRenderState(D3DRS_MULTISAMPLEANTIALIAS, Value);
			break;
			
		case RENDER_FSAAMASK:
			Ret = m_D3DDevice->SetRenderState(D3DRS_MULTISAMPLEMASK, Value);
			break;
			
		case RENDER_FILLMODE:
			//set the ret based on the value passed in
			if(Value == FILL_POINT)
					Ret = m_D3DDevice->SetRenderState(D3DRS_FILLMODE, D3DFILL_POINT);
			else if(Value == FILL_SOLID)
					Ret = m_D3DDevice->SetRenderState(D3DRS_FILLMODE, D3DFILL_SOLID);
			else if(Value == FILL_WIREFRAME)
					Ret = m_D3DDevice->SetRenderState(D3DRS_FILLMODE, D3DFILL_WIREFRAME);
			else
					Ret = D3DERR_INVALIDCALL;			//not a value we allow, fail
			break;

		case RENDER_SHADEMODE:
			//set the render based on the value
			if(Value == SHADE_FLAT)
				Ret = m_D3DDevice->SetRenderState(D3DRS_SHADEMODE, D3DSHADE_FLAT);
			else if(Value == SHADE_GOURAUD)
				Ret = m_D3DDevice->SetRenderState(D3DRS_SHADEMODE, D3DSHADE_GOURAUD);
			else
				Ret = D3DERR_INVALIDCALL;
			break;

		case RENDER_COLORMASK:
			{
				u32 mask = 0;
				if (Value & 0xff000000)
					mask |= D3DCOLORWRITEENABLE_ALPHA;
				if (Value & 0x00ff0000)
					mask |= D3DCOLORWRITEENABLE_RED;
				if (Value & 0x0000ff00)
					mask |= D3DCOLORWRITEENABLE_GREEN;
				if (Value & 0x000000ff)
					mask |= D3DCOLORWRITEENABLE_BLUE;
				Ret = m_D3DDevice->SetRenderState(D3DRS_COLORWRITEENABLE, mask);
			}
			break;

		case RENDER_ALPHATESTENABLE:
			Ret = m_D3DDevice->SetRenderState(D3DRS_ALPHATESTENABLE, Value);
			break;

		case RENDER_ALPHAREF:
			Ret = m_D3DDevice->SetRenderState(D3DRS_ALPHAREF, Value);
			break;

		case RENDER_DEPTHFUNCTION:
		case RENDER_ALPHAFUNCTION:
		case RENDER_STENCILFUNCTION:
		case RENDER_CCW_STENCILFUNCTION:
			//set the alpha or stencil function used
			{
				D3DCMPFUNC			Func;				
				switch (Value)
				{
				case COMPARE_NEVER:
					Func = D3DCMP_NEVER;
					break;
				case COMPARE_LESS:
					Func = D3DCMP_LESS;
					break;
				case COMPARE_EQUAL:
					Func = D3DCMP_EQUAL;
					break;
				case COMPARE_LESSEQUAL:
					Func = D3DCMP_LESSEQUAL;
					break;
				case COMPARE_GREATER:
					Func = D3DCMP_GREATER;
					break;
				case COMPARE_GREATEREQUAL:
					Func = D3DCMP_GREATEREQUAL;
					break;
				case COMPARE_NOTEQUAL:
					Func = D3DCMP_NOTEQUAL;
					break;
				case COMPARE_ALWAYS:
					Func = D3DCMP_ALWAYS;
					break;
				default:
					DEBUGRETURN(deFALSE, "SetRenderState: Unknown Comparison Value");
				}
				if (State == RENDER_STENCILFUNCTION)
					Ret = m_D3DDevice->SetRenderState(D3DRS_STENCILFUNC, Func);
				else if (State == RENDER_ALPHAFUNCTION)
					Ret = m_D3DDevice->SetRenderState(D3DRS_ALPHAFUNC, Func);
				else if (State == RENDER_DEPTHFUNCTION)
					Ret = m_D3DDevice->SetRenderState(D3DRS_ZFUNC, Func);
				else if (State == RENDER_CCW_STENCILFUNCTION)
					Ret = m_D3DDevice->SetRenderState(D3DRS_CCW_STENCILFUNC, Func);
			}
			break;
			
		case RENDER_ALPHABLENDENABLE:
			Ret = m_D3DDevice->SetRenderState(D3DRS_ALPHABLENDENABLE, Value);
			break;

		case RENDER_SOURCEBLEND:
		case RENDER_DESTBLEND:
			//set the type
			if(State == RENDER_SOURCEBLEND)
				D3DState = D3DRS_SRCBLEND;
			else
				D3DState = D3DRS_DESTBLEND;

			//figure out the type of blending
			switch(Value)
			{
			case BLEND_ZERO:
				Ret = m_D3DDevice->SetRenderState(D3DState, D3DBLEND_ZERO);
				break;
			case BLEND_ONE:
				Ret = m_D3DDevice->SetRenderState(D3DState, D3DBLEND_ONE);
				break;
			case BLEND_SOURCE:
				Ret = m_D3DDevice->SetRenderState(D3DState, D3DBLEND_SRCCOLOR);
				break;
			case BLEND_INVERSESOURCE:
				Ret = m_D3DDevice->SetRenderState(D3DState, D3DBLEND_INVSRCCOLOR);
				break;
			case BLEND_SOURCEALPHA:
				Ret = m_D3DDevice->SetRenderState(D3DState, D3DBLEND_SRCALPHA);
				break;
			case BLEND_INVERSESOURCEALPHA:
				Ret = m_D3DDevice->SetRenderState(D3DState, D3DBLEND_INVSRCALPHA);
				break;
			case BLEND_DEST:
				Ret = m_D3DDevice->SetRenderState(D3DState, D3DBLEND_DESTCOLOR);
				break;
			case BLEND_INVERSEDEST:
				Ret = m_D3DDevice->SetRenderState(D3DState, D3DBLEND_INVDESTCOLOR);
				break;
			case BLEND_DESTALPHA:
				Ret = m_D3DDevice->SetRenderState(D3DState, D3DBLEND_DESTALPHA);
				break;
			case BLEND_INVERSEDESTALPHA:
				Ret = m_D3DDevice->SetRenderState(D3DState, D3DBLEND_INVDESTALPHA);
				break;
			case BLEND_SOURCEALPHASAT:
				Ret = m_D3DDevice->SetRenderState(D3DState, D3DBLEND_SRCALPHASAT);
				break;
			default:
				Ret = D3DERR_INVALIDCALL;
				break;
			}
			break;

		case RENDER_TEXTUREFACTOR:
			Ret = m_D3DDevice->SetRenderState(D3DRS_TEXTUREFACTOR, Value);
			break;

		case RENDER_CULLMODE:
			//set the cull mode based on the value passed in
			if(Value == CULL_NONE)
				Ret = m_D3DDevice->SetRenderState(D3DRS_CULLMODE, D3DCULL_NONE);
			else if(Value == CULL_CW)
				Ret = m_D3DDevice->SetRenderState(D3DRS_CULLMODE, D3DCULL_CW);
			else if(Value == CULL_CCW)
				Ret = m_D3DDevice->SetRenderState(D3DRS_CULLMODE, D3DCULL_CCW);
			else
				Ret = D3DERR_INVALIDCALL;
			break;

		case RENDER_FOGENABLE:
			switch (Value)
			{
			default:
				break;
			case 0:
				Ret = m_D3DDevice->SetRenderState(D3DRS_FOGENABLE, FALSE);
				break;
			case 1:
				Ret = m_D3DDevice->SetRenderState(D3DRS_FOGENABLE, TRUE);
				if (Ret == D3D_OK)
					Ret = m_D3DDevice->SetRenderState(D3DRS_RANGEFOGENABLE, FALSE);
//				if (Ret == D3D_OK)
//					Ret = m_D3DDevice->SetRenderState(D3DRS_FOGTABLEMODE, D3DFOG_LINEAR);
				if (Ret == D3D_OK)
					Ret = m_D3DDevice->SetRenderState(D3DRS_FOGVERTEXMODE, D3DFOG_LINEAR);
				break;
			case 2:
				Ret = m_D3DDevice->SetRenderState(D3DRS_FOGENABLE, TRUE);
				if (Ret == D3D_OK)
					Ret = m_D3DDevice->SetRenderState(D3DRS_RANGEFOGENABLE, TRUE);
//				if (Ret == D3D_OK)
//					Ret = m_D3DDevice->SetRenderState(D3DRS_FOGTABLEMODE, D3DFOG_LINEAR);
				if (Ret == D3D_OK)
					Ret = m_D3DDevice->SetRenderState(D3DRS_FOGVERTEXMODE, D3DFOG_LINEAR);
				break;
			}
			break;

		case RENDER_FOGSTART:
			Ret = m_D3DDevice->SetRenderState(D3DRS_FOGSTART, Value);
			break;

		case RENDER_FOGEND:
			Ret = m_D3DDevice->SetRenderState(D3DRS_FOGEND, Value);
			break;

		case RENDER_FOGCOLOR:
			Ret = m_D3DDevice->SetRenderState(D3DRS_FOGCOLOR, Value);
			break;

		case RENDER_CLIPPINGENABLE:
			Ret = m_D3DDevice->SetRenderState(D3DRS_CLIPPING, Value);
			break;

		case RENDER_SWVERTEXPROCESS:
			Ret = m_D3DDevice->SetSoftwareVertexProcessing((BOOL)Value);
			break;

		case RENDER_HWLIGHTINGENABLE:
			Ret = m_D3DDevice->SetRenderState(D3DRS_LIGHTING, Value);
			break;

		case RENDER_AMBIENTLIGHT:
			Ret = m_D3DDevice->SetRenderState(D3DRS_AMBIENT, ((Value<<8) & 0xffffff00) | ((Value>>24) & 0xff));
			break;

		case RENDER_DITHERENABLE:
			Ret = m_D3DDevice->SetRenderState(D3DRS_DITHERENABLE, Value);
			break;

		case RENDER_SPECULARENABLE:
			Ret = m_D3DDevice->SetRenderState(D3DRS_SPECULARENABLE, Value);
			break;

		case RENDER_SPECULARPERSPECTIVE:
			Ret = m_D3DDevice->SetRenderState(D3DRS_LOCALVIEWER, Value);
			break;

		case RENDER_PERVERTEXCOLOR:
			Ret = m_D3DDevice->SetRenderState(D3DRS_COLORVERTEX, Value);
			break;

		case RENDER_STENCILENABLE:
			Ret = m_D3DDevice->SetRenderState(D3DRS_STENCILENABLE, Value);
			break;

		case RENDER_TWOSIDEDSTENCILENABLE:
			// Must set cull mode to none
			//m_D3DDevice->SetRenderState(D3DRS_CULLMODE, D3DCULL_NONE);
			Ret = m_D3DDevice->SetRenderState(D3DRS_TWOSIDEDSTENCILMODE, Value);
			break;
			
		case RENDER_STENCILREF:
			Ret = m_D3DDevice->SetRenderState(D3DRS_STENCILREF, Value);
			break;
			
		case RENDER_STENCILMASK:
			Ret = m_D3DDevice->SetRenderState(D3DRS_STENCILMASK, Value);
			break;
			
		case RENDER_STENCILWRITEMASK:
			Ret = m_D3DDevice->SetRenderState(D3DRS_STENCILWRITEMASK, Value);
			break;
			
		// merged with ALPHAFUNCTION
		//case RENDER_STENCILFUNCTION:
			
		case RENDER_STENCILFAIL:
		case RENDER_STENCILZFAIL:
		case RENDER_STENCILPASS:
		
		// Two sided stenciling
		case RENDER_CCW_STENCILFAIL:
		case RENDER_CCW_STENCILZFAIL:
		case RENDER_CCW_STENCILPASS:
			{
				D3DSTENCILOP Op;
				switch (Value)
				{
				case STENCIL_KEEP:
					Op = D3DSTENCILOP_KEEP;
					break;
				case STENCIL_ZERO:
					Op = D3DSTENCILOP_ZERO;
					break;
				case STENCIL_REPLACE:
					Op = D3DSTENCILOP_REPLACE;
					break;
				case STENCIL_INCRWRAP:
					Op = D3DSTENCILOP_INCRSAT;
					break;
				case STENCIL_DECRWRAP:
					Op = D3DSTENCILOP_DECRSAT;
					break;
				case STENCIL_INVERT:
					Op = D3DSTENCILOP_INVERT;
					break;
				case STENCIL_INCR:
					Op = D3DSTENCILOP_INCR;
					break;
				case STENCIL_DECR:
					Op = D3DSTENCILOP_DECR;
					break;
				default:
					DEBUGRETURN(deFALSE, "SetRenderState: Unknown Stencil Operation");
				}
				if (State == RENDER_STENCILFAIL)
					Ret = m_D3DDevice->SetRenderState(D3DRS_STENCILFAIL, Op);
				if (State == RENDER_STENCILZFAIL)
					Ret = m_D3DDevice->SetRenderState(D3DRS_STENCILZFAIL, Op);
				if (State == RENDER_STENCILPASS)
					Ret = m_D3DDevice->SetRenderState(D3DRS_STENCILPASS, Op);
				if (State == RENDER_CCW_STENCILFAIL)
					Ret = m_D3DDevice->SetRenderState(D3DRS_CCW_STENCILFAIL, Op);
				if (State == RENDER_CCW_STENCILZFAIL)
					Ret = m_D3DDevice->SetRenderState(D3DRS_CCW_STENCILZFAIL, Op);
				if (State == RENDER_CCW_STENCILPASS)
					Ret = m_D3DDevice->SetRenderState(D3DRS_CCW_STENCILPASS, Op);

			}
			break;
			
		default:
			Ret = D3DERR_INVALIDCALL;
	}

	//return based wether or not it failed
	return (Ret == D3D_OK) ? deTRUE : deFALSE;
}

unsigned long deDirect3D9::GetRenderState(RenderState State)
{
	//return the current status of a renderstate
	return m_RenderStates[State];
}

deBoolean deDirect3D9::EnableLight(long LightID, deBoolean Enable)
{
	DEBUG_EXP_RETURNMSG(LightID >= 0 && LightID < deD3D9_MAX_LIGHTS, deFALSE, "EnableLight: Bad Light ID");

	if (m_LightsEnabled[LightID] == Enable)
		return deTRUE;

	if(m_D3DDevice->LightEnable(LightID, Enable) != D3D_OK)
	{
		return deFALSE;
	}
	
	m_LightsEnabled[LightID] = Enable;

	return deTRUE;
}

deBoolean deDirect3D9::DisableAllLights()
{
	int i;
	for (i = 0; i < deD3D9_MAX_LIGHTS; ++i)
	{
		if (m_LightsEnabled[i])
		{
			if (D3D_OK != m_D3DDevice->LightEnable(i, FALSE))
				return deFALSE;
		}
	}
	memset(&m_LightsEnabled, 0, deD3D9_MAX_LIGHTS*sizeof(deBoolean));
	return deTRUE;
}
