//
// Asura_Direct3D_LineRendering.h
//
// Created by RJF - 06 September 2006
//
// Copyright (C) 2006 Rebellion, All Rights Reserved.
//

#ifdef ASURA_USE_PRAGMA_ONCE
#pragma once
#endif

#ifndef __ASURA_DIRECT3D_LINERENDERING_H__
#define __ASURA_DIRECT3D_LINERENDERING_H__

#include "Rendering/Asura_Direct3D.h"

class Asura_Direct3D_LineRenderer
{
public:

	class Line2D
	{
	public:
		Line2D( const Asura_Vector_2& xP1, const Asura_Vector_2& xP2, u_int uColour );
		Line2D( const Asura_Vector_2& xP1, const Asura_Vector_2& xP2, u_int uColour1, u_int uColour2 );
		struct Vertex
		{
			enum { FVF = D3DFVF_XYZRHW | D3DFVF_DIFFUSE };
			float fX, fY, fZ, fRhw;
			u_int uColour;
		};
		static void Render( const  Line2D::Vertex* const pxVertexData, const u_int uNumVertices);
		void ToVertices( Line2D::Vertex& xP1,  Line2D::Vertex& xP2, const Asura_Bounding_Rectangle& xSwapChainBounds) const;
	private:
		Asura_Vector_2 m_xP1, m_xP2;
		u_int m_uColour1, m_uColour2;
	};

	class Line3D
	{
	public:
		Line3D(const Asura_Vector_3& xP1, const Asura_Vector_3& xP2, u_int uColour );
		Line3D(const Asura_Vector_3& xP1, const Asura_Vector_3& xP2, u_int uColour1, u_int uColour2 );
		struct Vertex
		{
			enum { FVF = D3DFVF_XYZ | D3DFVF_DIFFUSE };
			float fX, fY, fZ;
			u_int uColour;
		};
		static void Render( const Line3D::Vertex* const pxVertexData, const u_int uNumVertices);
		void ToVertices( Line3D::Vertex& xP1, Line3D::Vertex& xP2, const Asura_Bounding_Rectangle& xSwapChainBounds) const;
	private:
		Asura_Vector_3 m_xP1, m_xP2;
		u_int m_uColour1, m_uColour2;
	};

	static void Initialise();
	static void Reset();
	static void Shutdown();

	static void PreD3DReset();
	static void PostD3DReset();

	template<class LineClass>
	static void AddLine(const LineClass& xLine)
	{
		Asura_Assert(BatchBuffer<LineClass>::s_pxBatchBuffers, "Batch buffers not created yet");
		if(!BatchBuffer<LineClass>::s_pxBatchBuffers) return;
		const u_int uNumSwapChains = BatchBuffer<LineClass>::s_pxBatchBuffers[0].m_uNumSwapChains;

		for(u_int uSwapChain = 0; uSwapChain < uNumSwapChains; ++uSwapChain)
		{
			BatchBuffer<LineClass>& xBuffer(BatchBuffer<LineClass>::s_pxBatchBuffers[uSwapChain]);
			
			Asura_Bounding_Rectangle xBounds = Asura_Direct3D::GetSwapChainDisplayLimits(xBuffer.m_iSwapChainIndex);

			xLine.ToVertices(xBuffer.m_pxVertices[xBuffer.m_iVerticesBatched], xBuffer.m_pxVertices[xBuffer.m_iVerticesBatched+1], xBounds);
			xBuffer.m_iVerticesBatched+=2;
			Asura_Assert( xBuffer.m_iVerticesBatched <= xBuffer.m_iBatchSize, "Batch line buffer overflowed" );

			// Flush if the buffer is full
			if(xBuffer.IsFull()) 
			{
				Flush<LineClass>(uSwapChain);
			}
		}
	}

