//
// Asura_PS3_VertexShader.cpp
//
// Copyright (C) 2005 Rebellion, All Rights Reserved.
//

// I N C L U D E S ///////////////////////////////////////////////////////////

#include "Core/Asura.h"

#include "Rendering/Shaders/Asura_PS3_GCM_ShadersLibrary.h"
// This file's header.
#include "Rendering/Shaders/Asura_PS3_VertexShader.h"

float						Asura_PS3_GCM_VertexShader::s_afRegisters[ 468 ][ 4 ];
Asura_PS3_GCM_VertexShader	*Asura_PS3_GCM_VertexShader::s_pxIgnoreRegisterCache = NULL;


Asura_PS3_GCM_VertexShader::Asura_PS3_GCM_VertexShader( unsigned char* uStartProgram, unsigned char* uEndProgram, Asura_PS3_GCM_VertexShaderUniformParameter* pxVertexShaderUniform, char cVertexShaderUniformElements, Asura_PS3_GCM_TextureUnitElement* pxTextureUnit /*= NULL*/, char cTextureUnitsElements /*= 0 */ ) : Asura_PS3_GCM_Shader( uStartProgram, uEndProgram )
{
	m_pxProgramAddr = cellCgbGetUCode( &m_xProgram );
	cellCgbGetVertexConfiguration( &m_xProgram, &m_xProgramConfig );

	m_pxVertexShaderUniform	= pxVertexShaderUniform;

	for( int i = 0; i < cVertexShaderUniformElements; ++i )
	{
#if 0 //Not supported by the compiler yet
		pxVertexShaderUniform[ i ].m_usRegister = new unsigned short[ pxVertexShaderUniform[ i ].m_usArraySize ];
		if( pxVertexShaderUniform[ i ].m_usArraySize > 1 )
		{
			for( int j = 0; j < pxVertexShaderUniform[ i ].m_usArraySize; ++j)
			{
				char szBuffer[ 256 ];
				Asura_PrintfToBfr( szBuffer, "%s[%d]", 255, true, pxVertexShaderUniform[ i ].m_szName, j );

				u_int index_tmp = cellCgbMapLookup( &m_xProgram, szBuffer );

				if( index_tmp == CELL_CGB_ERROR_FAILED )
				{
					Asura_Assert( false, "cellCgbMapLookup failed.  Probably vertex shader was changed!" );
				}
				else
				{
					cellCgbMapGetVertexUniformRegister( &m_xProgram, index_tmp, &pxVertexShaderUniform[ i ].m_usRegister[j], NULL );
				}
			}
		}
		else
#else
		pxVertexShaderUniform[ i ].m_usRegister = new unsigned short[ 1 ];
#endif
		{
			u_int index_tmp = cellCgbMapLookup( &m_xProgram, pxVertexShaderUniform[ i ].m_szName );
			if( index_tmp == CELL_CGB_ERROR_FAILED )
			{
				Asura_Assertf( false, ("cellCgbMapLookup failed (%s).  Probably vertex shader was changed!", pxVertexShaderUniform[ i ].m_szName) );
				pxVertexShaderUniform[ i ].m_usRegister[0] = 0xFFFF;
			}
			else
			{
				cellCgbMapGetVertexUniformRegister( &m_xProgram, index_tmp, &pxVertexShaderUniform[ i ].m_usRegister[0], NULL );
			}
		}
	}

	for( int i = 0; i < cTextureUnitsElements; ++i )
	{
#if 0 //Not supported by the compiler yet
		pxTextureUnit[ i ].m_cTextureUnit = new unsigned char[ pxTextureUnit[ i ].m_ucArraySize ];
		if( pxTextureUnit[ i ].m_ucArraySize > 1 )
		{
			for( j = 0; j < pxTextureUnit[ i ].m_ucArraySize; ++j )
			{
				char szBuffer[ 256 ];
				Asura_PrintfToBfr( szBuffer, "%s[%d]", 255, true, pxTextureUnit[ i ].m_szName, j );

				u_int attribute_tmp = cellCgbMapLookupTextureUnit( &m_xProgram, szBuffer );
				if( attribute_tmp == CELL_CGB_ERROR_FAILED )
				{
					Asura_Assert( false, "cellCgbMapLookupTextureUnit failed. Probably vertex shader was changed!" );
				}
				else
				{
					pxTextureUnit[ i ].m_cTextureUnit[j] = attribute_tmp;
				}
			}
		}
		else
#else
		pxTextureUnit[ i ].m_cTextureUnit = new char[ 1 ];
#endif
		{
			u_int attribute_tmp = cellCgbMapLookupTextureUnit( &m_xProgram, pxTextureUnit[ i ].m_szName );
			if( attribute_tmp == CELL_CGB_ERROR_FAILED )
			{
				Asura_Assertf( false, ("cellCgbMapLookupTextureUnit failed (%s). Probably vertex shader was changed!", pxTextureUnit[ i ].m_szName) );
			}
			else
			{
				pxTextureUnit[ i ].m_cTextureUnit[0] = attribute_tmp;
			}
		}
	}
}

