//////////////////////////////////////////////////////////////////////////////
//
// Asura_Chunk_ResourceFile_PS3_Botanicals.cpp
//
// Copyright (c) 2006 Rebellion. All rights reserved.
//
//////////////////////////////////////////////////////////////////////////////

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

#include "Core/Asura.h"


#include "ChunkSystem/Asura_Chunk_ResourceFile_PS3_Botanicals.h"

#ifdef ASURA_USE_BOTANICALS
// Asura Platform-independent
#include "Botanicals/Asura_Botanical_Game_Render_Type_BasicInteractive.h"
#include "ChunkSystem/Asura_Chunk_Stream.h"
#include "Core/Asura_HalfFloat.h"
#include "Core/Asura_Hash.h"
#include "Core/Asura_Material_System.h"

// Asura PS3
#include "Botanicals/Asura_PS3_Botanicals.h"
#include "ChunkSystem/Asura_Chunk_ResourceFile_PS3.h"

#include "Rendering/Shaders/Asura_PS3_GCM_Shader.h"
#include "Rendering/ShadersDefinition/VD/BotanicalsBillboards_VD.h"
#include "Rendering/ShadersDefinition/VD/BotanicalsImposters_VD.h"
#include "Rendering/ShadersDefinition/VD/BotanicalsGeometry_VD.h"

/******************************************************************************/
/* Constants                                                                  */
/******************************************************************************/

/******************************************************************************/
/* Asura_Chunk_ResourceFile_PS3_Botanicals Class Functions                */
/******************************************************************************/
void Asura_Chunk_ResourceFile_PS3_Botanicals::ProcessBotanicalElement( Asura_Chunk_Stream& xStream, Asura_PS3_Botanicals_Element& xCurrElement )
{
	u_int uElementType;
	xStream >> uElementType;
	xCurrElement.m_eType = static_cast< Asura_PS3_Botanicals_Mesh_Type >( uElementType );

	xStream >> xCurrElement.m_iNumLODs;
	xCurrElement.m_pxLODData = new Asura_PS3_Botanicals_LODData[ xCurrElement.m_iNumLODs ];

	if( !xCurrElement.m_pxLODData )
	{
		Asura_Assert( false, "Failed to create LOD Data element array." );
		return;
	}

#if defined( ASURA_BOTANICAL_SUPPORT_SECTION_VERSIONS) && 1 == ASURA_BOTANICAL_SUPPORT_SECTION_VERSIONS
	xStream >> xCurrElement.m_xSectionID;
	xStream >> xCurrElement.m_xVersionID;
#endif

	for( int j = 0; j < xCurrElement.m_iNumLODs; ++j )
	{
		xCurrElement.m_pxLODData[ j ].Initialise( xCurrElement.m_eType );
		Asura_PS3_Botanicals_LODData& xCurrLOD = xCurrElement.m_pxLODData[ j ];
		ProcessBotanicalLOD( xStream, xCurrLOD );
	}

	xStream >> xCurrElement.m_fBSCentreHeight;
	xStream >> xCurrElement.m_fBSRadius;
}

void Asura_Chunk_ResourceFile_PS3_Botanicals::ProcessBotanicalLOD( Asura_Chunk_Stream& xStream, Asura_PS3_Botanicals_LODData& xCurrentLOD )
{
	switch( xCurrentLOD.m_eType )
	{
	case TYPE_BOTANICALS_GEOMETRY_1SIDED:
	case TYPE_BOTANICALS_GEOMETRY_2SIDED:
		{
			Process2And1SidedMesh( xStream, xCurrentLOD );
			break;
		}
	case TYPE_BOTANICALS_BILLBOARDS:
		{
			ProcessBillboards( xStream, xCurrentLOD );
			break;
		}
	case TYPE_BOTANICALS_IMPOSTER:
		{
			ProcessImposters( xStream, xCurrentLOD );
			break;
		}
	default:
		{
			Asura_Assert( false, "Unknown botanicals element type." );
			break;
		}
	}
	xStream >> xCurrentLOD.m_fMinDistance;
	xStream >> xCurrentLOD.m_fMinFullAlphaDistance;
	xStream >> xCurrentLOD.m_fMaxFullAlphaDistance;
	xStream >> xCurrentLOD.m_fMaxDistance;
}

