/**
*
* \file Asura_PlatformRenderWater.cpp
*
* Created by Robert Dibley - 16/11/2004 14:18:36
*
* Copyright (C) 2004 Rebellion, All Rights Reserved.
*
* \brief
*
**/

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

#include "Core/Asura.h"

// This file's header.
#include "Asura_PlatformRenderWater.h"

// Stdlib platform-specific.
#include <eestruct.h>

// Engine independent.
#include "Debugging/Asura_Assert.h"
#include "Rendering/Asura_Water.h"

// Engine platform-specific.
#include "Rendering/Asura_PlatformRendering.h"
#include "Profiling/Asura_PS2_Profiling.h"
#include "Rendering/Asura_PS2_VU1.h"
#include "Rendering/Asura_PS2_WaterDefines.h"
#include "Rendering/Asura_PS2_WaterRenderer.h"

// M A C R O S ///////////////////////////////////////////////////////////////

#define NUMBER_TRIS_IN_PACKET			 (4)
#define NUMBER_VERTICES_IN_PACKET		 (3 * NUMBER_TRIS_IN_PACKET)
#define QW_PER_VERT						 (2)
#define QW_FOR_HEAD_AND_FOOT			 (2)
#define QW_FOR_PACKET					 ((QW_PER_VERT * NUMBER_VERTICES_IN_PACKET) + QW_FOR_HEAD_AND_FOOT)
#define QW_FOR_UNPACK					 ( QW_PER_VERT * NUMBER_VERTICES_IN_PACKET )

#define WATER_PACKET_SIZE_LIMIT		( ASURA_PS2_WATER__SIZE__INPUT_BFR )
#define WATER_PACKET_VERTEX_LIMIT	( ( WATER_PACKET_SIZE_LIMIT ) / 2 )
#define WATER_PACKET_TRI_LIMIT		( ( WATER_PACKET_VERTEX_LIMIT ) / 3 )
#define WATER_PACKET_HEADER_SIZE	( 1 )
#define WATER_PACKET_FOOTER_SIZE	( 1 )

#define WATER_VERTS_PER_PACKET		( ( WATER_PACKET_TRI_LIMIT ) * 3 )
#define WATER_QWC_PER_PACKET		( ( WATER_VERTS_PER_PACKET ) * 2 )

// G L O B A L S /////////////////////////////////////////////////////////////

DEBUGVAR bool dbg_bSkipMyWater = false;

// C L A S S   I M P L E M E N T A T I O N S /////////////////////////////////

//
// Asura_Water_System.
//

void Asura_Water_System::Platform_Initialise()
{
	Asura_PS2_WaterRenderer::Initialise();
}

void Asura_Water_System::Platform_Reset( void )
{
	Asura_PS2_WaterRenderer::Reset();
}

void Asura_Water_System::Platform_Shutdown(void)
{
	Asura_PS2_WaterRenderer::Shutdown();
}

void Asura_Water_System::Platform_BeginRender(void)
{
	ASURA_PS2_PROFILE__ADD_VIF_START( ASURA_PS2_PROFILE__WATER_MARKER );

	Asura_PS2_WaterRenderer::Begin();
}

void Asura_Water_System::Platform_Render(Asura_Platform_Water_Packet *pPacket)
{
	Asura_Assert(pPacket->m_bClosed, "Somebody forgot to close this packet so it will probably crash the DMA chain");
	if (!dbg_bSkipMyWater)
	{
		Asura_PS2_WaterRenderer::RenderMesh(reinterpret_cast<u_int>(pPacket->m_pBuffer), pPacket->m_iTotalQWC , pPacket->m_iVertexCount);
        
        // This mesh data is currently just a tri list so num tris will be
        // (num vertices / 3). Obviously this tri count will not reflect any
        // of the runtime subdivision going on.
        Asura_Assert( !(pPacket->m_iVertexCount % 3), "" );
        Asura_PS2_Render::UpdateTriStats( Asura_Camera::GetNumberOfCameras() * (pPacket->m_iVertexCount / 3),
                                          Asura_PS2_WaterRenderer::GetNumPassesPerTri(),
                                          Asura_PS2_Render::TRICOUNT_WATER );
	}
}

