//
// Asura_Platform_ShadowDecal.cpp
//
// Created by KJFL - 15 March 2005
//
// Copyright (C) 2005 Rebellion, All Rights Reserved.
//

#include "Core/Asura.h"

// This file's header.
#include "SpecialFX/Asura_ShadowDecal_System.h"
#include "SpecialFX/Asura_ShadowDecal_System_Internal.h"

#include "Camera/Asura_Camera.h"
#include "Rendering/Asura_Direct3D.h"
#include "Rendering/Asura_Render.h"
#include "Rendering/Asura_Direct3D_Shaders.h"
#include "Core/Asura_TextureManagement.h"

#define D3DFVF_DECALVERTEX (D3DFVF_XYZ|D3DFVF_TEX1)
class D3D_ShadowDecalVertex
{
public:

	float X,Y,Z;
	float U,V;

	
	inline bool operator == ( const D3D_ShadowDecalVertex& v) const
	{
	    return (X == v.X && Y == v.Y && Z == v.Z
			 && U == v.U && V == v.V);
	}


};

class Asura_Direct3D_ShadowDecalBuffer
{

	public:

	Asura_Direct3D_ShadowDecalBuffer()
	{
		uMaxNumberOfIndices = 1000;
		VertexStripIndex = 0;
		NumberOfStripTris = 0;
	
		uCurrentIndex = 0;
		uMaxIndex = 0;
	}

	~Asura_Direct3D_ShadowDecalBuffer()
	{
	}

	void Reset( void )
	{
		uMaxIndex = uCurrentIndex = NumberOfStripTris = VertexStripIndex = 0;
	}

	bool IsEmpty( void ) const
	{
		return (uMaxIndex==0);
	}

	void AddStrip(const Asura_ShadowDecal_Vertex* pxStrip, const u_int uNumberOfVertices)
	{
		if (uCurrentIndex + uNumberOfVertices+2 > uMaxNumberOfIndices)
		{
			uCurrentIndex = 0;
		}

		vertices[uCurrentIndex].X = pxStrip[0].m_xPosition.x;
		vertices[uCurrentIndex].Y = pxStrip[0].m_xPosition.y;
		vertices[uCurrentIndex].Z = pxStrip[0].m_xPosition.z;
		vertices[uCurrentIndex].U = pxStrip[0].m_fU;
		vertices[uCurrentIndex].V = pxStrip[0].m_fV;
		uCurrentIndex++;

		for(u_int i=0; i<uNumberOfVertices; i++)
		{
			u_int v = i + uCurrentIndex;
			vertices[v].X = pxStrip[i].m_xPosition.x;
			vertices[v].Y = pxStrip[i].m_xPosition.y;
			vertices[v].Z = pxStrip[i].m_xPosition.z;
			vertices[v].U = pxStrip[i].m_fU;
			vertices[v].V = pxStrip[i].m_fV;
		}
		
		uCurrentIndex+= uNumberOfVertices;
		
		vertices[uCurrentIndex].X = pxStrip[uNumberOfVertices-1].m_xPosition.x;
		vertices[uCurrentIndex].Y = pxStrip[uNumberOfVertices-1].m_xPosition.y;
		vertices[uCurrentIndex].Z = pxStrip[uNumberOfVertices-1].m_xPosition.z;
		vertices[uCurrentIndex].U = pxStrip[uNumberOfVertices-1].m_fU;
		vertices[uCurrentIndex].V = pxStrip[uNumberOfVertices-1].m_fV;
		uCurrentIndex++;
			
		// KJL - 29/04/2003 10:32:10 - if wrapped we have to tidy up any decals we've half-overwritten
		if (uCurrentIndex<uMaxIndex)
		{
			u_int u = uCurrentIndex;

			while (!(vertices[u]==vertices[u+1]) && u<uMaxIndex-1)
			{
				vertices[u].X		= vertices[u-1].X;
				vertices[u].Y		= vertices[u-1].Y;
				vertices[u].Z		= vertices[u-1].Z;
				vertices[u].U		= vertices[u-1].U;			
				vertices[u].V		= vertices[u-1].V;
				u++;
			}
		}
		else
		{
			uMaxIndex = uCurrentIndex;
		}
		
		if (uCurrentIndex>NumberOfStripTris+2)
		{
			NumberOfStripTris = uCurrentIndex - 2;
		}
	}

