//
// Asura_PlatformFSFX.cpp (PS2)
//
// Created by JGP 07/02/06
// 
// Copyright (C) 2006 Rebellion (Derby), All Rights Reserved.
//

#include "Core/Asura.h"

#ifdef ASURA_USE_FSFX

#include "Camera/Asura_Camera.h"
#include "Core/Asura_PS2_DMA.h"
#include "FSFX/Asura_FSFX_Component.h"
#include "FSFX/Asura_FSFX_System.h"
#include "FSFX/Asura_FSFX_Template.h"
#include "Rendering/Asura_PlatformRenderStates.h"
#include "Rendering/Asura_PlatformTextureManagement.h"
#include "Rendering/Asura_PS2_DeferredRendering.h"
#include "Rendering/Asura_PS2_GS.h"
#include "Rendering/Asura_PS2_GS_Util.h"
#include "Rendering/Asura_Render.h"
#include "Rendering/Asura_Viewport.h"

///////////////////////////////////////////////////////////////////////////////////////
// Asura_FSFX_System platform specific
///////////////////////////////////////////////////////////////////////////////////////


void Asura_FSFX_System::Platform_Initialise()
{
}

void Asura_FSFX_System::Platform_Reset()
{
}

void Asura_FSFX_System::Platform_Shutdown()
{
}

void Asura_FSFX_System::Platform_BeginRender(void)
{
}

void Asura_FSFX_System::Platform_EndRender(void)
{
}


///////////////////////////////////////////////////////////////////////////////////////
// Asura_FSFX_Template platform specific
///////////////////////////////////////////////////////////////////////////////////////


const int Asura_FSFX_Template::Platform_GetPlatformFlag()
{
	return Asura_FSFX_Component::FSFX_PLATFORM_PS2;
}


///////////////////////////////////////////////////////////////////////////////////////
// Asura_FSFX_Component_Test platform specific
///////////////////////////////////////////////////////////////////////////////////////


/*
As it happens this particular
render function is going to be the same between
platforms...
*/
void Asura_FSFX_Component_Test::Platform_Render( const float fIntensity )
{
	Asura_Render::Overlay.Colour           = 0xff000000 | ( static_cast<unsigned char>( fIntensity * m_fRed ) << 16 ) | ( static_cast<unsigned char>( fIntensity * m_fGreen ) << 8 ) | ( static_cast<unsigned char>( fIntensity * m_fBlue ) );
	Asura_Render::Overlay.TranslucencyMode = ( m_bSubtractive ) ? ASURA_TRANSLUCENCY_INVCOLOUR : ASURA_TRANSLUCENCY_GLOWING;
	Asura_Render::Overlay.TextureIndex     = ASURA_TEXTURE_NONE;

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

	Asura_Render::Overlay.SetRectXY( 0.0f, 0.0f, fWidth, fHeight );
	Asura_Render::Overlay.Render();   
}


///////////////////////////////////////////////////////////////////////////////////////
// Asura_FSFX_Component_BrightnessAndContrast platform specific
///////////////////////////////////////////////////////////////////////////////////////


/*
	Platform renderer for the effect
*/
void Asura_FSFX_Component_BrightnessAndContrast::Platform_Render( const float fIntensity )
{
	const float fBrightness = this->m_fBrightness * fIntensity * 0.01f;
	const float fContrast = this->m_fContrast * fIntensity * 0.01f;
	// Brightness = 0->256 (0->1), Contrast = 0-128 (0->1)
	int iBrightness = Asura_Maths::FloatToInt( fBrightness * 256.0f );
	int iContrast = Asura_Maths::FloatToInt( fContrast * 128.0f );

	iBrightness = Asura_Maths::Min( iBrightness, 255 );
	iBrightness = Asura_Maths::Max( iBrightness, 0 );
	iContrast = Asura_Maths::Min( iContrast, 128 );
	iContrast = Asura_Maths::Max( iContrast, 0 );
	Asura_PS2_GS_Util::BrightnessAndContrast( iBrightness, iContrast );
}


///////////////////////////////////////////////////////////////////////////////////////
// Asura_FSFX_Component_DesaturateAndTint platform specific
///////////////////////////////////////////////////////////////////////////////////////