void Asura_Chunk_ResourceFile_PS3_Botanicals::Process2And1SidedMesh( Asura_Chunk_Stream &xStream, Asura_PS3_Botanicals_LODData& xCurrentLOD )
{
	Asura_PS3_Botanicals_LOD_Geometry& xCurrLOD = *xCurrentLOD.m_pxGeometryLODs;

	xStream >> xCurrLOD.m_uMaterialResponseHashID;
	xStream >> xCurrLOD.m_uFPHashID;
	xStream >> xCurrLOD.m_uNumIndices >> xCurrLOD.m_uNumTriangles >> xCurrLOD.m_uNumVertices;

	//Create and populate the vertex buffer
	xCurrLOD.m_xVB.Initialise( BOTANICALS_GEOMETRY_INSTANCE_VERTEX_SIZE, xCurrLOD.m_uNumVertices, CELL_GCM_LOCATION_MAIN );
	void* const pVertexData = xCurrLOD.m_xVB.LockForWriting();
	xStream.Read( BOTANICALS_GEOMETRY_INSTANCE_VERTEX_SIZE * xCurrLOD.m_uNumVertices, pVertexData );
	xCurrLOD.m_xVB.Unlock();

	//Create and populate the index buffer
	xCurrLOD.m_xIB.Initialise( xCurrLOD.m_uNumIndices + 1, CELL_GCM_DRAW_INDEX_ARRAY_TYPE_16, CELL_GCM_LOCATION_MAIN, true );
	u_short* const pIndexData = static_cast<u_short*>( xCurrLOD.m_xIB.LockForWriting() );
	xStream.Read( sizeof( u_short )* xCurrLOD.m_uNumIndices, pIndexData );
	pIndexData[ xCurrLOD.m_uNumIndices ] = 0xFFFF;
	++xCurrLOD.m_uNumIndices;
	xCurrLOD.m_xIB.Unlock();
}

void Asura_Chunk_ResourceFile_PS3_Botanicals::ProcessBillboards( Asura_Chunk_Stream& xStream, Asura_PS3_Botanicals_LODData& xCurrentLOD )
{
	Asura_PS3_Botanicals_LOD_Billboards& xCurrLOD = *xCurrentLOD.m_pxBillboardLODs;

	xStream >> xCurrLOD.m_uMaterialResponseHashID;
	xStream >> xCurrLOD.m_uFPHashID;
	xStream >> xCurrLOD.m_iNumBillboards;

	int iVertexNum = xCurrLOD.m_iNumBillboards * 4;

	xCurrLOD.m_xBillboardVertexBuffer.Initialise( BOTANICALS_BILLBOARDS_INSTANCE_VERTEX_SIZE, iVertexNum, CELL_GCM_LOCATION_MAIN );

	void* pxBillboardData = xCurrLOD.m_xBillboardVertexBuffer.LockForWriting();

	u_int uiSize = iVertexNum * BOTANICALS_BILLBOARDS_INSTANCE_VERTEX_SIZE;
	xStream.Read( uiSize, pxBillboardData );
}

void Asura_Chunk_ResourceFile_PS3_Botanicals::ProcessImposters( Asura_Chunk_Stream& xStream, Asura_PS3_Botanicals_LODData& xCurrentLOD )
{
	Asura_PS3_Botanicals_LOD_Imposter& xCurrLOD = *xCurrentLOD.m_pxImposterLODs;

	xStream >> xCurrLOD.m_iNumImposterAngles;						
	xStream >> xCurrLOD.m_uMaterialResponseHashID;
	xStream >> xCurrLOD.m_uFPHashID;

	xCurrLOD.m_xVertexBuffer.Initialise( BOTANICALS_IMPOSTERS_VERTEX_SIZE, xCurrLOD.m_iNumImposterAngles * 4, CELL_GCM_LOCATION_MAIN );

	void* pxImposterData = xCurrLOD.m_xVertexBuffer.LockForWriting();
	u_int uiSize = xCurrLOD.m_iNumImposterAngles * BOTANICALS_IMPOSTERS_VERTEX_SIZE * 4;
	xStream.Read( uiSize, pxImposterData );
}

void Asura_Chunk_ResourceFile_PS3_Botanicals::ProcessBotanicalsShape( Asura_Chunk_Stream &xStream, Asura_PS3_Botanicals_Shape& xCurrShape )
{
	xStream >> xCurrShape.m_uHashID;

	xStream >> xCurrShape.m_iNumElements;
	xCurrShape.m_pxElements = new Asura_PS3_Botanicals_Element[ xCurrShape.m_iNumElements ];
	Asura_Assert( xCurrShape.m_pxElements, "Failed to create botanicals shape element array." );
	for( int i = 0; i < xCurrShape.m_iNumElements; ++i )
	{
		Asura_PS3_Botanicals_Element& xCurrElement = xCurrShape.m_pxElements[i];
		ProcessBotanicalElement( xStream, xCurrElement );
	}

	xCurrShape.Process( xStream );
}

