#include "StdAfx.h"
#include "ModelInstance.h"
#include "ResourceManager.h"
#include "KFMTool.h"

cModelInstance::cModelInstance( NiKFMTool* kfmTool )
{
	mpKFMTool = kfmTool;
}

cModelInstance::~cModelInstance()
{
	mSequenceMap.RemoveAll();
	mpKFMTool = 0;
}

cModelInstance* cModelInstance::Create( const char* pathName )
{
	assert( pathName );

	/// KFM 툴을 생성
	cKFMTool* kfmTool = NiNew cKFMTool;
	if( kfmTool->LoadFile( pathName ) != NiKFMTool::KFM_SUCCESS )
	{
		assert( 0 );
		return 0;
	}

	/// 모델 인스턴스를 생성
	cModelInstance* modelInstance = NiNew cModelInstance( kfmTool );

	/// Stream을 생성하고 모델 인스턴스 초기화
	NiStream* stream = NiNew NiStream;
	if ( modelInstance->Init( *stream ) == false )
	{
		delete modelInstance;
		delete stream;
		return NULL;
	}
	delete stream;

	return modelInstance;
}

bool cModelInstance::Init( NiStream& stream )
{
	/// Load NIF file
	const char* modelPath = mpKFMTool->GetFullModelPath();
	cString tempName = modelPath;
	tempName.TrimLeft( "./\\" );

	///
	if( RESOURCEMAN->LoadNIF( tempName ) == false )
	{
		return false;
	}

	///
	NiNode* pNode = RESOURCEMAN->GetObjectByName( tempName );
	if( pNode )
		CreatePickingData( pNode );

	/// Load Sequences Info
	unsigned int* pSequenceIDs;
	unsigned int numIDs;
	mpKFMTool->GetSequenceIDs( pSequenceIDs, numIDs );

	unsigned int	eSequenceID = 0;
	char			lastFileNameKF[NI_MAX_PATH];
	bool			isLoadFileKF = false;

	lastFileNameKF[0] = '\0';
	for( unsigned int i = 0; i < numIDs; ++i )
	{
		eSequenceID = pSequenceIDs[i];

		/// Get KF filename.
		NiFixedString fileNameKF = mpKFMTool->GetFullKFFilename( eSequenceID );

		/// 구동상의 문제는 아니지만, 전체 데이터에 영향을 준다. 별도로 체크!!
		assert(fileNameKF.Exists());

		/// Determine whether or not to load file.
		isLoadFileKF = false;
		if( ::strcmp(lastFileNameKF, fileNameKF) != 0 )
		{
			isLoadFileKF = true;
			NiStrcpy( lastFileNameKF, NI_MAX_PATH, fileNameKF );
		}

		AddSequence( eSequenceID, stream, isLoadFileKF );
	}

	NiFree(pSequenceIDs);

	return true;
}

bool cModelInstance::AddSequence( unsigned int eSequenceID, NiStream& stream, bool isLoadFileKF )
{
	/// Get sequence information.
	NiKFMTool::Sequence* pKfmSequence = mpKFMTool->GetSequence( eSequenceID );
	if( !pKfmSequence )
	{
		return false;
	}

	/// Get KF filename.
	NiFixedString fileNameKF = mpKFMTool->GetFullKFFilename(eSequenceID);
	assert(fileNameKF.Exists());

	/// Load file, if specified.
	if( isLoadFileKF )
	{
		cFileLoader loader;
		if( loader.Open( (const char*)fileNameKF, true ) == false )
		{
			return false;
		}

		bool isSuccess = stream.Load( (char*)loader.GetBufferPtr(), loader.GetSize() );
		if ( isSuccess == false )
		{
			NILOG( NIMESSAGE_GENERAL_0, "cActorManagerForPartition: Failed to load KF File: %s\n", (const char*)fileNameKF );
			return false;
		}
	}

	/// Setting Sequence (즉, 애니정보 셋팅)
	int animIdx = pKfmSequence->GetAnimIndex();
	NiControllerSequencePtr pSequence = NiControllerSequence::CreateSequenceFromFile( stream, animIdx );
	if ( pSequence == NULL )
	{
		NILOG( NIMESSAGE_GENERAL_0, "cActorManagerForPartition: Failed to add sequence at index %d in %s\n", animIdx, fileNameKF );
		return false;
	}

	mSequenceMap.SetAt( eSequenceID, pSequence );

	return true;
}

/// 주의사항 :
NiControllerSequence* cModelInstance::CloneSequence( unsigned int eSequenceID )
{
	NiControllerSequencePtr sequence;
	mSequenceMap.GetAt( eSequenceID, sequence );

	if( sequence == NULL )
	{
		assert(0);
		return NULL;
	}

	NiCloningProcess cloning;
	cloning.m_eCopyType = NiObjectNET::COPY_EXACT;

	return NiDynamicCast( NiControllerSequence, sequence->CreateClone(cloning) );
}

void cModelInstance::CreatePickingData( NiAVObject* pobj )
{
	NiBoundingVolume* pABV = 0;
	NiCollisionData* pData = NiGetCollisionData(pobj);
	if( pData )
	{
		pABV = pData->GetModelSpaceABV();
	}

	NiNode* pNode = NiDynamicCast( NiNode, pobj );
	if( pABV )
	{
		if( pNode )
		{
			/// 픽킹용 객체 생성..
			NiNode* pRoot = NiNew NiNode;
			assert(pRoot);
			NiCollisionData* pCollisionData = NiNew NiCollisionData(pRoot);
			assert(pCollisionData);
			pCollisionData->SetCollisionMode(NiCollisionData::NOTEST);
			pCollisionData->SetPropagationMode(NiCollisionData::PROPAGATE_NEVER);

			NiTriShape* pShape = 0;
			switch( pABV->Type() )
			{
			case NiBoundingVolume::BOX_BV:
				pShape = NiDrawableBV::CreateFromBox( ((NiBoxBV*)pABV)->GetBox(), 1.0f );
				pRoot->AttachChild( pShape );
				break;
			case NiBoundingVolume::CAPSULE_BV:
				pShape = NiDrawableBV::CreateFromCapsule( ((NiCapsuleBV*)pABV)->GetCapsule(), 1.0f );
				pRoot->AttachChild( pShape );
				break;
			case NiBoundingVolume::SPHERE_BV:
				pShape = NiDrawableBV::CreateFromSphere( ((NiSphereBV*)pABV)->GetSphere(), 1.0f );
				pRoot->AttachChild( pShape );
				break;
			default:  // New BV type?
				NiDelete pRoot;
				return;
			}

			if(pRoot)
			{
				pRoot->SetName( "PickObj" );
				pNode->AttachChild( pRoot );
			}
		}
	}

	/// 자식에 대한 검사..
	if( pNode )
	{
		NiAVObject* pChild = 0;
		for( unsigned int i = 0; i < pNode->GetArrayCount(); i++ )
		{
			pChild = pNode->GetAt(i);
			if( pChild )
				CreatePickingData( pChild );
		}
	}
}