//////////////////////////////////////////////////////////////////////////////
//
// Asura_Direct3D_AmbientOcclusion.cpp
//
// Copyright (c) 2009 Rebellion. All rights reserved.
//
//////////////////////////////////////////////////////////////////////////////

#ifdef ASURA_USE_PRAGMA_ONCE
#pragma once
#endif   // ifdef ASURA_USE_PRAGMA_ONCE

#ifndef __ASURA_XBOX360_EDGEANTIALIASING_H__
#define __ASURA_XBOX360_EDGEANTIALIASING_H__

#ifdef ASURA_EDGE_ANTIALIASING

#define MAX_ANTIALIAS_ENTITIES	(4096) //maximum number that will be shown at any one time (on environments each strip is a seperate AA entity because they are individually culled)


#include "Rendering/Asura_Xbox360_VertexBuffer.h"
#include "Rendering/Asura_Xbox360_IndexBuffer.h"


class Asura_Animation;
class Asura_Hierarchy_Skin;

#ifndef ASURA_HIERARCHY_QUAD
#error ASURA_EDGE_ANTIALIASING not supported without ASURA_HIERARCHY_QUAD
#endif

//
// This class holds a list of edges that need anti-aliasing.
// Other renderers fill this list with the silhouette edges of the objects they're drawing.
// Then, when the Render function is called, the edges are re-drawn using a special directional blur that gives the appearance of anti-aliasing.
// For our deferred rendering, the anti-aliasing pass is performed after all the deferred lighting.
//
class Asura_Xbox360_EdgeAntiAliasing
{
public:
	static void Initialise( void );
	static void Shutdown( void );
	static void BeginFrame( void );
	static void EndFrame( void );
	static void Render( void );

	// Calling function is responsible for ensuring index buffer & vertex buffer do not change for at least 1 frame (i.e. until graphics card has finished reading them)
	//  i.e. skinned objects will need to double buffer the vertices they pass to this function, and any dynamically created index lists (e.g. silhouette edges)
	// The projection matrix can change straight after calling this function
	// The vertex format is assumed to begin with 3 floats, representing x,y,z vertex position.
	static void AddEdgeList( Asura_Xbox360_IndexBuffer* pxIndexBuffer, Asura_Xbox360_VertexBuffer* pxVertexBuffer, Direct3D_VertexDeclaration* pxVertexDecl, const D3DXMATRIX& xWorldViewProjMatrix, int iStartOffset = 0, int iNumVerts = -1 )
	{
		if ( iNumVerts == 0 ) return;

		D3DINDEXBUFFER_DESC xIBDesc;
		pxIndexBuffer->GetDesc(&xIBDesc);
#if defined(ASURA_ASSERT)
		Asura_Assert( xIBDesc.Format == D3DFMT_INDEX16, "Asura_Xbox360_EdgeAntiAliasing::AddEdgeList only supports 16-bit indices" );
#endif
		Asura_Assert( s_uNumAntiAliasEntities < MAX_ANTIALIAS_ENTITIES, "Trying to anti-alias too many things, discarding the excess." );
		if ( s_uNumAntiAliasEntities >= MAX_ANTIALIAS_ENTITIES ) return; // alternatively we could check which is closest (using projection matrix)

		// append it to my array
		AntiAliasEntity* pxEnt = &s_axAntiAliasEntities[s_uNumAntiAliasEntities];
		pxEnt->m_xWorldViewProj		= xWorldViewProjMatrix;
		pxEnt->m_pxIndexBuffer		= pxIndexBuffer;
		pxEnt->m_pxVertexBuffer		= pxVertexBuffer;
		pxEnt->m_iStartOffset		= iStartOffset;
		pxEnt->m_pxVertexDecl		= pxVertexDecl;

		if ( iNumVerts == -1 )
		{
			pxEnt->m_iNumVerts = xIBDesc.Size/2 - iStartOffset; // all of them
		}
		else
		{
			pxEnt->m_iNumVerts = iNumVerts;
		}

		// (this is from PS3 version, but probably applies to 360/PC too)
		if ( /*pxEnt->m_iNumVerts > 0xfffff ||*/ pxEnt->m_iNumVerts <= 0 )
		{
			// no single object should have such a huge number of anti-alias indices (there's apparently a hardware limit of 0xfffff, according to GCM_IMPLEMENTATION_SUB.H)
			// in extreme cases this can cause a command buffer overflow crash because there's 1 command for every 0x7FF00 vertices
			// this has only fired when some kind of data corruption occurs, and it prevents a very hard-to-debug crash
			Asura_Assertf( 0, ("Asura_PS3_EdgeAntiAliasing::AddEdgeList - this object has %d anti-alias verts, this is probably a bad thing\n", pxEnt->m_iNumVerts) );
			return; // don't actually add this to the list.
		}

		s_uNumAntiAliasEntities++;
	}