void Asura_Water_System::Platform_EndRender(void)
{ 
	Asura_PS2_WaterRenderer::End();

	ASURA_PS2_PROFILE__ADD_VIF_END( ASURA_PS2_PROFILE__WATER_MARKER );
}

//
// Asura_Platform_Water_Packet.
//

void Asura_Platform_Water_Packet::Reset( void )
{
    Asura_DeallocateAlign( m_pBuffer );
	m_iVertexCount = 0;
	m_iCheckCount = 0;
	m_pBuffer = NULL;
	m_pCurrent = NULL;
	m_pHeader = NULL;
	m_iSubPacketVertexCount = 0;
	m_iTotalQWC = 0;
	m_bClosed = false;
}

void Asura_Platform_Water_Packet::SetVertexCount( int iVertCount ) 
{
	m_iVertexCount = iVertCount;

	int iTotalQWC;
    
    {
        iTotalQWC = 0;
	    while (iVertCount >= WATER_VERTS_PER_PACKET)
	    {
		    iTotalQWC += WATER_PACKET_HEADER_SIZE;

		    iVertCount -= WATER_VERTS_PER_PACKET;
		    iTotalQWC += WATER_QWC_PER_PACKET;

		    iTotalQWC += WATER_PACKET_FOOTER_SIZE;
	    }

	    if (iVertCount > 0)
	    {
		    iTotalQWC += WATER_PACKET_HEADER_SIZE;
		    iTotalQWC += iVertCount * QW_PER_VERT;
		    iTotalQWC += WATER_PACKET_FOOTER_SIZE;
	    }
    }
    
	m_pBuffer = reinterpret_cast<Asura_PS2_QW*>(Asura_AllocateAlign( iTotalQWC * 16 , ASURA_ALIGN_128_BYTE ));
	m_pCurrent = m_pBuffer;
	m_iSubPacketVertexCount = 0;
	m_iTotalQWC = iTotalQWC;
}

void Asura_Platform_Water_Packet::AddFooter(void)
{
	m_pHeader->u32s[2] = SCE_VIF1_SET_ITOP(m_iSubPacketVertexCount/3 /* TRIS */ ,0);	// num triangles - should we do verts ? strips ?
	m_pHeader->u32s[3] = SCE_VIF1_SET_UNPACKR(/*offset*/ 0, /*qwc*/ m_iSubPacketVertexCount * QW_PER_VERT, /*cmd*/ Asura_PS2_VU1::VIFPK_V4_32, /*irq*/false);

	m_pCurrent->u32s[0] = 0;
	m_pCurrent->u32s[1] = 0;
	m_pCurrent->u32s[2] = ASURA_PS2_MAKE_MSCAL_FROM_VU_PTR(VU1_WATER_RENDER);
	m_pCurrent->u32s[3] = SCE_VIF1_SET_FLUSH(0);
	m_pCurrent++;
	m_iSubPacketVertexCount = 0;
}

void Asura_Platform_Water_Packet::AddVertex(float x, float y, float z, float r, float g, float b, float u, float v)
{
    if (m_iSubPacketVertexCount == 0)
    {
	    m_pHeader = m_pCurrent;
	    m_pHeader->u32s[0] = SCE_VIF1_SET_FLUSH(0);
	    m_pHeader->u32s[1] = 0;
	    m_pHeader->u32s[2] = 0;	// will contain number of tris in packet
	    m_pHeader->u32s[3] = 0;	// will contain UNPACKR vifcode
	    m_pCurrent++;
    }

	m_pCurrent->f32s[0] = x;
	m_pCurrent->f32s[1] = y;
	m_pCurrent->f32s[2] = z;
	m_pCurrent->f32s[3] = v;
	m_pCurrent++;

	m_pCurrent->f32s[0] = u;
	m_pCurrent->f32s[1] = r;
	m_pCurrent->f32s[2] = g;
	m_pCurrent->f32s[3] = b;
	m_pCurrent++;

	m_iSubPacketVertexCount++;
	m_iCheckCount++;

	if (m_iSubPacketVertexCount == WATER_VERTS_PER_PACKET) AddFooter();
}

