//////////////////////////////////////////////////////////////////////////////
//
// Asura_Chunk_ResourceFile_PC_Skin.cpp
//
// Copyright (c) 2009 Rebellion. All rights reserved.
//
//////////////////////////////////////////////////////////////////////////////

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

#include "Core/Asura.h"

#if defined(ASURA_TARGET_SM3)

// Our header
#include "ChunkSystem/Asura_Chunk_ResourceFile_PC_Skin.h"

// Asura Platform-independent
#include "ChunkSystem/Asura_Chunk_Stream.h"
#include "Debugging/Asura_Assert.h"
#include "FileAccess/Asura_FileAccess.h"

// Asura PC
#include "Rendering/Asura_Direct3D_SkinRenderer.h"


/******************************************************************************/
/* Asura_Chunk_ResourceFile_Direct3D_Skin Class Functions                      */
/******************************************************************************/

////////////////////////////////////////////////////////////////////////////////
//
// Asura_Chunk_ResourceFile_PC_Skin::Process()
//
// Processes a chunk containing a PC skin resource
//
////////////////////////////////////////////////////////////////////////////////

bool Asura_Chunk_ResourceFile_PC_Skin::Process( const char* const szFilename, Asura_Chunk_Stream& xStream )
{
	if( Version != iCURRENT_VERSION )
	{
		Asura_Assertf( false,
			("Invalid skin chunk version (%d) for '%s'. Re-export level or get new code.",
			Version, szFilename) );

		return false;
	}

	const Asura_Hash_ID uHash = Asura_GetHashID( szFilename );

	bool bAlreadyExisted = false;

	Asura_Direct3D_Skin* const pxSkin = CreateAndRead( uHash, xStream, bAlreadyExisted );

#if !defined ASURA_DONT_ASSERT_ON_DUPLICATE_RESOURCE_DATA
	Asura_Assertf( !bAlreadyExisted, ("Skin '%s' being loaded more than once.", szFilename) );
#endif
	if( bAlreadyExisted ) return NULL;

	Asura_Assert( pxSkin, "Failed to create render skin" );
	if( !pxSkin ) return false;

	return true;
}


////////////////////////////////////////////////////////////////////////////////
//
// Asura_Chunk_ResourceFile_PC_Skin::Process()
//
// Creates the skin instance and reads it from stream
//
////////////////////////////////////////////////////////////////////////////////

Asura_Direct3D_Skin* Asura_Chunk_ResourceFile_PC_Skin::CreateAndRead( const Asura_Hash_ID uHash, Asura_Chunk_Stream& xStream, bool& bAlreadyExisted )
{
	Asura_Direct3D_Skin* const pxSkin = Asura_Direct3D_SkinRenderer::CreateSkin( uHash, bAlreadyExisted );

	if( bAlreadyExisted ) return NULL;

	Asura_Assert( pxSkin, "Failed to create render skin" );
	if( !pxSkin ) return NULL;

	xStream >> pxSkin->m_uNumStrips;

	u_int uTotalVertices, uTotalIndices, uTotalTriangles;
	xStream >> uTotalVertices >> uTotalIndices >> uTotalTriangles;

	u_int uNumBoneMaps;
	xStream >> uNumBoneMaps;

	xStream >> pxSkin->m_bTriLists;

	// It's valid to have 0 strips. Sometimes empty skins are used for faking things up. We've
	// reached the end of the chunk in this case, so bail out now to avoid asserts about 0 size
	// reads and buffer creations
	if( pxSkin->m_uNumStrips == 0 ) return pxSkin;

	//////////////////////////////////////////////////////////////////////////
	// Read strips

	pxSkin->m_pxStrips = new Asura_Direct3D_SkinStrip[ pxSkin->m_uNumStrips ];

	if( !pxSkin->m_pxStrips )
	{
		Asura_Assertf( false, ("Out of memory while allocating skin") );
		pxSkin->m_uNumStrips = 0;

		return NULL;
	}

	xStream.Read( sizeof( Asura_Direct3D_SkinStrip ) * pxSkin->m_uNumStrips, pxSkin->m_pxStrips );


	//////////////////////////////////////////////////////////////////////////
	// Create DirectX vertex buffer and read vertices into it

	HRESULT hr;
	const u_int uVBSize = uTotalVertices * Asura_Direct3D_SkinRenderer::uVERTEX_SIZE;
	hr = Asura_Direct3D::D3DDevice->CreateVertexBuffer(	uVBSize,
		D3DUSAGE_WRITEONLY,	0, D3DPOOL_MANAGED,	&pxSkin->m_pxVB, 0 );
	Asura_Assertf( SUCCEEDED(hr), ("Failed to create vertex buffer for skin") );
	
	void* pVtxData = NULL;

	if( SUCCEEDED(hr) )
	{
		hr = pxSkin->m_pxVB->Lock( 0, 0, &pVtxData, 0 );
		Asura_Assertf( SUCCEEDED(hr), ("Failed to lock vertex buffer for skin") );
	}

	if( pVtxData )
	{
		xStream.Read( uVBSize, pVtxData );
		pxSkin->m_pxVB->Unlock();
	}
	else
	{
		xStream.Skip( uVBSize );
	}
	pxSkin->m_uNumVertices = uTotalVertices;


	//////////////////////////////////////////////////////////////////////////
	// Create DirectX index buffer and read indices into it

	const u_int uIBSize = sizeof( u_short ) * uTotalIndices;
	hr = Asura_Direct3D::D3DDevice->CreateIndexBuffer( sizeof( u_short ) * uTotalIndices, D3DUSAGE_WRITEONLY, D3DFMT_INDEX16, D3DPOOL_MANAGED, &pxSkin->m_pxIB, NULL);

	void* pIdxData = NULL;

	if( SUCCEEDED(hr) )
	{
		pxSkin->m_pxIB->Lock( 0, 0, &pIdxData, 0 );
		Asura_Assertf( pIdxData, ("Failed to lock index buffer for skin") );
	}

	if( pIdxData )
	{
		xStream.Read( uIBSize, pIdxData );
		pxSkin->m_pxIB->Unlock();
	}
	else
	{
		xStream.Skip( uIBSize );
	}

	//////////////////////////////////////////////////////////////////////////
	// Read bone maps

	pxSkin->SetNumBoneMaps( uNumBoneMaps );

	for(u_int uBoneIndex = 0; uBoneIndex < pxSkin->GetNumBoneMaps(); ++uBoneIndex)
	{
		Asura_Direct3D_Skin::BoneMap &xBoneMap = pxSkin->GetBoneMap(uBoneIndex);
		xStream.Read(sizeof(u_int), &xBoneMap.m_uSize);
		xBoneMap.m_puEntries = new u_int[xBoneMap.m_uSize];
		xStream.Read(sizeof(u_int) * xBoneMap.m_uSize, xBoneMap.m_puEntries);
	}

	return pxSkin;
}

#endif // #if defined(ASURA_TARGET_SM3)

