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

/******************************************************************************/
/* Included Files                                                             */
/******************************************************************************/

#include "Core/Asura.h"

#if defined ASURA_USE_FSFX

// Asura Platform-independent
#include "FSFX/Asura_FSFX_System.h"
#include "Rendering/Asura_Render.h"
#include "Rendering/Asura_ViewPort.h"
#include "Rendering/Asura_Overlay_Quad.h"


// Asura Xbox360
// 
// AVP Platform-independent
#include "FSFX/AVP_FSFX.h"

// Asura Xbox360
#include "Rendering/Asura_Xbox360_Graphics.h"

// Shader Include Files
#include "AVP_FSFX_Edges.h"

// AVP Platform-independent
#include "FSFX/AVP_FSFX.h"

#include <xgraphics.h>


static Asura_Xbox360_Effect*					g_pxEdgesEffect					= NULL;
static Direct3D_VertexDeclaration*				g_pxEdgesVertexDecl				= NULL;
static Asura_Xbox360_Effect_System::Handle		g_pxEdgesInitialImageHandle		= 0;
static Asura_Xbox360_Effect_System::Handle		g_pxEdgesInvScreenDimsHandle	= 0;
static Asura_Xbox360_Effect_System::Handle		g_pxEdgesTintHandle	= 0;
static Asura_Xbox360_Effect_System::Handle		g_pxEdgesParamsHandle	= 0;

static Direct3D_Texture *g_pxRenderTarget;
static Direct3D_Surface *g_pxRenderTargetSurface;

static void OnEffectUnloaded(Asura_Xbox360_Effect* pxEffect)
{
	g_pxEdgesEffect = NULL;
}

static void OnEffectLoaded(Asura_Xbox360_Effect* pxEffect)
{
#define LOADPARAMETER(codeName, shaderName) { codeName = g_pxEdgesEffect->GetParameterHandle(shaderName); Asura_Assert(codeName, "Could not get shader parameter '" shaderName "'."); }

	g_pxEdgesEffect = Asura_Xbox360_Effect_System::CreateEffectFromMemory(g_auAVP_FSFX_Edges);

	if (!g_pxEdgesEffect)
	{
		return;
	}

	// Get the params
	LOADPARAMETER(g_pxEdgesInitialImageHandle, "g_xInitialImage" );
	LOADPARAMETER(g_pxEdgesInvScreenDimsHandle, "g_fInvScreenDims" );
	LOADPARAMETER(g_pxEdgesTintHandle, "g_fTint" );
	LOADPARAMETER(g_pxEdgesParamsHandle, "g_fEdgeDetectionParams" );

#undef LOADPARAMETER
}

static bool PostReset(void*)
{
	const D3DFORMAT eCOLOUR_TILE_FORMAT   = D3DFMT_A2B10G10R10F_EDRAM;
	const D3DFORMAT eDEPTH_BUFFER_FORMAT       = D3DFMT_D24S8;

	// Full-screen final render target
	const u_int uFINAL_RT_SIZE = XGSurfaceSize( Asura_Xbox360_Graphics::GetScreenWidth(), Asura_Xbox360_Graphics::GetScreenHeight(),
		eCOLOUR_TILE_FORMAT, D3DMULTISAMPLE_NONE );
	const u_int uFINAL_DEPTH_BUFFER_SIZE = XGSurfaceSize( Asura_Xbox360_Graphics::GetScreenWidth(), Asura_Xbox360_Graphics::GetScreenHeight(),
		eDEPTH_BUFFER_FORMAT, D3DMULTISAMPLE_NONE );

	D3DSURFACE_PARAMETERS xSurfaceParams;
	xSurfaceParams.Base = uFINAL_RT_SIZE + uFINAL_DEPTH_BUFFER_SIZE;
	xSurfaceParams.HierarchicalZBase = 0;
	xSurfaceParams.ColorExpBias = 0;
	xSurfaceParams.HiZFunc = D3DHIZFUNC_DEFAULT;

	Asura_Xbox360_Graphics::GetDevice()->CreateRenderTarget( Asura_Viewport::GetWidth() /2, Asura_Viewport::GetHeight() / 2, D3DFMT_A16B16G16R16F, D3DMULTISAMPLE_NONE, 0, FALSE, &g_pxRenderTargetSurface, &xSurfaceParams );
	Asura_Xbox360_Graphics::GetDevice()->CreateTexture(Asura_Viewport::GetWidth() /2, Asura_Viewport::GetHeight() / 2, 1, D3DUSAGE_RENDERTARGET, D3DFMT_A8R8G8B8, D3DPOOL_DEFAULT, &g_pxRenderTarget, NULL);
	return true;
}
static bool PreReset(void*)
{
	SAFE_RELEASE(g_pxRenderTarget);
	SAFE_RELEASE(g_pxRenderTargetSurface);
	return true;
}

