//
// Asura_PS3_GCM_VertexBuffer.h
//
// Created by KJFL - 04 July 2005
// Modified by Catalin Arsenescu - 28 March 2007
//
// Copyright (C) 2005 Rebellion, All Rights Reserved.
//

#ifdef ASURA_USE_PRAGMA_ONCE
	#pragma once
#endif

#ifndef __ASURA_PS3_GCM_VERTEXBUFFER_H__
#define __ASURA_PS3_GCM_VERTEXBUFFER_H__

// I N C L U D E S ///////////////////////////////////////////////////////////
#include "Rendering/Asura_Render.h"
#include "Rendering/Asura_PS3_Graphics.h"

#include "Core/Memory/Asura_Mem.h"

class Asura_PS3_GCM_VertexShader;
class Asura_PS3_VertexBufferDeclaration;

// C L A S S E S /////////////////////////////////////////////////////////////
class Asura_PS3_GCM_VertexBuffer
{
public:
	Asura_PS3_GCM_VertexBuffer()
		: m_uBaseOffest( 0 )
		, m_uNumVerts( 0 )
		, m_uBufferSize( 0 )
		, m_pxVertexData( NULL )
		, m_ucLocation( 0 )
		, m_ucVertSize( 0 )
	{
	}

	~Asura_PS3_GCM_VertexBuffer()
	{
		Destroy();
	}

	void Initialise( const u_char ucVertSize, const u_int uNumVerts, const u_char ucLocation, bool bForceLocation = false );
	void Shutdown( void );
	void Destroy( void );

	bool IsInitialized( void ) { return m_pxVertexData != NULL; }

	//bClearUnusedVertsDataArray must be false only if you use more that one vertex buffer to draw the data on screen (useful for instances)
	//for first buffer clear all the unused verts and for the second, third... use false for the last parameter
	//pbKeep is either NULL or an array of 16 bools specifying which entries in the vertex declaration should not be cleared, overriding bClearUnusedVertsDataArray.
	void Bind ( const Asura_PS3_VertexBufferDeclaration* pxDeclaration, unsigned char ucElements, u_short usFrequency = 1, bool bClearUnusedVertsDataArray = true, bool* pKeep = NULL ) const;
	void BindFromVertex ( const Asura_PS3_VertexBufferDeclaration* pxDeclaration, unsigned char ucElements, unsigned int uiVertex, u_short usFrequency = 1, bool bClearUnusedVertsDataArray = true, bool* pKeep = NULL ) const;

	// Locking/unlocking functions...

    const void* GetReadPtr( void ) const
    {
        return m_pxVertexData;
    }

	void* LockForWriting (void)
	{ 
		return	m_pxVertexData;
	}

	void Unlock (void)
	{
		//Probably the cache must be invalidated to maintain the coherency.
	}

	void RenderLines( const u_int uNumVertices, const u_int uStartVertex = 0 ) const
	{
		cellGcmSetDrawArraysUnsafeInline( Asura_PS3_Graphics::GetGcmCurrentContext(), CELL_GCM_PRIMITIVE_LINES, uStartVertex, uNumVertices );
	}

	void RenderLinesInline( const u_int uNumVertices, const void* data ) const
	{
		void*	xPointer = NULL;
		u_int	iSize = uNumVertices * m_ucVertSize;

		cellGcmSetDrawBeginUnsafeInline( Asura_PS3_Graphics::GetGcmCurrentContext(), CELL_GCM_PRIMITIVE_LINES );
		cellGcmSetDrawInlineArrayPointerUnsafeInline( Asura_PS3_Graphics::GetGcmCurrentContext(), iSize / sizeof( float ), &xPointer );
		Asura_Mem::Copy( xPointer, data, iSize );
		cellGcmSetDrawEndUnsafeInline(Asura_PS3_Graphics::GetGcmCurrentContext() );
	}

	void RenderQuadStrip( const u_int uNumVertices, const u_int uStartVertex = 0 ) const
	{
		cellGcmSetDrawArraysUnsafeInline( Asura_PS3_Graphics::GetGcmCurrentContext(), CELL_GCM_PRIMITIVE_QUAD_STRIP, uStartVertex, uNumVertices );
		Asura_Render::AddToTrianglesRendered( uNumVertices - 2 );
	}

	void RenderQuads( const u_int uNumVertices, const u_int uStartVertex = 0 ) const
	{
		cellGcmSetDrawArraysUnsafeInline( Asura_PS3_Graphics::GetGcmCurrentContext(), CELL_GCM_PRIMITIVE_QUADS, uStartVertex, uNumVertices );
		Asura_Render::AddToTrianglesRendered( uNumVertices / 2 );
	}