/*
	Platform renderer for the effect
*/
void Asura_FSFX_Component_DesaturateAndTint::Platform_Render( const float fIntensity )
{
	const float fDesatValue = this->m_fStrength * fIntensity * 0.01f;
	const float fTintR = this->m_fIntensityR;
	const float fTintG = this->m_fIntensityG;
	const float fTintB = this->m_fIntensityB;

	// Remap 0.0->1.0 into 0->128
	// The PS2 effect does tint_colour^2 so take the sqaure root of the component values
	int iTintR = Asura_Maths::FloatToInt( Asura_Maths::Sqrt( fTintR * 0.01f ) * 128.0f );
	int iTintG = Asura_Maths::FloatToInt( Asura_Maths::Sqrt( fTintG * 0.01f ) * 128.0f );
	int iTintB = Asura_Maths::FloatToInt( Asura_Maths::Sqrt( fTintB * 0.01f ) * 128.0f );
	// Remap 0.0->1.0 into 0->128
	int iDesatScale = Asura_Maths::FloatToInt( fDesatValue * 128.0f );

	iTintR = Asura_Maths::Min( iTintR, 255 );
	iTintR = Asura_Maths::Max( iTintR, 0 );
	iTintG = Asura_Maths::Min( iTintG, 255 );
	iTintG = Asura_Maths::Max( iTintG, 0 );
	iTintB = Asura_Maths::Min( iTintB, 255 );
	iTintB = Asura_Maths::Max( iTintB, 0 );
	iDesatScale = Asura_Maths::Min( iDesatScale, 255 );
	iDesatScale = Asura_Maths::Max( iDesatScale, 0 );
	Asura_PS2_GS_Util::DesaturateAndTint( iTintR, iTintG, iTintB, iDesatScale );
}


///////////////////////////////////////////////////////////////////////////////////////
// Asura_FSFX_Component_Noise platform specific
///////////////////////////////////////////////////////////////////////////////////////