void Asura_Chunk_ResourceFile_PS3_Botanicals::ProcessBotanicalTemplate( Asura_Chunk_Stream &xStream, Asura_PS3_Botanicals_Template &xCurrTemplate )
{
	xStream >> xCurrTemplate.m_xName;

	u_int uBotanicalsShapeIndex;
	xStream >> uBotanicalsShapeIndex;

	xCurrTemplate.m_pxShape = &Asura_PS3_Botanicals_System::s_pxShapes[ uBotanicalsShapeIndex ];
	xCurrTemplate.SetType( Asura_BGR_TypeID( uBotanicalsShapeIndex));

	Asura_Bounding_Box xTemplateBoundingBox;
	xStream >> xTemplateBoundingBox;
	xTemplateBoundingBox.Grow( xCurrTemplate.m_pxShape->GetBoundingRadius());
	xCurrTemplate.SetBoundingBox( xTemplateBoundingBox);

	xStream >> xCurrTemplate.m_iNumInstances;
	xCurrTemplate.m_iOffsetToNextBase = 1;

	xCurrTemplate.m_pxInstances = new Asura_PS3_Botanicals_Instance[ xCurrTemplate.m_iNumInstances ];
	xStream.Read( sizeof( Asura_PS3_Botanicals_Instance ) * xCurrTemplate.m_iNumInstances, xCurrTemplate.m_pxInstances );

	// Allocate enough visibility queries so we can store one per instance
	xCurrTemplate.m_pxVisibilityQueries = new Asura_Static_Visibility_Query[ xCurrTemplate.m_iNumInstances ];

#if defined( ASURA_BOTANICAL_SUPPORT_DYNAMIC_DATA) && 1 == ASURA_BOTANICAL_SUPPORT_DYNAMIC_DATA
	xCurrTemplate.SetDynamicDataLayout( xCurrTemplate.m_pxShape->GetDynamicDataLayout());
	xCurrTemplate.AllocateDynamicData( xCurrTemplate.m_iNumInstances);
#endif
}
#endif // defined(ASURA_USE_BOTANICALS)

////////////////////////////////////////////////////////////////////////////////
// Asura_Chunk_ResourceFile_PS3_Botanicals::Process()
// Processes an PS3 botanicals chunk
// Parameters:		szFilename - Filename of incoming chunk (not used)
//					xStream - Chunk stream from which to read
// Return value:	None
////////////////////////////////////////////////////////////////////////////////
void Asura_Chunk_ResourceFile_PS3_Botanicals::Process( const char *const szFilename, Asura_Chunk_Stream &xStream )
{
#ifdef ASURA_USE_BOTANICALS
	if( Version != ASURA_PS3_BOTANICALS_CHUNK_VERSION )
	{
		Asura_Assertf( false, ("Invalid botanicals chunk version (%d) encountered. Should be %d. You may need new code, or to re-export the level.", Version, ASURA_PS3_BOTANICALS_CHUNK_VERSION) );
		return;
	}

	Asura_Assert( !Asura_PS3_Botanicals_System::s_pxShapes, "Botanicals shape list already exists." );

	// DMR - 24/11/06 - Added a version check
	uint32 uVersion;
	xStream >> uVersion;
	if (Asura_Chunk_ResourceFile_PS3_Botanicals::CURRENT_VERSION == 7 && uVersion == 6)
	{
		//BW I only made a minor change (Asura_Platform_BGR_Instance::m_uModuleIndex changed to a short), so it should be compatible
		Asura_Assert(false,"Botanicals data is not current version. Re-export the level! Will try to load anyway.");
	}
	else if (uVersion!= Asura_Chunk_ResourceFile_PS3_Botanicals::CURRENT_VERSION)
	{
		Asura_Assert(false,"Botanicals data is not current version. Re-export the level!");
		return;
	}

	xStream >> uVersion;
	if( Asura_BGR_Manager::s_uChunkVersion != uVersion)
	{
		Asura_Assert(false,"Botanical Features or Version does not match. Re-export the level!");
		return;
	}

	xStream >> Asura_PS3_Botanicals_System::s_uNumShapes;
	Asura_PS3_Botanicals_System::s_pxShapes = new Asura_PS3_Botanicals_Shape[ Asura_PS3_Botanicals_System::s_uNumShapes ];
	Asura_Assert( Asura_PS3_Botanicals_System::s_pxShapes, "Failed to create botanicals shape array." );
	for( u_int u = 0; u < Asura_PS3_Botanicals_System::s_uNumShapes; ++u )
	{
		Asura_PS3_Botanicals_Shape& xCurrShape = Asura_PS3_Botanicals_System::s_pxShapes[ u ];
		xCurrShape.SetType( Asura_BGR_TypeID( u ) );
		ProcessBotanicalsShape( xStream, xCurrShape );
	}


	xStream >> Asura_PS3_Botanicals_System::s_uNumTemplates;
	Asura_PS3_Botanicals_System::s_pxTemplates = new Asura_PS3_Botanicals_Template[ Asura_PS3_Botanicals_System::s_uNumTemplates ];
	Asura_Assert( Asura_PS3_Botanicals_System::s_pxTemplates, "Failed to create botanicals template array." );

	for( u_int u = 0; u < Asura_PS3_Botanicals_System::s_uNumTemplates; ++u )
	{
		Asura_PS3_Botanicals_Template& xCurrTemplate = Asura_PS3_Botanicals_System::s_pxTemplates[u];
        ProcessBotanicalTemplate( xStream, xCurrTemplate );
	}

	for(int i=0; i< Asura_PS3_Botanicals_System::NUM_INSTANCE_BUFFERS; i++)
	{		
		Asura_PS3_Botanicals_System::s_axInstanceVBs[ i ].Initialise( sizeof(Asura_PS3_Botanicals_Instance::RenderData), ASURA_BOTANICALS_MAX_ELEMENT_INSTANCES_PER_FRAME, CELL_GCM_LOCATION_MAIN );
	}

	{	
		Asura_PS3_Botanicals_Template* pxCurrTemplateBase = Asura_PS3_Botanicals_System::s_pxTemplates;
		for( u_int u = 1; u < Asura_PS3_Botanicals_System::s_uNumTemplates; ++u)
		{
			Asura_PS3_Botanicals_Template* pxCurrTemplate = Asura_PS3_Botanicals_System::s_pxTemplates + u;
			
			if( pxCurrTemplate->m_pxShape != pxCurrTemplateBase->m_pxShape)
			{
				pxCurrTemplateBase = pxCurrTemplate;
				continue;
			}
			
			pxCurrTemplateBase->m_iOffsetToNextBase += 1;
		}
	}
#endif // defined(ASURA_USE_BOTANICALS)
}