//void Asura_Platform_Water_Packet::CreatePacket( const Asura_Water_System::Water_Quad* pxQuadDef, bool bFlip )
//{
//	// compute grid step size - want something around 100m, but giving a whole number of tiles to exactly cover area
//	float f_xStep, f_zStep, f_uStep, f_vStep;
//	int	  i_xCount, i_zCount;
//
//	i_xCount = Asura_Maths::FloatToInt_Chop((pxQuadDef->fXSize + 50.0f) / 100.0f);
//	i_zCount = Asura_Maths::FloatToInt_Chop((pxQuadDef->fZSize + 50.0f) / 100.0f);
//
//	f_xStep = pxQuadDef->fXSize / i_xCount;
//	f_zStep = pxQuadDef->fZSize / i_zCount;
//	f_uStep = pxQuadDef->fURepeats / i_xCount;		// note we assume u and x are tied together for this
//	f_vStep = pxQuadDef->fVRepeats / i_zCount;
//
//	int i_vertCount = i_xCount * i_zCount * 6;
//	
//	SetVertexCount(i_vertCount);	// returns qwc for dma packet to be used later
//
//  const float fHeight = pxQuadDef->fHeight;
//  const float fR      = pxQuadDef->fRed;
//  const float fG      = pxQuadDef->fGreen;
//  const float fB      = pxQuadDef->fBlue;
//
//	int i,j;
//	float f_currX = pxQuadDef->fXPos;
//	float f_currU = 0.0f;
//	for (i=0; i<i_xCount; i++)
//	{
//		float f_currZ = pxQuadDef->fZPos;
//		float f_currV = 0.0f;
//		for (j=0; j<i_zCount; j++)
//		{
//			if (!bFlip)
//			{
//				// add vertices for two triangles
//				AddVertex(f_currX          , fHeight, f_currZ,           fR, fG, fB, f_currU          , f_currV);
//				AddVertex(f_currX          , fHeight, f_currZ + f_zStep, fR, fG, fB, f_currU          , f_currV + f_vStep);
//				AddVertex(f_currX + f_xStep, fHeight, f_currZ          , fR, fG, fB, f_currU + f_uStep, f_currV);
//
//				AddVertex(f_currX + f_xStep, fHeight, f_currZ          , fR, fG, fB, f_currU + f_uStep, f_currV);
//				AddVertex(f_currX          , fHeight, f_currZ + f_zStep, fR, fG, fB, f_currU          , f_currV + f_vStep);
//				AddVertex(f_currX + f_xStep, fHeight, f_currZ + f_zStep, fR, fG, fB, f_currU + f_uStep, f_currV + f_vStep);
//			}
//			else
//			{
//				// add vertices for two triangles - but flipped
//				AddVertex(f_currX          , fHeight, f_currZ,           fR, fG, fB, f_currU          , f_currV);
//				AddVertex(f_currX          , fHeight, f_currZ + f_zStep, fR, fG, fB, f_currU          , f_currV + f_vStep);
//				AddVertex(f_currX + f_xStep, fHeight, f_currZ + f_zStep, fR, fG, fB, f_currU + f_uStep, f_currV + f_vStep);
//
//				AddVertex(f_currX + f_xStep, fHeight, f_currZ + f_zStep, fR, fG, fB, f_currU + f_uStep, f_currV + f_vStep);
//				AddVertex(f_currX + f_xStep, fHeight, f_currZ          , fR, fG, fB, f_currU + f_uStep, f_currV);
//				AddVertex(f_currX          , fHeight, f_currZ,           fR, fG, fB, f_currU          , f_currV);
//			}
//			f_currZ += f_zStep;
//			f_currV += f_vStep;
//			if (f_currV > 8.0f - f_vStep)
//			{
//				f_currV-=8.0f;
//			}
//		}
//		f_currX += f_xStep;
//		f_currU += f_uStep;
//		if (f_currU > 8.0f - f_uStep)
//		{
//			f_currU-=8.0f;
//		}
//	}
//
//  Close();
//}

void Asura_Platform_Water_Packet::Close( void )
{
	if (m_iSubPacketVertexCount != 0)
	{
		AddFooter();
	}
	Asura_Assert(m_iCheckCount == m_iVertexCount, "Closing packet with wrong number of verts in it.");
	m_bClosed = true;
}