void Asura_PS3_GCM_VertexShader::InitVertexBufferDeclaration( Asura_PS3_VertexBufferDeclaration* pxVertexDeclaration, u_char cVertexDeclarationElements )
{
	for( int i = 0; i < cVertexDeclarationElements; ++i )
	{
		u_int attribute_tmp = cellCgbMapLookupAttribute( &m_xProgram, pxVertexDeclaration[ i ].m_szName );
		if( attribute_tmp == CELL_CGB_ERROR_FAILED )
		{
			Asura_Assertf( false, ("cellCgbMapLookupAttribute failed. Couldn't find attribute \"%s\"", pxVertexDeclaration[ i ].m_szName) );
			pxVertexDeclaration[ i ].m_ucAttributeIndex = 0xFF;
		}
		else
		{
			pxVertexDeclaration[ i ].m_ucAttributeIndex = attribute_tmp;
		}
	}
}

void Asura_PS3_GCM_VertexShader::SetBoolParameter( int iParameter, bool bValue )
{
	float fBoolValue[4];

	if( bValue )
	{
		fBoolValue[ 0 ] = 1.0f;
		fBoolValue[ 1 ] = 1.0f;
		fBoolValue[ 2 ] = 1.0f;
		fBoolValue[ 3 ] = 1.0f;
	}
	else
	{
		fBoolValue[ 0 ] = 0.0f;
		fBoolValue[ 1 ] = 0.0f;
		fBoolValue[ 2 ] = 0.0f;
		fBoolValue[ 3 ] = 0.0f;
	}

	SetUniformParameter( iParameter, &fBoolValue[0] );
}

void Asura_PS3_GCM_VertexShader::LoadVertexProgramConstants( void )
{
	uint32_t count = cellCgbGetVertexConstantCount( &m_xProgram );
	for( int i = 0; i < (int)count; ++i )
	{
		const float *value = NULL;
		uint16_t reg = 0;
		//get the constant value and the register it needs to go to
		cellCgbGetVertexConstantValues( &m_xProgram, i, &reg, &value );
		//set the register to the value
		if( reg < 468 ) //This check was not suppose to be here, but it seems that the compiler has an error so we need to check if the register is not out of range; hope it will be corrected in the next release
		{
			SetRegisterValue( reg, value, 1 );
		}
	}
}

void Asura_PS3_GCM_VertexShader::SetRegisterValue( int iRegister, const float* pfData, unsigned short usFloat4 )
{
	// make sure cached register values are valid
	if ( s_pxIgnoreRegisterCache && this != s_pxIgnoreRegisterCache )
	{
		ResetToDefault();
	}

#if 0
	for( int i = iRegister, k = 0; i < iRegister + usFloat4; ++i, k += 4 )
	{
		if( Asura_Mem::Compare( &pfData[ k ], s_afRegisters[ i ], 16 ) != 0 ) // 4 * sizeof( float )
		{
			cellGcmSetVertexProgramParameterBlockUnsafeInline( Asura_PS3_Graphics::GetGcmCurrentContext(), i, 1, &pfData[ k ] );
			Asura_Mem::Copy( s_afRegisters[ i ], &pfData[ k ], 16 );
		}
	}
#else
	if (iRegister == 0xFFFF)
	{
		return;
	}
	int iStartRegister = -1, iCurrentPosition = 0;
	for( int i = iRegister, k = 0; i < iRegister + usFloat4; ++i, k += 4 )
	{
		if( Asura_Mem::Compare( &pfData[ k ], s_afRegisters[ i ], 16 ) != 0 ) // 4 * sizeof( float )
		{
			if( iStartRegister == -1 )
			{
				iStartRegister = i;
				iCurrentPosition = k;
			}
		}
		else
		{
			if( iStartRegister != -1 )
			{
				int l = i - iStartRegister;
				Asura_Mem::Copy( s_afRegisters[ iStartRegister ], &pfData[ iCurrentPosition ], l << 4 ); //float4
				cellGcmSetVertexProgramParameterBlockUnsafeInline( Asura_PS3_Graphics::GetGcmCurrentContext(), iStartRegister, l, &pfData[ iCurrentPosition ] );
				iStartRegister = -1;
			}
		}
	}
	if( iStartRegister != -1 )
	{
		int l = iRegister + usFloat4 - iStartRegister;
		Asura_Mem::Copy( s_afRegisters[ iStartRegister ], &pfData[ iCurrentPosition ], l << 4 ); //float4
		cellGcmSetVertexProgramParameterBlockUnsafeInline( Asura_PS3_Graphics::GetGcmCurrentContext(), iStartRegister, l, &pfData[ iCurrentPosition ] );
	}
#endif
}