#include "stdafx.h"

#include "ResourceManager.h"
#include "RenderSystem.h"

#include "Stream.h"


cResourceManager* cResourceManager::mSingleton = 0;

cResourceManager::cResourceManager()
: mNiTextureMap( 4096 )
, mNiNodeArray( 2048, 1024 )
, mNiNodeIndexMap( 2048 )
//, mModelIndexMap
{
	assert( mSingleton == 0 && "bad singleton!" );
	mSingleton = this;

	/// 텍스처 팔레트를 생성
	mNiTexturePalette = NiNew cTexturePalette(8192);

	/// 그림자 텍스처를 로딩
#ifdef MAP_EDITOR
	mShadowTexture = LoadNiTexture( "MapData/shadow.tga", false );
#else
	mShadowTexture = LoadNiTexture( "Data/2DData/shadow.tga", false );
#endif
	assert( mShadowTexture );
}

cResourceManager::~cResourceManager()
{
	mSingleton = 0;
}

void cResourceManager::Clear()
{
	mModelArray.RemoveAll();
	mModelIndexMap.RemoveAll();
	RemoveAllNiNodes();
}

bool cResourceManager::LoadNIF( const cString& pathName )
{
	cString tempName = pathName;
	tempName.TrimLeft( "./\\" );

	cString name;
	if( ::GetFileName( &name, pathName ) == false )
		name = tempName;

	/// 이미 로딩되었는지 검사
	NiNode* n = GetNiNode( name );
	if( n )
	{
		return true;
	}

	///
	cFileLoader loader;
	if( loader.Open( tempName.Cstr(), true ) == false )
	{
		return false;
	}

	///
	cStream stream( tempName.Cstr(), mNiTexturePalette );
	mNiTexturePalette->SetStream( &stream );

	if( stream.Load( (char*)loader.GetBufferPtr(), loader.GetSize() ) == false )
	{
		return false;
	}

	n = NiDynamicCast( NiNode, stream.GetObjectAt( 0 ) );
	if( n == 0 )
		assert(0);

	cString fileName;
	::GetFileName( &fileName, pathName );

	n->SetName( fileName.Cstr() );
	AddNiNode( n );

	stream.RemoveAllObjects();
	mNiTexturePalette->SetStream( 0 );

	return true;
}

bool cResourceManager::LoadKFM( const cString& pathName )
{
	cString name;
	if( ::GetFileName( &name, pathName ) == false )
		name = pathName;

	cModelInstance* model = cModelInstance::Create( pathName.Cstr() );

	if( model == 0 )
		return false;

	AddModel( model, name );
	return true;
}

NiTexture* cResourceManager::LoadNiTexture( const cString& pathName, bool useMipMap )
{
	cString name;
	if( ::GetFileName( &name, pathName ) == false )
		name = pathName;

	/// 이미 존재하는지 검사
	cNiTextureMap::cIterator pos = mNiTextureMap.Find( name );

	if( pos != mNiTextureMap.End() )
	{
		return pos->mSecond;
	}

	/// 밉맵 설정
	NiTexture::FormatPrefs fmt;
	if( useMipMap == false )
	{
		fmt.m_eMipMapped = NiTexture::FormatPrefs::NO;
	}

	/// 텍스처 로딩
	NiSourceTexture* tex = NiSourceTexture::Create( pathName.Cstr(), fmt );

	if( tex == 0 )
	{
		assert( 0 && "failed to load texture" );
		return 0;
	}

	/// 텍스처 추가
	tex->SetName( name.Cstr() );
	mNiTextureMap.Insert( name.Cstr(), tex );
	return tex;
}

void cResourceManager::RemoveTexture( NiTexture* tex )
{
	assert( tex );

	mNiTextureMap.Erase( (const char*)tex->GetName() );
}

LPDIRECT3DTEXTURE9 cResourceManager::LoadD3DTexture( const cString& pathName )
{
	/// 텍스처 로딩
	cFileLoader loader;
	if( loader.Open( pathName, true ) == false )
	{
		assert( 0 );
		return 0;
	}

	LPDIRECT3DTEXTURE9 tex = 0;
	cRenderer* renderer = RENDERSYS->GetRenderer();
	HRESULT ret = ::D3DXCreateTextureFromFileInMemory( renderer->GetD3DDevice(), loader.GetBufferPtr(), loader.GetSize(), &tex );

	if( FAILED(ret) || tex == 0 )
	{
		assert( 0 );
		return 0;
	}
	return tex;
}

unsigned int cResourceManager::AddNiNode( NiNode* node )
{
	assert( node );

	/// 엔진 노드를 추가
	unsigned int i = mNiNodeArray.Add( node );
	mNiNodeIndexMap.Insert( (const char*)node->GetName(), i );
	return i;
}