void AVP_FSFX_Component_Edges::Platform_Initialise(  )
{
	const Direct3D_VertexElement axVertDecl[] =
	{
		{ 0, 0, D3DDECLTYPE_FLOAT2, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_POSITION, 0 },	// Position
		{ 0, 8, D3DDECLTYPE_FLOAT2, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_TEXCOORD, 0 },	// UVs
		D3DDECL_END()
	};

	Asura_Xbox360_Graphics::GetDevice()->CreateVertexDeclaration( axVertDecl, &g_pxEdgesVertexDecl );

	OnEffectLoaded(NULL);

	PostReset(NULL);
}
void AVP_FSFX_Component_Edges::Platform_Shutdown(  )
{
	PreReset(NULL);
	OnEffectUnloaded(NULL);

	if (g_pxEdgesVertexDecl)
	{
		g_pxEdgesVertexDecl->Release();
		g_pxEdgesVertexDecl = NULL;
	}
}



void AVP_FSFX_Component_Edges::Platform_Render( float fIntensity )
{
	Asura_Xbox360_Effect* const pxEffect = g_pxEdgesEffect;
	if( !pxEffect) return;

	SCOPEDPIXEVENT(( 0xFFE0D0C0, "FSFX: AVP - Edges" ));

	// Setup
	Asura_Xbox360_Graphics::Resolve( Asura_Xbox360_Graphics::GetPostToneMapTexture() );

	g_pxEdgesEffect->SetTexture( g_pxEdgesInitialImageHandle, Asura_Xbox360_Graphics::GetPostToneMapTexture() );
	pxEffect->SetVector( g_pxEdgesInvScreenDimsHandle, &(D3DXVECTOR4(1.0f / Asura_Viewport::GetWidth(), 1.0f / Asura_Viewport::GetHeight(), 0.0f, 0.0f).x));
	pxEffect->SetVector( g_pxEdgesParamsHandle, &(D3DXVECTOR4(m_fLowThreshold * 0.1f, m_fHighThreshold * 0.1f, m_fEDWidth * 0.1f, 0.0f).x));

	Direct3D_Surface* pxOldRenderTargetBuffer[5] = 
	{
		NULL,
		NULL,
		NULL,
		NULL,
		NULL,
	};

	for (int j = 0; j < 4; ++j)
	{
		if (FAILED(Asura_Xbox360_Graphics::GetDevice()->GetRenderTarget(j, &pxOldRenderTargetBuffer[j])))
		{
			pxOldRenderTargetBuffer[j] = NULL;
		}

	}
	if (FAILED(Asura_Xbox360_Graphics::GetDevice()->GetDepthStencilSurface(&pxOldRenderTargetBuffer[4])))
	{
		pxOldRenderTargetBuffer[4] = NULL;
	}

	Asura_Xbox360_Graphics::GetDevice()->SetRenderTarget(0, g_pxRenderTargetSurface);
	Asura_Xbox360_Graphics::GetDevice()->SetRenderTarget(1, NULL);
	Asura_Xbox360_Graphics::GetDevice()->SetRenderTarget(2, NULL);
	Asura_Xbox360_Graphics::GetDevice()->SetRenderTarget(3, NULL);
	Asura_Xbox360_Graphics::GetDevice()->SetDepthStencilSurface(NULL);
	Asura_Xbox360_Graphics::GetDevice()->Clear(0, NULL, D3DCLEAR_TARGET, 0, 0.0f, 0);




	Asura_Render::CurrentStates.RequestAlphaTest( false );
	Asura_Render::CurrentStates.RequestCullMode( ASURA_CULLMODE_NONE );
	Asura_Render::CurrentStates.RequestTranslucencyMode( ASURA_TRANSLUCENCY_OFF );
	Asura_Render::CurrentStates.RequestZBufferEnabled( false );

	Direct3D_VertexDeclaration* const pxVD = g_pxEdgesVertexDecl;
	Asura_Xbox360_Graphics::GetDevice()->SetVertexDeclaration( pxVD );

	const float fWidth = static_cast< float >( Asura_Viewport::GetWidth() );
	const float fHeight = static_cast< float >( Asura_Viewport::GetHeight() );

	const struct Vertex { float fX, fY, fU, fV; } axVerts[] =
	{
		{ -1.0f,  1.0f, 0.5f / fWidth,            0.5f / fHeight },
		{  1.0f,  1.0f, (fWidth + 0.5f) / fWidth, 0.5f / fHeight },
		{  1.0f, -1.0f, (fWidth + 0.5f) / fWidth, (fHeight + 0.5f) / fHeight },
		{ -1.0f, -1.0f, 0.5f / fWidth,            (fHeight + 0.5f) / fHeight },
	};

	pxEffect->Begin(m_uMethod, 0);
	pxEffect->Commit();

	Asura_Xbox360_Graphics::GetDevice()->DrawPrimitiveUP( D3DPT_TRIANGLEFAN, 2, axVerts, sizeof( Vertex ) );
	Asura_Xbox360_Graphics::Resolve( g_pxRenderTarget );



	Asura_Xbox360_Graphics::GetDevice()->SetRenderTarget(0, pxOldRenderTargetBuffer[0]);
	Asura_Xbox360_Graphics::GetDevice()->SetRenderTarget(1, pxOldRenderTargetBuffer[1]);
	Asura_Xbox360_Graphics::GetDevice()->SetRenderTarget(2, pxOldRenderTargetBuffer[2]);
	Asura_Xbox360_Graphics::GetDevice()->SetRenderTarget(3, pxOldRenderTargetBuffer[3]);
	Asura_Xbox360_Graphics::GetDevice()->SetDepthStencilSurface(pxOldRenderTargetBuffer[4]);
	SAFE_RELEASE(pxOldRenderTargetBuffer[0]);
	SAFE_RELEASE(pxOldRenderTargetBuffer[1]);
	SAFE_RELEASE(pxOldRenderTargetBuffer[2]);
	SAFE_RELEASE(pxOldRenderTargetBuffer[3]);
	SAFE_RELEASE(pxOldRenderTargetBuffer[4]);

	g_pxEdgesEffect->SetTexture( g_pxEdgesInitialImageHandle, g_pxRenderTarget );

	fIntensity *= m_fAmount * 0.01f;
	pxEffect->SetVector( g_pxEdgesTintHandle, &(D3DXVECTOR4(m_xTintColour.m_fR * fIntensity, m_xTintColour.m_fG * fIntensity, m_xTintColour.m_fB * fIntensity,  m_fBlurWidth * 0.1f).x));
	Asura_Render::CurrentStates.RequestTranslucencyMode( ASURA_TRANSLUCENCY_GLOWING);

	pxEffect->BeginPass(1);
	pxEffect->Commit();

	Asura_Xbox360_Graphics::GetDevice()->DrawPrimitiveUP( D3DPT_TRIANGLEFAN, 2, axVerts, sizeof( Vertex ) );

	pxEffect->End();

}
#endif //defined ASURA_USE_FSFX