/*
	Platform renderer for the effect
*/
void Asura_FSFX_Component_Noise::Platform_Render( const float fIntensity )
{
	if( ASURA_TEXTURE_INVALID == this->m_iTextureID )
	{
		return;
	}
	const u_int uWidth  = Asura_Viewport::GetWidth();
	const u_int uHeight = Asura_Viewport::GetHeight();

	const float fTextureRandOffsetU = Asura_Random::GetFloat( 0.0f, 1.0f );
	const float fTextureRandOffsetV = Asura_Random::GetFloat( 0.0f, 1.0f );

	const u_int uTextureTileU = this->m_uTextureTileX;
	const u_int uTextureTileV = this->m_uTextureTileY;
	const float fURatio = 1.0f / static_cast< float>( uTextureTileU );
	const float fVRatio = 1.0f / static_cast< float>( uTextureTileV );
	const float fUScale = ( Asura_Random::GetFloat( 0.0f, 1.0f ) <= 0.5f ) ? 1.0f : -1.0f;
	const float fVScale = ( Asura_Random::GetFloat( 0.0f, 1.0f ) <= 0.5f ) ? 1.0f : -1.0f;

	const float fUStart = fTextureRandOffsetU;
	const float fVStart = fTextureRandOffsetV;
	const float fUSize = fUScale / fURatio;
	const float fVSize = fVScale / fVRatio;
	const float fUEnd = fUStart + fUSize;
	const float fVEnd = fVStart + fVSize;

	const float fEffectStrength = fIntensity * this->m_fAmount / 100.0f;
	// 64.0f to make noise effect be half as strong so it doesn't brighten the image
	const u_int uEffectStrength = Asura_Maths::FloatToInt_Near( 64.0f * fEffectStrength );

    // Set a proper XYOFFSET value
    Asura_Render::AllowFullScreenRendering();

	// Set texture buffer layout.
	Asura_PS2_TextureManagement::SetBufferLayout( ASURA_PS2_TEXBFRLAYOUT_2_LARGE );     
	const int iTexID = this->m_iTextureID;
    bool bUploadedTexture = Asura_PS2_TextureManagement::UploadTexture( iTexID );
	Asura_PS2_TextureManagement::SetupTextureEnvironment( bUploadedTexture,
														  ASURA_PS2_TEXREGFLAG_ALL,
														  iTexID,
														  Asura_PS2_TextureManagement::BFR_AUTO,
														  SCE_GS_LINEAR,
														  SCE_GS_LINEAR_MIPMAP_NEAREST,
														  true 
														);

	const u_int uNumRegs  = 9;
	const u_int uQWC      = 1 + uNumRegs;
	const u_int uTotalQWs = 1 + uQWC;

	Asura_PS2_QW* const pxBase = Asura_PS2_DeferredRendering::GetVifBuffer( uTotalQWs );
	Asura_PS2_QW* pxOut = pxBase;

	// DMA tag.
	(reinterpret_cast<Asura_PS2_DmaTag*>(pxOut++))->Set( uQWC,
		Asura_PS2_DmaTag::PCE_NOCHANGE,
		Asura_PS2_DmaTag::ID_CNT,
		false,
		0,
		false,
		SCE_VIF1_SET_NOP(false),
		SCE_VIF1_SET_DIRECT(uQWC, false) );

	// GIF tag.
	{
		const u_int uPRIM = SCE_GS_SET_PRIM( SCE_GS_PRIM_SPRITE,    // PRIM
			0,                     // IIP
			true,                  // TME
			false,                 // FGE
			true,				   // ABE
			false,                 // AA1
			0,                     // FST
			0,                     // CTXT
			0 );                   // FIX

		(reinterpret_cast<Asura_PS2_GifTag*>(pxOut++))->Set( 1,                 // NLOOP
			true,              // EOP
			true,              // PRE
			uPRIM,             // PRIM
			SCE_GIF_PACKED,    // FLG
			uNumRegs,          // NREG
			SCE_GIF_PACKED_AD, 
			SCE_GIF_PACKED_AD, 
			SCE_GIF_PACKED_AD, 
			SCE_GIF_PACKED_AD, 
			SCE_GIF_PACKED_AD, 
			SCE_GIF_PACKED_AD, 
			SCE_GIF_PACKED_AD, 
			SCE_GIF_PACKED_AD, 
			SCE_GIF_PACKED_AD );
	}

	// TEST
	pxOut->u64s[0] = SCE_GS_SET_TEST( 1, SCE_GS_ALPHA_NEVER, 0, SCE_GS_AFAIL_RGB_ONLY, 0, 0, 1, SCE_GS_DEPTH_ALWAYS );
	pxOut->u64s[1] = SCE_GS_TEST_1;
	++pxOut;

	// TEXA
	pxOut->u64s[0] = SCE_GS_SET_TEXA( 0, 0, 128 );
	pxOut->u64s[1] = SCE_GS_TEXA;
	++pxOut;

	// CLAMP.- no texture clamping
	pxOut->u64s[0] = SCE_GS_SET_CLAMP( SCE_GS_REPEAT, SCE_GS_REPEAT, 0, 0, 0, 0 );
	pxOut->u64s[1] = SCE_GS_CLAMP_1;
	++pxOut;

	// The colour
	// Set Q = 1.0f -> 0x3F800000
	pxOut->u64s[0] = SCE_GS_SET_RGBAQ( uEffectStrength, uEffectStrength, uEffectStrength, uEffectStrength, 0x3F800000 );
	pxOut->u64s[1] = SCE_GS_RGBAQ;
	++pxOut;

	// COLOUR_DESTINATION + SOURCE_ALPHA * COLOUR_DESTINATION
	pxOut->u64s[0] = SCE_GS_SET_ALPHA( SCE_GS_ALPHA_CD, SCE_GS_ALPHA_ZERO, SCE_GS_ALPHA_AS, SCE_GS_ALPHA_CD, 0 );
	pxOut->u64s[1] = SCE_GS_ALPHA_1;
	++pxOut;

    int iW = uWidth;
    int iH = uHeight;
    int iX = ( Asura_Viewport::GetWidth()  >> 1 ) - ( iW >> 1 );
    int iY = ( Asura_Viewport::GetHeight() >> 1 ) - ( iH >> 1 );
    iX += ASURA_PS2_SCREEN_CENTRE_X - (Asura_PS2_GS::GetScreenWidth() >> 1);
    iY += ASURA_PS2_SCREEN_CENTRE_Y - (Asura_PS2_GS::GetScreenHeight()>> 1);

	// TL.
	pxOut->u64s[0] = SCE_GS_SET_ST( *reinterpret_cast<const u_int*>( &fUStart ), *reinterpret_cast<const u_int*>( &fVStart ) );
	pxOut->u64s[1] = SCE_GS_ST;
	++pxOut;

	pxOut->u64s[0] = SCE_GS_SET_XYZ( iX << 4, iY << 4, 0 );
	pxOut->u64s[1] = SCE_GS_XYZ2;
	++pxOut;

	// BR vertex.
	pxOut->u64s[0] = SCE_GS_SET_ST( *reinterpret_cast<const u_int*>( &fUEnd ), *reinterpret_cast<const u_int*>( &fVEnd ) );
	pxOut->u64s[1] = SCE_GS_ST;
	++pxOut;

	pxOut->u64s[0] = SCE_GS_SET_XYZ( ( iX + iW ) << 4, ( iY + iH ) << 4, 0 );
	pxOut->u64s[1] = SCE_GS_XYZ2;
	++pxOut;

	Asura_Assert( static_cast<u_int>(pxOut - pxBase) == uTotalQWs, "" );
}


///////////////////////////////////////////////////////////////////////////////////////
// Asura_FSFX_Component_GaussianBlur platform specific
///////////////////////////////////////////////////////////////////////////////////////