bool cResourceManager::RemoveNiNode( const char* name )
{
	assert( name );

	/// 엔진 노드를 이름으로 검색
	cNiNodeIndexMap::cIterator pos = mNiNodeIndexMap.Find( name );

	if( pos == mNiNodeIndexMap.End() )
	{
		assert( 0 && "can't find node by name" );
		return false;
	}

	/// 엔진 노드를 인덱스로 얻음
	unsigned int i = (*pos).mSecond;
	NiNode* n = mNiNodeArray.GetAt( i );

	if( n == 0 )
	{
		assert( 0 && "can't find node by index" );
		return false;
	}

	/// 참조 카운트 검사
	assert( n->GetRefCount() == 1 && "bad reference counting!" );

	/// 엔진 노드를 제거
	unsigned int s = mNiNodeArray.GetSize();

	if( i >= s )
	{
		assert( 0 && "index out of range" );
		return false;
	}
	if( s > 1 && i != s - 1 )
	{
		mNiNodeArray.SetAt( i, mNiNodeArray.GetAt( s - 1 ) );
	}
	mNiNodeArray.RemoveEnd();

	n = mNiNodeArray.GetAt( i );
	mNiNodeIndexMap[(const char*)(n->GetName())] = i;
	mNiNodeIndexMap.Erase( pos );
	return true;
}

bool cResourceManager::RemoveNiNode( NiNode* node )
{
	assert( node );

	return RemoveNiNode( (const char*)node->GetName() );
}

void cResourceManager::RemoveAllNiNodes()
{
#ifdef _DEBUG
	for( unsigned int i = 0, iend = mNiNodeArray.GetSize(); i < iend; ++i )
	{
		NiNode* n = mNiNodeArray.GetAt( i );

		if( n )
		{
			assert( n->GetRefCount() == 1 && "bad reference counting!" );
		}
	}
#endif

	mNiNodeArray.RemoveAll();
	mNiNodeIndexMap.Clear();
}

NiNode* cResourceManager::GetNiNode( unsigned int i ) const
{
	if( i >= mNiNodeArray.GetSize() )
	{
		assert( 0 && "index out of range" );
		return 0;
	}
	else
	{
		return mNiNodeArray.GetAt( i );
	}
}

NiNode* cResourceManager::GetNiNode( const cString& fileName ) const
{
	cString name;
	if( ::GetFileName( &name, fileName ) == false )
		name = fileName;


	/// 엔진 노드 인덱스를 이름으로 검색
	cNiNodeIndexMap::cConstIterator pos = mNiNodeIndexMap.Find( name );

	if( pos == mNiNodeIndexMap.End() )
	{
		return 0;
	}

	return GetNiNode( (*pos).mSecond );
}

NiNode* cResourceManager::CloneNiNode( unsigned int i )
{
	NiNode* n = GetNiNode( i );

	if( n )
	{
		NiCloningProcess cloning;
		cloning.m_eCopyType = NiObjectNET::COPY_EXACT;

		return (NiNode*)n->Clone( cloning );
	}
	return 0;
}

NiNode* cResourceManager::CloneNiNode( const cString& pathName )
{
	cString fileName;
	::GetFileName( &fileName, pathName );

	NiNode* n = GetNiNode( fileName.Cstr() );

	if( n )
	{
		NiCloningProcess cloning;
		cloning.m_eCopyType = NiObjectNET::COPY_EXACT;

		return (NiNode*)n->Clone( cloning );
	}
	return 0;
}

unsigned int cResourceManager::AddModel( cModelInstance* model, const cString& name )
{
	assert(model);
	unsigned int index = mModelArray.AddFirstEmpty(model);
	mModelIndexMap.SetAt( name.Cstr(), index );

	return index;
}

void cResourceManager::RemoveAllModels()
{
#ifdef _DEBUG
	for (unsigned int i = 0; i < mModelArray.GetSize(); i++)
	{
		cModelInstance* obj = mModelArray.GetAt( i );
		if( obj )
		{
			assert(obj->GetRefCount() == 1);
			obj = 0;
		}
	}
#endif

	mModelArray.RemoveAll();
	mModelIndexMap.RemoveAll();
}

cModelInstance* cResourceManager::GetModel( unsigned int i ) const
{
	return mModelArray.GetAt( i );
}

cModelInstance* cResourceManager::GetModel( const cString& name )
{
	cString fileName;
	::GetFileName( &fileName, name );

	unsigned int i;
	if( mModelIndexMap.GetAt( fileName.Cstr(), i ) )
		return GetModel( i );

	if( LoadKFM( name ) )
	{
		if( mModelIndexMap.GetAt( fileName.Cstr(), i ) )
			return GetModel( i );
	}
	return 0;
}