	static bool Enabled() { return s_iDebugMode != AA_DISABLED; }

	static void SetCollectEdges( bool bCollect )	{ s_bCollectingSkinEntities  = bCollect; }

	static void AddSkinEntity( const Asura_Vector_3& xPos, const Asura_Matrix_3x3& xOrient, const Asura_Animation* pxAnimation, const Asura_Hierarchy_Skin* pxHierarchySkin, int iCameraIndex )
								//const Asura_AMatrix4x4& xWorldMat )
	{
		/*
		if( !s_bCollectingSkinEntities )
			return;

		Asura_Assert( s_uNumAntiAliasSkinEntities < MAX_ANTIALIAS_ENTITIES, "Trying to anti-alias too many things, discarding the excess." );
		if ( s_uNumAntiAliasSkinEntities >= MAX_ANTIALIAS_ENTITIES ) return;

		s_axAntiAliasSkinEntities[s_uNumAntiAliasSkinEntities].m_pxAnimation		= pxAnimation;
		s_axAntiAliasSkinEntities[s_uNumAntiAliasSkinEntities].m_pxHierarchySkin	= pxHierarchySkin;
		s_axAntiAliasSkinEntities[s_uNumAntiAliasSkinEntities].m_iCameraIndex		= iCameraIndex;
		s_axAntiAliasSkinEntities[s_uNumAntiAliasSkinEntities].m_xPos				= xPos;
		s_axAntiAliasSkinEntities[s_uNumAntiAliasSkinEntities].m_xOrient			= xOrient;
		s_uNumAntiAliasSkinEntities++;
		*/
	}

private:
	static void		RenderEnvironment();
	static void		RenderSkins();


protected:
	enum AA_DEBUG_MODE
	{
		AA_DISABLED,
		AA_ENABLED,
		AA_DEBUG,
		NUM_AA_DEBUG_MODES,
	};

	static int s_iDebugMode;

#ifdef ASURA_SUPPORT_COMMAND_CONSOLE
	static bool SwitchAAModes ( bool bInc, bool bInterrogate, bool bHelp );
#endif

	struct AntiAliasEntity 
	{
		D3DXMATRIX					m_xWorldViewProj; //BW todo - remove this from this struct, it's pointless having it for every env strip. => maybe 2 lists of entities, ones with matrices and ones in world space?
		Asura_Xbox360_IndexBuffer*	m_pxIndexBuffer;
		Asura_Xbox360_VertexBuffer*	m_pxVertexBuffer;
		Direct3D_VertexDeclaration* m_pxVertexDecl;
		int 						m_iStartOffset;
		int 						m_iNumVerts;
	};

	static AntiAliasEntity		s_axAntiAliasEntities[MAX_ANTIALIAS_ENTITIES];
	static u_int				s_uNumAntiAliasEntities;


	struct AntiAliasSkinEntity
	{
		const Asura_Animation*		m_pxAnimation;
		const Asura_Hierarchy_Skin* m_pxHierarchySkin;
		int							m_iCameraIndex;
		Asura_Vector_3				m_xPos;
		Asura_Matrix_3x3			m_xOrient;
	};

	static AntiAliasSkinEntity	s_axAntiAliasSkinEntities[MAX_ANTIALIAS_ENTITIES];
	static u_int				s_uNumAntiAliasSkinEntities;
	static bool					s_bCollectingSkinEntities;


};


#endif // ASURA_EDGE_ANTIALIASING

#endif // __ASURA_XBOX360_EDGEANTIALIASING_H__