#include "Mix/Private/Dynamics/StaticMesh.h"

#include "Mix/Private/Dynamics/Utility.h"
#include "Mix/Private/Dynamics/ObjectContext.h"

namespace Mix{ namespace Dynamics{

const wchar_t* StaticMesh::FAILED_CREATE = L"スタティックメッシュの作成に失敗";

StaticMesh* StaticMesh::CreateInstance( void )
{
	return MIX_LIB_NEW_T( Mix::Memory::SECTION_DYNAMICS, StaticMesh );
}

StaticMesh::StaticMesh( void ) :
Mix::Dynamics::Object( Mix::Dynamics::OF_STATIC, Mix::Dynamics::OF_STATIC_MASK, Mix::Dynamics::DD_WIREFRAME ),
m_pImporter( NULL ),
m_pShapeData( NULL ),
m_pShape( NULL ),
m_pMotionState( NULL ),
m_pRigidBody( NULL ),
m_pContext( NULL ),
m_UserIndex( 0 ),
m_pUserPtr( NULL )
{
}

StaticMesh::~StaticMesh( void )
{
	MIX_LIB_DELETE_T( StaticMeshContext, m_pContext );

	MIX_LIB_DELETE( m_pRigidBody );
	MIX_LIB_DELETE( m_pMotionState );

	if( m_pImporter != NULL )
	{
		//m_pImporter側で解放しています
		m_pShape = NULL;
		m_pShapeData = NULL;

		m_pImporter->deleteAllData();
		MIX_LIB_DELETE( m_pImporter );
	}
	else
	{
		MIX_LIB_DELETE( m_pShape );
		MIX_LIB_DELETE( m_pShapeData );
	}
}

Boolean StaticMesh::Initialize( const Mix::Dynamics::MESH* pMeshes, UInt32 meshNum, UInt32 materialNum, const wchar_t* pDebugName )
{
	MIX_ASSERT( m_pImporter == NULL );

	//////////////////////////////////////////////////////////////////////////////////////////////////////////////
	// シェイプデータの作成
	//////////////////////////////////////////////////////////////////////////////////////////////////////////////

	btTriangleMesh* pData = MIX_LIB_NEW btTriangleMesh();
	if( pData != NULL )
	{
		for( UInt32 i = 0; i < meshNum; i++ )
		{
			btIndexedMesh mesh;

			mesh.m_numTriangles        = static_cast<Int32>( pMeshes[i].polygonCount );
			mesh.m_triangleIndexBase   = reinterpret_cast<const UInt8*>(pMeshes[i].polygonIndices);
			mesh.m_triangleIndexStride = sizeof( UInt32[3] );
			mesh.m_numVertices         = pMeshes[i].vertexCount;
			mesh.m_vertexBase          = reinterpret_cast<const UInt8*>(pMeshes[i].vertices);
			mesh.m_vertexStride        = sizeof( Mix::Vector3 );
			mesh.m_indexType           = PHY_INTEGER;

			if( ( mesh.m_numTriangles >= 1 ) &&
				( mesh.m_numVertices >= 3 ) )
			{
				pData->addIndexedMesh( mesh );
			}
		}
	}
	else
	{
		MIX_LOG_ERROR( L"%s : %s : DebugName[%s]", StaticMesh::FAILED_CREATE, Mix::STR_OUTOFMEMORY, pDebugName );
		return False;
	}

	//////////////////////////////////////////////////////////////////////////////////////////////////////////////
	// シェイプの作成
	//////////////////////////////////////////////////////////////////////////////////////////////////////////////

	m_pShape = MIX_LIB_NEW btBvhTriangleMeshShape( pData, true, true );
	if( m_pShape != NULL )
	{
		m_pShape->buildOptimizedBvh();

		MIX_ASSERT( m_pShape->getMeshInterface() != NULL );
		m_pShapeData = static_cast<btTriangleIndexVertexArray*>( m_pShape->getMeshInterface() );
	}
	else
	{
		MIX_LOG_ERROR( L"%s : %s : DebugName[%s]", StaticMesh::FAILED_CREATE, Mix::STR_OUTOFMEMORY, pDebugName );
		return False;
	}

	//////////////////////////////////////////////////////////////////////////////////////////////////////////////
	// フィニッシュ
	//////////////////////////////////////////////////////////////////////////////////////////////////////////////

	return FinishInitialize( materialNum, pDebugName );
}

Boolean StaticMesh::Initialize( void* pSrc, Int32 size, UInt32 materialNum, const wchar_t* pDebugName )
{
	MIX_ASSERT( m_pImporter == NULL );

	Int32 numShape;

	//////////////////////////////////////////////////////////////////////////////////////////////////////////////
	// インポーターの作成
	//////////////////////////////////////////////////////////////////////////////////////////////////////////////

	m_pImporter = MIX_LIB_NEW btBulletWorldImporter( NULL );
	if( m_pImporter != NULL )
	{
		if( m_pImporter->loadFileFromMemory( reinterpret_cast<Int8*>( pSrc ), size ) == false )
		{
			MIX_LOG_ERROR( L"%s : btBulletWorldImporter::loadFileFromMemory %s : DebugName[%s]", StaticMesh::FAILED_CREATE, Mix::STR_RETERROR, pDebugName );
			MIX_LIB_DELETE( m_pImporter );
			return False;
		}
	}
	else
	{
		MIX_LOG_ERROR( L"%s : %s : DebugName[%s]", StaticMesh::FAILED_CREATE, Mix::STR_OUTOFMEMORY, pDebugName );
		return False;
	}

	//////////////////////////////////////////////////////////////////////////////////////////////////////////////
	// シェイプの取得
	//////////////////////////////////////////////////////////////////////////////////////////////////////////////

	numShape = m_pImporter->getNumCollisionShapes();
	if( numShape > 0 )
	{
		btCollisionShape* pCollShape = m_pImporter->getCollisionShapeByIndex( 0 );
		if( pCollShape->getShapeType() != TRIANGLE_MESH_SHAPE_PROXYTYPE )
		{
			MIX_LOG_ERROR( L"%s : シェイプは btBvhTriangleMeshShape である必要があります : DebugName[%s]", StaticMesh::FAILED_CREATE, pDebugName );
			return False;
		}

		m_pShape = static_cast<btBvhTriangleMeshShape*>( m_pImporter->getCollisionShapeByIndex( 0 ) );
		m_pShapeData = static_cast<btTriangleIndexVertexArray*>( m_pShape->getMeshInterface() );
	}
	else
	{
		MIX_LOG_ERROR( L"%s : シェイプがありません : DebugName[%s]", StaticMesh::FAILED_CREATE, pDebugName );
		return False;
	}

	//////////////////////////////////////////////////////////////////////////////////////////////////////////////
	// フィニッシュ
	//////////////////////////////////////////////////////////////////////////////////////////////////////////////

	return FinishInitialize( materialNum, pDebugName );
}

Boolean StaticMesh::FinishInitialize( UInt32 materialNum, const wchar_t* pDebugName )
{
	MIX_ASSERT( m_pShapeData != NULL );
	MIX_ASSERT( m_pShape != NULL );
	MIX_ASSERT( m_pMotionState == NULL );
	MIX_ASSERT( m_pRigidBody == NULL );
	MIX_ASSERT( m_pContext == NULL );

	//////////////////////////////////////////////////////////////////////////////////////////////////////////////
	// モーションステートの作成
	//////////////////////////////////////////////////////////////////////////////////////////////////////////////

	m_pMotionState = MIX_LIB_NEW btDefaultMotionState();
	if( m_pMotionState == NULL )
	{
		MIX_LOG_ERROR( L"%s : %s : DebugName[%s]", StaticMesh::FAILED_CREATE, Mix::STR_OUTOFMEMORY, pDebugName );
		return False;
	}

	//////////////////////////////////////////////////////////////////////////////////////////////////////////////
	// リジッドボディの作成
	//////////////////////////////////////////////////////////////////////////////////////////////////////////////

	m_pRigidBody = MIX_LIB_NEW btRigidBody( 0.0f, m_pMotionState, m_pShape );
	if( m_pRigidBody != NULL )
	{
		Int32 collFlags = m_pRigidBody->getCollisionFlags();

		MIX_SETBIT( collFlags, btCollisionObject::CF_CUSTOM_MATERIAL_CALLBACK );
		MIX_SETBIT( collFlags, btCollisionObject::CF_DISABLE_SPU_COLLISION_PROCESSING ); //並列処理にした場合、正しく衝突判定が行われないため、SPU による衝突判定を無効にする
		m_pRigidBody->setCollisionFlags( collFlags );
	}
	else
	{
		MIX_LOG_ERROR( L"%s : %s : DebugName[%s]", StaticMesh::FAILED_CREATE, Mix::STR_OUTOFMEMORY, pDebugName );
		return False;
	}

	//////////////////////////////////////////////////////////////////////////////////////////////////////////////
	// コンテキストの作成
	//////////////////////////////////////////////////////////////////////////////////////////////////////////////

	m_pContext = MIX_LIB_NEW_T( Mix::Memory::SECTION_DYNAMICS, Mix::Dynamics::StaticMeshContext, this, pDebugName );
	if( m_pContext != NULL )
	{
		m_pRigidBody->setUserPointer( m_pContext );
	}
	else
	{
		MIX_LOG_ERROR( L"%s : %s : DebugName[%s]", StaticMesh::FAILED_CREATE, Mix::STR_OUTOFMEMORY, pDebugName );
		return False;
	}

	////////////////////////////////////////////////////////////////////////////////////////////////////
	// コンテキストの初期化 : ポリゴンマテリアルインデックス配列を作成
	////////////////////////////////////////////////////////////////////////////////////////////////////

	UInt32 partCount = m_pShapeData->getNumSubParts() - 1;

	if( partCount == 0 )
	{
		MIX_LOG_ERROR( L"%s : メッシュがありません : DebugName[%s]", StaticMesh::FAILED_CREATE, pDebugName );
		return False;
	}

	const IndexedMeshArray& meshes = m_pShapeData->getIndexedMeshArray();
	MIX_ASSERT( meshes.size() > 1 );

	m_pContext->InitializeParts( partCount );

	for( UInt32 i = 0; i < partCount; i++ )
	{
		UInt32 meshIndex = i + 1;
		UInt32 polygonCount = static_cast<UInt32>( meshes[meshIndex].m_numTriangles );

		m_pContext->InitializePolygonMaterialIndices( i, polygonCount );
	}

	////////////////////////////////////////////////////////////////////////////////////////////////////
	// コンテキストの初期化 : マテリアル配列を作成
	////////////////////////////////////////////////////////////////////////////////////////////////////

	m_pContext->InitializeMaterials( materialNum );

	////////////////////////////////////////////////////////////////////////////////////////////////////
	// コンテキストの初期化 : フィニッシュ
	////////////////////////////////////////////////////////////////////////////////////////////////////

	m_pContext->InitializeFinish();

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

	return True;
}

////////////////////////////////////////////////////////////////////////////////////////////////////
// Mix::Dynamics::Object
////////////////////////////////////////////////////////////////////////////////////////////////////

Mix::Dynamics::ObjectContext* StaticMesh::GetContextPtr( void ) const
{
	return m_pContext;
}

btCollisionObject* StaticMesh::Bullet_GetCollisionObjectPtr( void ) const
{
	return m_pRigidBody;
}

btRigidBody* StaticMesh::Bullet_GetRigidBodyPtr( void ) const
{
	return m_pRigidBody;
}

////////////////////////////////////////////////////////////////////////////////////////////////////
// Mix::Dynamics::IStaticMesh
////////////////////////////////////////////////////////////////////////////////////////////////////

UInt32 StaticMesh::GetPartCount( void ) const
{
	return m_pContext->GetPartCount();
}

UInt32 StaticMesh::GetPolygonCount( UInt32 partIndex ) const
{
	return m_pContext->GetPolygonCount( partIndex );
}

UInt32 StaticMesh::GetTotalPolygonCount( void ) const
{
	return m_pContext->GetTotalPolygonCount();
}

UInt32 StaticMesh::GetMaterialCount( void ) const
{
	return m_pContext->GetLocalMaterialCount();
}

UInt32 StaticMesh::GetMaterialIndex( UInt32 partIndex, UInt32 polygonIndex ) const
{
	return m_pContext->GetMaterialIndex( partIndex, polygonIndex );
}

Boolean StaticMesh::SetMaterialIndex( UInt32 partIndex, UInt32 polygonIndex, UInt32 materialIndex )
{
	return m_pContext->SetMaterialIndex( partIndex, polygonIndex, materialIndex );
}

UInt32 StaticMesh::LockMaterialIndices( UInt32 partIndex, UInt32 polygonStartIndex, UInt32 polygonCount, UInt32** ppMaterialIndices ) const
{
	return m_pContext->LockMaterialIndices( partIndex, polygonStartIndex, polygonCount, ppMaterialIndices );
}

const Mix::Dynamics::MATERIAL& StaticMesh::GetMaterial( UInt32 index ) const
{
	return m_pContext->GetLocalMaterial( index );
}

Boolean StaticMesh::GetMaterial( UInt32 index, Mix::Dynamics::MATERIAL& material ) const
{
	return m_pContext->GetLocalMaterial( index, material );
}

const Mix::Dynamics::MATERIAL& StaticMesh::GetMaterial( UInt32 partIndex, UInt32 polygonIndex ) const
{
	UInt32 materialIndex = m_pContext->GetMaterialIndex( partIndex, polygonIndex );
	if( materialIndex == 0xFFFFFFFF )
	{
		return Mix::Dynamics::DEF_MATERIAL;
	}

	return m_pContext->GetLocalMaterial( materialIndex );
}

Boolean StaticMesh::GetMaterial( UInt32 partIndex, UInt32 polygonIndex, Mix::Dynamics::MATERIAL& material ) const
{
	UInt32 materialIndex = m_pContext->GetMaterialIndex( partIndex, polygonIndex );
	if( materialIndex != 0xFFFFFFFF )
	{
		material = m_pContext->GetLocalMaterial( materialIndex );
	}
	else
	{
		return False;
	}

	return True;
}

Boolean StaticMesh::SetMaterial( UInt32 index, const Mix::Dynamics::MATERIAL& material )
{
	return m_pContext->SetLocalMaterial( index, material );
}

UInt32 StaticMesh::LockMaterials( UInt32 startIndex, UInt32 count, Mix::Dynamics::MATERIAL** ppMaterials ) const
{
	return m_pContext->LockLocalMaterials( startIndex, count, ppMaterials );
}

////////////////////////////////////////////////////////////////////////////////////////////////////
// Mix::Dynamics::IObject
////////////////////////////////////////////////////////////////////////////////////////////////////

Mix::Dynamics::IObject::TYPE StaticMesh::GetType( void ) const
{
	return Mix::Dynamics::IObject::STATIC_MESH;
}

Boolean StaticMesh::GetShape( Mix::Dynamics::IShape** ppShape )
{
	return False;
}

Float32 StaticMesh::GetShapeMargin( void ) const
{
	return m_pShape->getMargin();
}

void StaticMesh::SetShapeMargin( Float32 margin )
{
	m_pShape->setMargin( margin );
}

UInt16 StaticMesh::GetFilterGroup( void ) const
{
	return Object::Bullet_GetFilterGroup();
}

void StaticMesh::SetFilterGroup( UInt16 filterGroup )
{
	Object::Bullet_SetFilterGroup( filterGroup );
}

UInt16 StaticMesh::GetFilterMask( void ) const
{
	return Object::Bullet_GetFilterMask();
}

void StaticMesh::SetFilterMask( UInt16 filterMask )
{
	Object::Bullet_SetFilterMask( filterMask );
}

const Mix::Dynamics::MATERIAL& StaticMesh::GetMaterial( void ) const
{
	return m_pContext->GetMaterial();
}

void StaticMesh::SetMaterial( const Mix::Dynamics::MATERIAL& material )
{
	m_pContext->SetMaterial( material );
}

Mix::Quaternion StaticMesh::GetWorldRotation( void ) const
{
	const btTransform& tr = m_pRigidBody->getWorldTransform();
	btQuaternion rot = tr.getRotation();

	return Mix::Quaternion( rot.x(), rot.y(), rot.z(), rot.w() );
}

Mix::Vector3 StaticMesh::GetWorldPosition( void ) const
{
	const btTransform& tr = m_pRigidBody->getWorldTransform();
	const btVector3& pos = tr.getOrigin();

	return Mix::Vector3( pos.x(), pos.y(), pos.z() );
}

Mix::Matrix4x4 StaticMesh::GetWorldMatrix( void ) const
{
	const btTransform& tr = m_pRigidBody->getWorldTransform();
	btQuaternion rot = tr.getRotation();
	const btVector3& pos = tr.getOrigin();

	Mix::Matrix4x4 mat( Mix::Quaternion( rot.x(), rot.y(), rot.z(), rot.w() ) );

	mat.m30 = pos.x();
	mat.m31 = pos.y();
	mat.m32 = pos.z();
	mat.m33 = 1.0f;

	return mat;
}

void StaticMesh::SetWorldRotation( const Mix::Quaternion& rotation )
{
	btTransform tr = m_pRigidBody->getWorldTransform();

	tr.setRotation( btQuaternion( rotation.x, rotation.y, rotation.z, rotation.w ) );

	m_pRigidBody->setWorldTransform( tr );
}

void StaticMesh::SetWorldPosition( const Mix::Vector3& pos )
{
	btTransform tr = m_pRigidBody->getWorldTransform();

	tr.setOrigin( btVector3( pos.x, pos.y, pos.z ) );

	m_pRigidBody->setWorldTransform( tr );
}

void StaticMesh::SetWorldTransform( const Mix::Quaternion& rot, const Mix::Vector3& pos )
{
	btTransform tr;

	tr.setRotation( btQuaternion( rot.x, rot.y, rot.z, rot.w ) );
	tr.setOrigin( btVector3( pos.x, pos.y, pos.z ) );

	m_pRigidBody->setWorldTransform( tr );
}

Boolean StaticMesh::IsInWorld( void ) const
{
	return Object::IsInWorld();
}

Mix::Geometry::AABB StaticMesh::GetBounds( void ) const
{
	btVector3 aabbMin;
	btVector3 aabbMax;

	m_pRigidBody->getAabb( aabbMin, aabbMax );

	return Mix::Geometry::AABB( ToMixVector3( aabbMin ), ToMixVector3( aabbMax ) );
}

Boolean StaticMesh::AddListener( Mix::Dynamics::IObjectListener* pListener )
{
	return m_pContext->AddListener( pListener );
}

void StaticMesh::RemoveListener( Mix::Dynamics::IObjectListener* pListener )
{
	m_pContext->RemoveListener( pListener );
}

Int32 StaticMesh::GetUserIndex( void ) const
{
	return m_UserIndex;
}

void StaticMesh::SetUserIndex( Int32 index )
{
	m_UserIndex = index;
}

void* StaticMesh::GetUserPtr( void ) const
{
	return m_pUserPtr;
}

void StaticMesh::SetUserPtr( void* pData )
{
	m_pUserPtr = pData;
}

UInt32 StaticMesh::Debug_GetDrawFlags( void ) const
{
	return m_DebugDrawFlags;
}

void StaticMesh::Debug_SetDrawFlags( UInt32 flags )
{
	m_DebugDrawFlags = flags;
}

Float32 StaticMesh::Debug_GetDrawAxisScaling( void ) const
{
	return m_DebugDrawAxisScaling;
}

void StaticMesh::Debug_SetDrawAxisScaling( Float32 scaling )
{
	m_DebugDrawAxisScaling = max( 0.0f, scaling );
}

void StaticMesh::Debug_Draw( Mix::Graphics::Utility::IPerspectiveRenderer* pPerspectiveRenderer, Float32 opacity )
{
}

}}