// (MP - 05.03.2009) Left this code here in case we ever want to do it
// 'properly' and because it's a somewhat handy way of calculating the max
// number of instances required if rendering everything (and then you can,
// eg., take 10% of that as your budget). To do this, just call
// calc_max_simultaneous_element_instances() for each template.
#if 0

#include "Core/Asura_SortAndSearch.h"

struct FadePoint
{
    float fDist;
    bool bIn; // Or, if not, out.
};

static int qsort_compare_fadepoint_callback( const void* pvData1, const void* pvData2 )
{
    const FadePoint* pxFadePoint1 = static_cast<const FadePoint*>( pvData1 );
    const FadePoint* pxFadePoint2 = static_cast<const FadePoint*>( pvData2 );

    // Renderer regards fade distances as inclusive so sort fade-in points
    // before fade-out points to ensure we catch the maximum count.
    if( pxFadePoint1->fDist < pxFadePoint2->fDist )
    {
        return -1;
    }
    else if( pxFadePoint1->fDist > pxFadePoint2->fDist )
    {
        return 1;
    }
    else
    {
        if( pxFadePoint1->bIn && !pxFadePoint2->bIn )
        {
            return -1;
        }
        else if( !pxFadePoint1->bIn && pxFadePoint2->bIn )
        {
            return 1;
        }
        else
        {
            return 0;
        }
    }
}