/*
	Platform renderer for the effect
*/
void Asura_FSFX_Component_GaussianBlur::Platform_Render( const float fIntensity )
{
	const float fSize = this->m_fSize;
	const float fStrength = this->m_fStrength * fIntensity * 0.01f;
	if ( ( fStrength >= 0.01f ) && ( fSize >= 1.0f ) )
	{
		Asura_PS2_GS_Util::RenderGaussianBlur( fSize, fStrength );
	}
}


///////////////////////////////////////////////////////////////////////////////////////
// Asura_FSFX_Component_RadialBlur platform specific
///////////////////////////////////////////////////////////////////////////////////////


/*
	Platform renderer for the effect
*/
void Asura_FSFX_Component_RadialBlur::Platform_Render( const float fIntensity )
{
	// Size is in pixels
	const float fSize = this->m_fSize;

	// Strength = 0-1
	const float fStrength = this->m_fStrength * fIntensity * 0.01f;

	// Angle = is in radians
	const float fAngle = this->m_fAngle;

	// CentreX = 0->1
	// CentreY = 0->1
	const float fCentreX = ( this->m_fCentreX + 100.0f ) * 0.005f;
	const float fCentreY = ( this->m_fCentreY + 100.0f ) * 0.005f;

	if ( ( fStrength >= 0.01f ) && ( ( fSize > 0.0f ) || ( fAngle != 0.0f ) ) )
	{
		Asura_PS2_GS_Util::RenderRadialBlur( fSize, fStrength, fAngle, fCentreX, fCentreY );
	}
}


///////////////////////////////////////////////////////////////////////////////////////
// Asura_FSFX_Component_DepthOfField platform specific
///////////////////////////////////////////////////////////////////////////////////////


/*
	Platform renderer for the effect
*/
void Asura_FSFX_Component_DepthOfField::Platform_Render( const float fIntensity )
{
	// Need support for intensity and size
	const float fSize = this->m_fSize;
	const float fStrength = this->m_fStrength * fIntensity * 0.01f;
	if ( ( fStrength >= 0.01f ) && ( fSize >= 1.0f ) )
	{
		Asura_PS2_GS_Util::RenderDepthOfField( this->m_fNearBlurZ, this->m_fFarBlurZ, fSize, fStrength );
	}
}

///////////////////////////////////////////////////////////////////////////////////////
// Asura_FSFX_Component_Bloom platform specific
///////////////////////////////////////////////////////////////////////////////////////


/*
	Platform renderer for the effect
*/
DEBUGVAR u_int dbg_uTintR = 0x80;
DEBUGVAR u_int dbg_uTintG = 0x80;
DEBUGVAR u_int dbg_uTintB = 0x80;
DEBUGVAR u_int dbg_uDesatScale = 0x80;

DEBUGVAR u_int dbg_uBloomAlphaFinal = 0x20;	// applied for the final pass
DEBUGVAR u_int dbg_uNumBloomDownsamples = 4;	// 512,256,128,64,32 or 640,320,160,80,40 x 448,224,112,56,28
void Asura_FSFX_Component_Bloom::Platform_Render( const float fIntensity )
{
	const float fLevel = this->m_fThreshold * 0.01f;

	u_int auBloomLevel[ASURA_MAX_NO_OF_CAMERAS];
	const u_int uNumCameras = Asura_Camera::GetNumberOfCameras();
	for ( u_int uI = 0; uI < uNumCameras; uI++ )
	{
		// TODO: support a bloom strength per camera?
		auBloomLevel[uI] = Asura_Maths::Min( 255, Asura_Maths::FloatToInt_Near( 128.0f * fLevel * 1.0f ) );
	}
	// TODO: support colour tinting for glow on PS2
	Asura_PS2_GS_Util::RenderGlow( dbg_uBloomAlphaFinal, dbg_uNumBloomDownsamples, auBloomLevel, uNumCameras );
}


///////////////////////////////////////////////////////////////////////////////////////
// Asura_FSFX_Component_Gamma platform specific
///////////////////////////////////////////////////////////////////////////////////////


/*
	Platform renderer for the effect
*/
void Asura_FSFX_Component_Gamma::Platform_Render( const float fIntensity )
{
//	this->m_fAmoun/

}


///////////////////////////////////////////////////////////////////////////////////////
// Asura_FSFX_Component_RGBGamma platform specific
///////////////////////////////////////////////////////////////////////////////////////


/*
	Platform renderer for the effect
*/
void Asura_FSFX_Component_RGBGamma::Platform_Render( const float fIntensity )
{
//	this->m_fAmountR
//	this->m_fAmountG
//	this->m_fAmountB
}


#endif // ASURA_USE_FSFX