	void RenderQuadsInline( const u_int uNumVertices, const void* data ) const
	{
		void*	xPointer = NULL;
		u_int	iSize = uNumVertices * m_ucVertSize;

		cellGcmSetDrawBeginUnsafeInline( Asura_PS3_Graphics::GetGcmCurrentContext(), CELL_GCM_PRIMITIVE_QUADS );
		cellGcmSetDrawInlineArrayPointerUnsafeInline( Asura_PS3_Graphics::GetGcmCurrentContext(), iSize / sizeof( float ), &xPointer );
		Asura_Mem::Copy( xPointer, data, iSize );
		cellGcmSetDrawEndUnsafeInline(Asura_PS3_Graphics::GetGcmCurrentContext() );

		Asura_Render::AddToTrianglesRendered( uNumVertices / 2 );
	}

	void RenderTriangleFan( const u_int uNumVertices, const u_int uStartVertex = 0 ) const
	{
		cellGcmSetDrawArraysUnsafeInline( Asura_PS3_Graphics::GetGcmCurrentContext(), CELL_GCM_PRIMITIVE_TRIANGLE_FAN, uStartVertex, uNumVertices );
		Asura_Render::AddToTrianglesRendered( uNumVertices - 2 );
	}

	void RenderTriangleFanInline( const u_int uNumVertices, const void* data ) const
	{
		void*	xPointer = NULL;
		u_int	iSize = uNumVertices * m_ucVertSize;

		cellGcmSetDrawBeginUnsafeInline( Asura_PS3_Graphics::GetGcmCurrentContext(), CELL_GCM_PRIMITIVE_TRIANGLE_FAN );
		cellGcmSetDrawInlineArrayPointerUnsafeInline( Asura_PS3_Graphics::GetGcmCurrentContext(), iSize / sizeof( float ), &xPointer );
		Asura_Mem::Copy( xPointer, data, iSize );
		cellGcmSetDrawEndUnsafeInline(Asura_PS3_Graphics::GetGcmCurrentContext() );

		Asura_Render::AddToTrianglesRendered( uNumVertices - 2 );
	}

	void RenderTriangleStrip( const u_int uNumVertices, const u_int uStartVertex = 0 ) const
	{
		cellGcmSetDrawArraysUnsafeInline( Asura_PS3_Graphics::GetGcmCurrentContext(), CELL_GCM_PRIMITIVE_TRIANGLE_STRIP, uStartVertex, uNumVertices );
		Asura_Render::AddToTrianglesRendered( uNumVertices - 2 );
	}

	void RenderTriangleStripInline( const u_int uNumVertices, const void* data ) const
	{
		void*	xPointer = NULL;
		u_int	iSize = uNumVertices * m_ucVertSize;

		cellGcmSetDrawBeginUnsafeInline( Asura_PS3_Graphics::GetGcmCurrentContext(), CELL_GCM_PRIMITIVE_TRIANGLE_STRIP );
		cellGcmSetDrawInlineArrayPointerUnsafeInline( Asura_PS3_Graphics::GetGcmCurrentContext(), iSize / sizeof( float ), &xPointer );
		Asura_Mem::Copy( xPointer, data, iSize );
		cellGcmSetDrawEndUnsafeInline(Asura_PS3_Graphics::GetGcmCurrentContext() );

		Asura_Render::AddToTrianglesRendered( uNumVertices - 2 );
	}

	void RenderTriangles( const u_int uNumVertices, const u_int uStartVertex = 0 ) const
	{
		cellGcmSetDrawArraysUnsafeInline( Asura_PS3_Graphics::GetGcmCurrentContext(), CELL_GCM_PRIMITIVE_TRIANGLES, uStartVertex, uNumVertices );
		Asura_Render::AddToTrianglesRendered( uNumVertices / 3 );
	}

	u_int	GetVertexBufferSize	( void ) const { return m_uBufferSize; }
	u_char	GetLocation			( void ) const { return m_ucLocation; }
	u_char	GetSize				( void ) const { return m_ucVertSize; }
	u_int	GetBaseOffset		( void ) const { return m_uBaseOffest; }
	u_int	GetNumVerts			( void ) const { return m_uNumVerts; }

protected:
	u_int										m_uBaseOffest; // needed for cellGcmSetVertexDataArray
	u_int										m_uNumVerts;
	u_int										m_uBufferSize;
	void*										m_pxVertexData;

	u_char										m_ucLocation;	// needed for cellGcmSetVertexDataArray
	u_char										m_ucVertSize;	// needed for cellGcmSetVertexDataArray

	void	BindInternal( const Asura_PS3_VertexBufferDeclaration* pxDeclaration, unsigned char ucElements, u_int uiBaseOffset, u_short usFrequency, bool bClearUnusedVertsDataArray, bool* pbKeep = false ) const;

}; //class Asura_PS3_GCM_VertexBuffer

#endif//__ASURA_PS3_GCM_VERTEXBUFFER_H__