static u_int calc_max_simultaneous_element_instances_for_section_and_version( const Asura_PS3_Botanicals_Template& xTemplate, Asura_BGR_Section_ID uSectionID, Asura_BGR_Version_ID uVersionID )
{
    u_int uTotalNumFadePoints = 0;
    for( int iElement = 0; iElement < xTemplate.m_pxShape->m_iNumElements; ++iElement )
    {
        const Asura_PS3_Botanicals_Element& xElement = xTemplate.m_pxShape->m_pxElements[iElement];

#ifdef ASURA_BOTANICAL_SUPPORT_SECTION_VERSIONS
        if( xElement.m_xSectionID != uSectionID || xElement.m_xVersionID != uVersionID ) { continue; }
#endif

        uTotalNumFadePoints += xElement.m_iNumLODs * 2;
    }

    FadePoint* pxFadePoints = new FadePoint[uTotalNumFadePoints];

    u_int uFadePointOut = 0;
    for( int iElement = 0; iElement < xTemplate.m_pxShape->m_iNumElements; ++iElement )
    {
        const Asura_PS3_Botanicals_Element& xElement = xTemplate.m_pxShape->m_pxElements[iElement];

#ifdef ASURA_BOTANICAL_SUPPORT_SECTION_VERSIONS
        if( xElement.m_xSectionID != uSectionID || xElement.m_xVersionID != uVersionID ) { continue; }
#endif

        for( int iLOD = 0; iLOD < xElement.m_iNumLODs; ++iLOD )
        {
            pxFadePoints[uFadePointOut].fDist = xElement.m_pxLODData[iLOD].m_fMinDistance;
            pxFadePoints[uFadePointOut].bIn = true;
            ++uFadePointOut;

            pxFadePoints[uFadePointOut].fDist = xElement.m_pxLODData[iLOD].m_fMaxDistance;
            pxFadePoints[uFadePointOut].bIn = false;
            ++uFadePointOut;
        }
    }
    Asura_Assert( uFadePointOut == uTotalNumFadePoints, "" );

    Asura_SortAndSearch::Quicksort( pxFadePoints, uTotalNumFadePoints, qsort_compare_fadepoint_callback );

    u_int uMaxRenderingElements = 0;

    u_int uNumRenderingElements = 0;
    for( u_int u = 0; u < uTotalNumFadePoints; ++u )
    {
        const FadePoint& xFadePoint = pxFadePoints[u];
        if( xFadePoint.bIn )
        {
            ++uNumRenderingElements;
            uMaxRenderingElements = Asura_Maths::Max( uNumRenderingElements, uMaxRenderingElements );
        }
        else
        {
            Asura_Assert( uNumRenderingElements, "" );
            --uNumRenderingElements;
        }
    }
    Asura_Assert( !uNumRenderingElements, "" );

    return uMaxRenderingElements;
}

static u_int calc_max_simultaneous_element_instances( const Asura_PS3_Botanicals_Template& xTemplate )
{
    // Sort the fade-in / fade-out thresholds and find maximum count,
    // accounting for distinct elements rendering simultaneously, cross-
    // fading between elements' LODs, and state restrictions.
    //
    // The code for handling the state, section and version stuff is quite
    // complex but it seems that for each section the possible versions are
    // all mutually exclusive. So, assume that all sections are always 
    // rendered and evaluate the maximum for each of them, then sum them.

    u_int uMaxRenderingElements = 0;

    // The section and version IDs seem to be somewhat arbitrary so prepare a
    // list of them.
    Asura_LinkList<Asura_BGR_Section_ID> xSectionIDList;
    Asura_LinkList<Asura_BGR_Version_ID> xVersionIDList;
    for( int iElement = 0; iElement < xTemplate.m_pxShape->m_iNumElements; ++iElement )
    {
        Asura_BGR_Section_ID uSectionID = xTemplate.m_pxShape->m_pxElements[iElement].m_xSectionID;
        if( !xSectionIDList.Find(uSectionID) )
        {
            xSectionIDList.Push_Back( uSectionID );
        }

        Asura_BGR_Version_ID uVersionID = xTemplate.m_pxShape->m_pxElements[iElement].m_xVersionID;
        if( !xVersionIDList.Find(uVersionID) )
        {
            xVersionIDList.Push_Back( uVersionID );
        }
    }

    // Find the maximum for each section and add it on.
    for( Asura_LinkList_ConstIt<Asura_BGR_Section_ID> xSectionIDIt(xSectionIDList); !xSectionIDIt.Done(); xSectionIDIt.Next() )
    {
        const Asura_BGR_Section_ID uSectionID = *(xSectionIDIt.GetCurrent());

        u_int uMaxRenderingElementsForThisSection = 0;

        // Find the maximum for each version ID and see if it's the highest
        // for this section so far.
        for( Asura_LinkList_ConstIt<Asura_BGR_Version_ID> xVersionIDIt(xVersionIDList); !xVersionIDIt.Done(); xVersionIDIt.Next() )
        {
            u_int uThisMax = calc_max_simultaneous_element_instances_for_section_and_version( xTemplate, uSectionID, *(xVersionIDIt.GetCurrent()) );
            uMaxRenderingElementsForThisSection = Asura_Maths::Max( uMaxRenderingElementsForThisSection, uThisMax );
        }

        uMaxRenderingElements += uMaxRenderingElementsForThisSection;
    }

    // Sanity check against the worst possible case.
    Asura_Assert( uMaxRenderingElements <= static_cast<u_int>(xTemplate.m_pxShape->m_iNumElements * 2), "" );

    return uMaxRenderingElements * xTemplate.m_iNumInstances;
}

#endif