	template<class LineClass>
	static void Flush(u_int uSwapChainIndex = -1)
	{
		Asura_Assert(BatchBuffer<LineClass>::s_pxBatchBuffers, "Batch buffers not created yet");
		if(!BatchBuffer<LineClass>::s_pxBatchBuffers) return;

		bool bMadePixEvent = false;

		const u_int uNumSwapChains = BatchBuffer<LineClass>::s_pxBatchBuffers[0].m_uNumSwapChains;
		for(u_int uIndex = 0; uIndex < uNumSwapChains; ++uIndex)
		{
			if(uIndex == uSwapChainIndex || uSwapChainIndex == -1)
			{
				BatchBuffer<LineClass>& xBuffer(BatchBuffer<LineClass>::s_pxBatchBuffers[uIndex]);

				// JAT 24/11/06 - Only render if there is something to render
				if ( xBuffer.m_iVerticesBatched > 0 )
				{
					if( !bMadePixEvent )
					{
						// Only insert the event if we're going to render anything, otherwise we end
						// up with loads of empty entries in the event view
						D3DPERFCALL( BeginEvent(  0xFF000000, L"Flush line renderer" ) );
						bMadePixEvent = true;
					}

					Asura_Direct3D::RequestSwapChain(xBuffer.m_iSwapChainIndex);
					LineClass::Render(xBuffer.m_pxVertices, xBuffer.m_iVerticesBatched);
					xBuffer.m_iVerticesBatched = 0;
				}
			}
		}

		if( bMadePixEvent )
		{
			D3DPERFCALL( EndEvent() ); // Flush line renderer
		}
	}

private:
	template<class LineClass>
	struct BatchBuffer
	{
		BatchBuffer( void ) : m_iSwapChainIndex(0), m_iVerticesBatched(0), m_pxVertices(NULL), m_iBatchSize( 0 ) { }
		~BatchBuffer() { delete[] m_pxVertices; }
		
		typedef typename LineClass::Vertex VertexType;
		
		int m_iSwapChainIndex;
		int m_iBatchSize;
		int m_iVerticesBatched;
		u_int m_uNumSwapChains;
		
		bool IsFull() const { return m_iBatchSize == m_iVerticesBatched; }
		
		VertexType* m_pxVertices;
		static BatchBuffer<LineClass>* s_pxBatchBuffers;

		static void Initialise( const u_int uBatchSize, const u_int uMaxNumSwapChains )
		{ 
			const u_int uNumSwapChains = Asura_Maths::Min( uMaxNumSwapChains, Asura_Direct3D::GetNumSwapChains() );
			s_pxBatchBuffers = new BatchBuffer<LineClass>[uNumSwapChains];
			for(u_int uSwapChain = 0; uSwapChain < uNumSwapChains; ++uSwapChain)
			{
				s_pxBatchBuffers[uSwapChain].m_iSwapChainIndex = uSwapChain;
				s_pxBatchBuffers[uSwapChain].m_iBatchSize = uBatchSize;
				s_pxBatchBuffers[uSwapChain].m_pxVertices = new LineClass::Vertex[s_pxBatchBuffers[uSwapChain].m_iBatchSize];
				s_pxBatchBuffers[uSwapChain].m_uNumSwapChains = uNumSwapChains;
			}
		}

		static void Shutdown() 
		{
			delete[] s_pxBatchBuffers; 
			s_pxBatchBuffers = NULL; 
		}

		// Has the number of swap chains changed?
		static bool ShouldReset(const u_int uMaxNumSwapChains)
		{
			const u_int uNewNumSwapChains = Asura_Maths::Min( uMaxNumSwapChains, Asura_Direct3D::GetNumSwapChains() );
			return s_pxBatchBuffers && (s_pxBatchBuffers[0].m_uNumSwapChains != uNewNumSwapChains);
		}

		// Clear any batched vertices.
		static void Clear()
		{
			if (s_pxBatchBuffers)
			{
				const u_int uNumSwapChains = s_pxBatchBuffers[0].m_uNumSwapChains;
				for(u_int uSwapChain = 0; uSwapChain < uNumSwapChains; ++uSwapChain)
				{
					s_pxBatchBuffers[uSwapChain].m_iVerticesBatched = 0;
				}
			}
		}
	};	
};

#endif//__ASURA_DIRECT3D_LINERENDERING_H__