	void Render(void)
	{
		for(int i=0; i<Asura_Camera::GetNumberOfCameras(); i++)
		{
			Asura_Camera::SetCameraIndex(i);

			if (Asura_Camera::IsCameraActive())
			{
				Asura_Render::ApplyCameraSettings();
				
				Asura_Direct3D::D3DDevice->DrawPrimitiveUP
				(
					D3DPT_TRIANGLESTRIP,
					NumberOfStripTris,
					(void*)vertices,
					sizeof(D3D_ShadowDecalVertex)
				);

			}
		}
	}

	D3D_ShadowDecalVertex vertices[1024];
	u_int uMaxNumberOfIndices;
	u_int VertexStripIndex;
	u_int NumberOfStripTris;

	u_int uCurrentIndex;
	u_int uMaxIndex;

};

static Asura_Direct3D_ShadowDecalBuffer g_xShadowDecalBuffer;
static int g_iShadowTexture = 1;

void Asura_ShadowDecal_System::Platform_Initialise(void)
{
	g_xShadowDecalBuffer.Reset();

	g_iShadowTexture = ASURA_TEXTURE_NONE;
	Asura_Texture_Management::LoadTextures();
}

void Asura_ShadowDecal_System::Platform_Reset(void)
{
	Platform_Initialise();
}

void Asura_ShadowDecal_System::Platform_Shutdown(void)
{
	g_xShadowDecalBuffer.Reset();
}

void Asura_ShadowDecal_System::Platform_AddStrip(const Asura_ShadowDecal_Vertex* pxStrip, u_int uNumVertices )
{
	g_xShadowDecalBuffer.AddStrip(pxStrip, uNumVertices);
}

void Asura_ShadowDecal_System::Platform_Render(void)
{
	if (g_xShadowDecalBuffer.IsEmpty()) return;

	if ( g_iShadowTexture == ASURA_TEXTURE_NONE)
	{
		g_iShadowTexture = Asura_Texture_Management::AddTexture("\\specialfx\\blobshadow.tga");
		Asura_Texture_Management::LoadTextures();
	}

	Asura_Direct3D_Shaders::RequestFixedFunctionVertexShader(D3DFVF_DECALVERTEX);
	Asura_Direct3D_Shaders::RequestPixelShader(ASURA_D3D_PS_NONE);

//	Asura_Render::CurrentStates.RequestTextureIndex(0,ASURA_TEXTURE_NONE);
	Asura_Render::CurrentStates.RequestTranslucencyMode(ASURA_TRANSLUCENCY_NORMAL);
	Asura_Render::CurrentStates.RequestTextureOpMode(ASURA_TEXTUREOP_DUALTEXTURE);
	Asura_Render::CurrentStates.RequestTextureOpMode(ASURA_TEXTUREOP_SINGLE_PASS);

	Asura_Render::CurrentStates.RequestZBufferWriteEnabled(false);
	Asura_Render::CurrentStates.RequestCullMode(ASURA_CULLMODE_NONE);
	
	Asura_Render::CurrentStates.RequestTextureIndex(0, g_iShadowTexture);

	// offset Z closer to camera to avoid z-fighting
	float fZBias = -0.0001f;
	Asura_Direct3D::D3DDevice->SetRenderState(D3DRS_DEPTHBIAS, *((DWORD *)&fZBias) );


	g_xShadowDecalBuffer.Render();

	fZBias = 0.0f;
	Asura_Direct3D::D3DDevice->SetRenderState(D3DRS_DEPTHBIAS, *((DWORD *)&fZBias) );
	Asura_Render::CurrentStates.RequestZBufferWriteEnabled(true);

	g_xShadowDecalBuffer.Reset();
}
