#include "Mix/Private/Scene/Common/TerrainLoader.h"

#include "Mix/Private/IO/MemoryReader.h"
#include "Mix/Memory/IBuffer.h"
#include "Mix/Graphics/IManager.h"
#include "Mix/Graphics/IDevice.h"
#include "Mix/Graphics/IVertexBuffer.h"
#include "Mix/Graphics/IIndexBuffer.h"
#include "Mix/Dynamics/IManager.h"
#include "Mix/Dynamics/IStaticMesh.h"
#include "Mix/Scene/IMaterial.h"

#include "Mix/Private/Scene/Common/Manager.h"
#include "Mix/Private/Scene/Common/TerrainMesh.h"
#include "Mix/Private/Scene/Common/TerrainNode.h"
#include "Mix/Private/Scene/Common/TerrainModel.h"

namespace Mix{ namespace Scene{ namespace Common{

const wchar_t* TerrainLoader::FAILED_CREATE = L"地形モデルの作成に失敗";

TerrainLoader::TerrainLoader( void ) :
m_pDynamicsManager( NULL ),
m_pGraphicsDevice( NULL )
{
	Mix::Dynamics::IManager* pDynamicsMgr = Mix::Dynamics::GetManagerPtr();

	//ダイナミクスが無効にされていても使用できるようにするためのアサートはださない
	MIX_ADD_REF( pDynamicsMgr );
	m_pDynamicsManager = pDynamicsMgr;

	////////////////////////////////////////////////////////////////////////////////////////////////////

	Mix::Graphics::IManager* pGraphicsMgr = Mix::Graphics::GetManagerPtr();

	MIX_ASSERT( pGraphicsMgr != NULL );

	if( pGraphicsMgr->GetDevice( &m_pGraphicsDevice ) == False )
	{
		MIX_ASSERT( m_pGraphicsDevice != NULL );
	}
}

TerrainLoader::~TerrainLoader( void )
{
	MIX_RELEASE( m_pGraphicsDevice );
	MIX_RELEASE( m_pDynamicsManager );
}

Mix::Scene::ITerrainModel* TerrainLoader::Create( Mix::Scene::Common::EffectPackage* pEffectPackage, Mix::IO::IReader* pReader )
{
	MIX_ASSERT( pEffectPackage != NULL );
	MIX_ASSERT( pReader != NULL );

	const wchar_t* pFilePath = pReader->GetFilePath();
	Mix::Scene::Common::TerrainModel* pModel = NULL;

	////////////////////////////////////////////////////////////////////////////////////////////////////
	// インターフェースを作成
	////////////////////////////////////////////////////////////////////////////////////////////////////

	pModel = TerrainModel::CreateInstance( pFilePath );
	if( pModel == NULL )
	{
		MIX_LOG_INFO( L"%s : %s : FilePath[%s]", FAILED_CREATE, Mix::STR_OUTOFMEMORY, pFilePath );
		return NULL;
	}

	////////////////////////////////////////////////////////////////////////////////////////////////////
	// 読み込み
	////////////////////////////////////////////////////////////////////////////////////////////////////

	if( Create( pEffectPackage, pReader, pModel ) == False )
	{
		MIX_RELEASE( pModel );
	}

	////////////////////////////////////////////////////////////////////////////////////////////////////
	// 後処理
	////////////////////////////////////////////////////////////////////////////////////////////////////

	if( m_MeshList.size() > 0 )
	{
		for( TerrainLoader::MeshList::iterator it = m_MeshList.begin(); it != m_MeshList.end(); ++it )
		{
			MIX_RELEASE( ( *it ) );
		}

		m_MeshList.clear();
	}

	m_NodeList.clear();

	////////////////////////////////////////////////////////////////////////////////////////////////////

	return pModel;
}

Boolean TerrainLoader::Create(	Mix::Scene::Common::EffectPackage* pEffectPackage,
								Mix::IO::IReader* pReader,
								Mix::Scene::Common::TerrainModel* pModel )
{
	MIX_ASSERT( pEffectPackage != NULL );
	MIX_ASSERT( pReader != NULL );
	MIX_ASSERT( pModel != NULL );

	const wchar_t* pFilePath = pReader->GetFilePath();
	Mix::Scene::Common::Manager* pSceneMgr = Mix::Scene::GetInternalManagerPtr();

	MIX_ASSERT( pSceneMgr != NULL );

	UInt32 i;
	UInt32 nodeByeSize;
	Mix::String strTemp;

	TerrainLoader::MTM_FILE_HEADER_1_0_0_0 fileHeader;
	TerrainLoader::MTM_DATA_HEADER_1_0_0_0 dataHeader;

	bool bExistsCollision = False;

	////////////////////////////////////////////////////////////////////////////////////////////////////
	// ファイルヘッダ
	////////////////////////////////////////////////////////////////////////////////////////////////////

	if( pReader->Read( &fileHeader, sizeof( fileHeader ) ) != sizeof( fileHeader ) )
	{
		MIX_LOG_ERROR( L"%s : %s : FilePath[%s]", FAILED_CREATE, Mix::STR_ILLEGALFORMAT, pFilePath );
		return False;
	}

	if( fileHeader.magicNumber != TerrainLoader::MTM_MAGICNUMBER )
	{
		MIX_LOG_ERROR( L"%s : %s : FilePath[%s]", FAILED_CREATE, Mix::STR_ILLEGALFORMAT, pFilePath );
		return False;
	}

	////////////////////////////////////////////////////////////////////////////////////////////////////
	// データヘッダ
	////////////////////////////////////////////////////////////////////////////////////////////////////

	if( pReader->Read( &dataHeader, sizeof( dataHeader ) ) != sizeof( dataHeader ) )
	{
		MIX_LOG_ERROR( L"%s : %s : FilePath[%s]", FAILED_CREATE, Mix::STR_ILLEGALFORMAT, pFilePath );
		return False;
	}

	if( ( dataHeader.materialNum == 0 ) ||
		( dataHeader.meshNum == 0 ) ||
		( dataHeader.nodeNum == 0 ) )
	{
		MIX_LOG_ERROR( L"%s : %s : FilePath[%s]", FAILED_CREATE, Mix::STR_ILLEGALFORMAT, pFilePath );
		return False;
	}

	if( ( dataHeader.collShapeSize == 0 ) &&
		( dataHeader.collPolygonNum == 0 ) &&
		( dataHeader.collMaterialNum == 0 ) )
	{
		//コリジョンを所持していない
		bExistsCollision = False;
	}
	else if(	( dataHeader.collShapeSize > 0 ) &&
				( dataHeader.collPolygonNum > 0 ) &&
				( dataHeader.collMaterialNum > 0 ) )
	{
		//コリジョンを所持している
		bExistsCollision = True;
	}
	else if(	( dataHeader.collShapeSize == 0 ) ||
				( dataHeader.collPolygonNum == 0 ) ||
				( dataHeader.collMaterialNum == 0 ) )
	{
		//不正なフォーマット
		MIX_LOG_ERROR( L"%s : %s : FilePath[%s]", FAILED_CREATE, Mix::STR_ILLEGALFORMAT, pFilePath );
		return False;
	}

	////////////////////////////////////////////////////////////////////////////////////////////////////
	// マテリアル
	////////////////////////////////////////////////////////////////////////////////////////////////////

	pModel->ReserveMaterialSlots( dataHeader.materialNum );

	for( i = 0; i < dataHeader.materialNum; i++ )
	{
		wchar_t slotName[32];
		Mix::Scene::IMaterial* pMaterial = NULL;

		if( pReader->Read( slotName, sizeof( slotName ) ) != sizeof( slotName ) )
		{
			MIX_LOG_ERROR( L"%s : %s : FilePath[%s]", FAILED_CREATE, Mix::STR_ILLEGALFORMAT, pFilePath );
			return False;
		}

		strTemp.Sprintf( L"%s\\%s", pFilePath, slotName );

		pMaterial = pSceneMgr->CreateMaterialWithoutMagicNumber( pEffectPackage, pReader, strTemp.GetConstPtr() );
		if( pMaterial == NULL )
		{
			return False;
		}

		pModel->AddMaterialSlot( slotName, pMaterial );
	}

	////////////////////////////////////////////////////////////////////////////////////////////////////
	// メッシュ
	////////////////////////////////////////////////////////////////////////////////////////////////////

	m_MeshList.reserve( dataHeader.meshNum );

	for( i = 0; i < dataHeader.meshNum; i++ )
	{
		Mix::Scene::Common::TerrainMesh* pMesh = NULL;

		strTemp.Sprintf( L"%s\\Mesh%d", pFilePath, i );

		pMesh = Mix::Scene::Common::TerrainMesh::CreateInstance( m_pGraphicsDevice, pReader, strTemp.GetConstPtr() );
		if( pMesh == NULL )
		{
			MIX_LOG_ERROR( L"%s : メッシュを作成できませんでした : FilePath[%s]", FAILED_CREATE, pFilePath );
			return False;
		}

		m_MeshList.push_back( pMesh );
	}

	////////////////////////////////////////////////////////////////////////////////////////////////////
	// ノード
	////////////////////////////////////////////////////////////////////////////////////////////////////

	Mix::Scene::Common::TerrainNode** nodeList;

	m_NodeList.clear();
	m_NodeList.resize( dataHeader.nodeNum );

	nodeByeSize = sizeof( TerrainLoader::MTM_NODE_1_0_0_0 ) * dataHeader.nodeNum;

	if( pReader->Read( &( m_NodeList[0] ), nodeByeSize ) != nodeByeSize )
	{
		MIX_LOG_ERROR( L"%s : %s : FilePath[%s]", FAILED_CREATE, Mix::STR_ILLEGALFORMAT, pFilePath );
		return False;
	}

	nodeList = pModel->AllocateNodeList( dataHeader.nodeNum );
	if( nodeList == NULL )
	{
		MIX_LOG_ERROR( L"%s : %s : FilePath[%s]", FAILED_CREATE, Mix::STR_OUTOFMEMORY, pFilePath );
		return False;
	}

	for( i = 0; i < dataHeader.nodeNum; i++ )
	{
		const TerrainLoader::MTM_NODE_1_0_0_0& srcNode = m_NodeList[i];
		Mix::Scene::Common::TerrainNode* pDstNode = nodeList[i];

		pDstNode->SetBounds( srcNode.aabb );
		pDstNode->SetMesh( ( srcNode.mesh >= 0 )? m_MeshList[srcNode.mesh] : NULL );
		pDstNode->SetParent( srcNode.parent, ( srcNode.parent >= 0 )? nodeList[srcNode.parent] : NULL );
		pDstNode->ReserveChildList( srcNode.childCount );
	}

	////////////////////////////////////////////////////////////////////////////////////////////////////
	// コリジョン
	////////////////////////////////////////////////////////////////////////////////////////////////////

	if( ( m_pDynamicsManager != NULL ) && ( bExistsCollision == True ) )
	{
		/*
			ダイナミクス : スタティックメッシュ : 作成
		*/

		Mix::Memory::IBuffer* pBuffer = NULL;
		Mix::Dynamics::IStaticMesh* pStaticMesh = NULL;
		UInt32* polygonMaterialIndices = NULL;
		Mix::Dynamics::MATERIAL* materials = 0;
			
		if( Mix::Memory::CreateBuffer( Mix::Memory::SECTION_SCENE, dataHeader.collShapeSize, NULL, &pBuffer ) == False )
		{
			MIX_LOG_ERROR( L"%s : %s : FilePath[%s]", FAILED_CREATE, Mix::STR_OUTOFMEMORY, pFilePath );
			return False;
		}

		if( pReader->Read( pBuffer->GetPointer(), MIX_UIT_TO_UI32( pBuffer->GetSize() ) ) != dataHeader.collShapeSize )
		{
			MIX_RELEASE( pBuffer );
			return False;
		}

		if( m_pDynamicsManager->CreateStaticMesh( pBuffer, dataHeader.collMaterialNum, &pStaticMesh, pFilePath ) == False )
		{
			MIX_RELEASE( pBuffer );
			return False;
		}

		MIX_RELEASE( pBuffer );

		/*
			ダイナミクス : スタティックメッシュ : ポリゴンのマテリアル参照リスト
		*/

		if( pStaticMesh->LockMaterialIndices( 0, 0, dataHeader.collPolygonNum, &polygonMaterialIndices ) == dataHeader.collPolygonNum )
		{
			UInt32 readSize = sizeof( UInt32 ) * dataHeader.collPolygonNum;

			if( pReader->Read( polygonMaterialIndices, readSize ) != readSize )
			{
				MIX_LOG_ERROR( L"%s : %s : FilePath[%s]", FAILED_CREATE, Mix::STR_ILLEGALFORMAT, pFilePath );
				MIX_RELEASE( pStaticMesh );
				return False;
			}
		}
		else
		{
			MIX_LOG_ERROR( L"%s : %s : FilePath[%s]", FAILED_CREATE, Mix::STR_ILLEGALFORMAT, pFilePath );
			MIX_RELEASE( pStaticMesh );
			return False;
		}

		/*
			ダイナミクス : スタティックメッシュ : マテリアルリスト
		*/

		if( pStaticMesh->LockMaterials( 0, dataHeader.collMaterialNum, &materials ) == dataHeader.collMaterialNum )
		{
			UInt32 readSize = sizeof( Mix::Dynamics::MATERIAL ) * dataHeader.collMaterialNum;

			if( pReader->Read( materials, readSize ) != readSize )
			{
				MIX_LOG_ERROR( L"%s : %s : FilePath[%s]", FAILED_CREATE, Mix::STR_ILLEGALFORMAT, pFilePath );
				MIX_RELEASE( pStaticMesh );
				return False;
			}
		}
		else
		{
			MIX_RELEASE( pStaticMesh );
			return False;
		}

		/*
			シーン : コライダーの作成
		*/

		if( pModel->CreateCollider( pStaticMesh ) == False )
		{
			MIX_LOG_ERROR( L"%s : %s : FilePath[%s]", FAILED_CREATE, Mix::STR_OUTOFMEMORY, pFilePath );
			MIX_RELEASE( pStaticMesh );
			return False;
		}

		/*
			後処理
		*/

		MIX_RELEASE( pStaticMesh );
	}

	////////////////////////////////////////////////////////////////////////////////////////////////////

	return True;
}

}}}
