/*
 BMManager.cpp
 BISHAMON sample manager
 Copyright (C) 2011 Matchlock, Inc.
 All Rights Reserved.
 */

#include "DXUT.h"
#include "BMManager.h"

#include "BMBinary.h"
#include "BMTexture.h"
#include "BMModel.h"
#include "BMEffect.h"
#include "BMAllocator.h"

#define LIST_MAXCOUNT	256

// 追加
BMManager*	BMManager::s_instance = NULL;

/*
	追加：生成＆初期化
*/
void BMManager::Create(LPDIRECT3DDEVICE9 device, const wchar_t* databaseFolder, unsigned int maxVertexBufferSize) {
	s_instance = this;
	this->device = device;
	this->databaseFolder = databaseFolder;
	this->databaseBmsln = databaseFolder + std::wstring(L"bmsln/");
	this->databaseTexture = databaseFolder + std::wstring(L"texture/");
	this->databaseModel = databaseFolder + std::wstring(L"model/");
	listEffect.reserve( LIST_MAXCOUNT );
	// 初期化
	Initialize(maxVertexBufferSize);
}

BMManager::~BMManager() {
	Destroy();
}

void BMManager::SetMaxGeneratingScale(float scale) {
	bm3::System::instance().SetMaxGenerateScale(scale);
	maxGeneratingScale = scale;
}

void BMManager::Initialize(unsigned int maxVertexBufferSize) {

	// check environment
	assert(bm3::CheckEnvironment());

	// initialize system
	bm3::System::initialize();
	bm3::System::instance().Initialize();

	// register memory allocator
	bm3::MemoryManager::instance().RegisterAllocator(allocator);

	// initialize render state manager
	bm3::RenderStateInitializeInfo info(device);
	bm3::RenderStateManager::instance().Initialize(info);

	// register resource hash
	hash = new bm3::StandardHash[bm3::ResourceManager_UseHashCount];
	bm3::ResourceManager::instance().RegisterHash(hash);

	// register vertex manager & double buffering
	vertexManager.Initialize(device, maxVertexBufferSize, 2);
	bm3::VertexManager::instance().RegisterManager(vertexManager);

	// default values
	// view
	ml::vector3d eye(0, 30, -40, 0);
	ml::vector3d at(0, 0, 0, 0);
	ml::vector3d up(0, 1, 0, 0);
	view.lookat_r(eye, at, up);
	// view inverse
	viewInverse = view;
	viewInverse.inverse();
	// projection
	projection.perspectivefov_r(40, 16.f/9.f, 0.1f, 1000);

}

BMBinary *BMManager::LoadBinary(std::wstring name) {

	if (this->device == NULL){
		return NULL;
	}

	if(binaries[name] != NULL) {
		binaries[name]->Retain();
		return binaries[name];
	}
	
	const std::wstring path = GetBmslnFolder() + name + L".bmb";
	BMBinary *binary = new BMBinary;
	binary->Load(path);

	// load textures
	{
		int count = bm3::fileformat::bmb::TextureCount(binary->Bytes());
		for(int i=0; i<count; i++) {
			const wchar_t *name = bm3::fileformat::bmb::TextureNameFromIndex(binary->Bytes(), i);
			BMTexture *texture = RegisterTexture(name, bm3::TextureCategory::Effect);
			if(texture != NULL) {
				bm3::ResourceManager::instance().RegisterTexture(name, texture);
			}
		}
	}

	// load model textures
	{
		int count = bm3::fileformat::bmb::ModelTextureCount(binary->Bytes());
		for(int i=0; i<count; i++) {
			const wchar_t *name = bm3::fileformat::bmb::ModelTextureNameFromIndex(binary->Bytes(), i);
			BMTexture *texture = RegisterTexture(name, bm3::TextureCategory::Model);
			if(texture != NULL) {
				bm3::ResourceManager::instance().RegisterTexture(name, texture);
			}
		}
	}

	// load model
	{
		int count = bm3::fileformat::bmb::ModelCount(binary->Bytes());
		for(int i=0; i<count; i++) {
			const wchar_t *name = bm3::fileformat::bmb::ModelNameFromIndex(binary->Bytes(), i);
			BMModel *model = RegisterModel(name);
			if(model != NULL) {
				bm3::ResourceManager::instance().RegisterModel(name, model);
			}
		}
	}

	// load reference binary
	{
		int count = bm3::fileformat::bmb::BmslnCount(binary->Bytes());
		for(int i=0; i<count; i++) {
			const wchar_t *name = bm3::fileformat::bmb::BmslnNameFromIndex(binary->Bytes(), i);
			const BMBinary *binary = LoadBinary(name);
			if(binary != NULL) {
				bm3::ResourceManager::instance().RegisterBmsln(name, const_cast<BMBinary *>(binary));
			}
		}
	}

	binaries[name] = binary;

	return binary;

}

void BMManager::ReleaseBinary(std::wstring name) {

	BMBinaryMap::iterator itr = binaries.find(name);
	BMBinary *binary = (itr != binaries.end()) ? itr->second : NULL;

	if(binary == NULL) return;

	if(binary->RetainCount() > 1) {
		binary->Release();
		return;
	}

	// release textures
	{
		int count = bm3::fileformat::bmb::TextureCount(binary->Bytes());
		for(int i=0; i<count; i++) {
			const wchar_t *key = bm3::fileformat::bmb::TextureNameFromIndex(binary->Bytes(), i);
			BMTextureMap::iterator itr = textures.find(key);
			BMTexture *texture = (itr != textures.end()) ? itr->second : NULL;
			if(texture) {
				if(texture->RetainCount() == 1) {
					// unregister
					bm3::ResourceManager::instance().UnregisterTexture(key);
					textures.erase(key);
				}
				// retain -1
				texture->Release();
			}
		}
	}

	// release model textures
	{
		int count = bm3::fileformat::bmb::ModelTextureCount(binary->Bytes());
		for(int i=0; i<count; i++) {
			const wchar_t *key = bm3::fileformat::bmb::ModelTextureNameFromIndex(binary->Bytes(), i);
			BMTextureMap::iterator itr = textures.find(key);
			BMTexture *texture = (itr != textures.end())? itr->second : NULL;
			if(texture) {
				if(texture->RetainCount() == 1) {
					// unregister
					bm3::ResourceManager::instance().UnregisterTexture(key);
					textures.erase(key);
				}
				// retain -1
				texture->Release();
			}
		}
	}

	// release model
	{
		int count = bm3::fileformat::bmb::ModelCount(binary->Bytes());
		for(int i=0; i<count; i++) {
			const wchar_t *key = bm3::fileformat::bmb::ModelNameFromIndex(binary->Bytes(), i);
			BMModelMap::iterator itr = models.find(key);
			BMModel *model = (itr != models.end()) ? itr->second : NULL;
			if(model) {
				if(model->RetainCount() == 1) {
					// unregister
					bm3::ResourceManager::instance().UnregisterModel(key);
					models.erase(key);
				}
				// retain -1
				model->Release();
			}
		}
	}

	// release reference binary
	{
		int count = bm3::fileformat::bmb::BmslnCount(binary->Bytes());
		for(int i=0; i<count; i++) {
			const wchar_t *key = bm3::fileformat::bmb::BmslnNameFromIndex(binary->Bytes(), i);
			BMBinaryMap::iterator itr = binaries.find(key);
			BMBinary *binary = (itr != binaries.end()) ? itr->second : NULL;
			if(binary) {
				if(binary->RetainCount() == 1) {
					// unregister
					bm3::ResourceManager::instance().UnregisterBmsln(key);
				}
				ReleaseBinary(key);
			}
		}
	}

	// release
	binary->Release();
	binaries.erase(name);

}

BMEffect *BMManager::CreateEffect(std::wstring name) {

	BMBinary *binary = LoadBinary(name);
	if(binary == NULL) return NULL;

	GetTotalLifeTime(binary);
	GetUserData(binary);

	BMEffect *effect = new BMEffect();
	if(!effect->Create(binary, name)) {
		effect->Release();
		return NULL;
	}

	return effect;

}

BMEffect *BMManager::CreateEffect(const wchar_t *name) {
	if (this->device == NULL){
		return NULL;
	}

	// エフェクトを生成
	BMEffect* effect = CreateEffect(std::wstring(name));
	// 追加：管理リストへ登録（空きがあったらそこへ入れ、なかったらお尻に追加）
	if (effect != NULL){
		BMEffectListType::iterator ite = listEffect.begin();
		BMEffectListType::iterator end = listEffect.end();
		for (; ite != end ; ++ite ){
			if (*ite == NULL) break;
		}
		if (ite != end){
			*ite = effect;
		} else {
			listEffect.push_back(effect);
		}
	}
	return effect;
}

void BMManager::ReleaseEffect(BMEffect *effect) {
	// 追加：管理リストから削除
	if (effect != NULL){
		BMEffectListType::iterator ite = listEffect.begin();
		BMEffectListType::iterator end = listEffect.end();
		for (; ite != end ; ++ite ){
			if (*ite == effect) break;
		}
		if (ite != end){
			// 管理から外して破棄
			ReleaseEffect(ite);
		} else {
			// エフェクトを破棄
			std::wstring name = effect->GetName();
			effect->Release();
			ReleaseBinary(name);
		}
	}
}

/*
	追加：エフェクトの破棄（Iteretor版）
*/
void BMManager::ReleaseEffect(BMEffectListType::iterator& iteEffect) {
	BMEffect* effect = *iteEffect;

	// 管理から外す
	*iteEffect = NULL;

	// エフェクトを破棄
	if (effect != NULL){
		std::wstring name = effect->GetName();
		effect->Release();
		ReleaseBinary(name);
	}
}

void BMManager::DrawEffect(BMEffect *effect) {
	effect->Draw(vertexManager, drawInfo);
}

void BMManager::Begin() {

	// info
	drawInfo = bm3::DrawInfo(viewInverse, view, projection);
	bm3::RendererInfo renderer;
	renderer.d3d_device_ = device;
	drawInfo.SetRenderer(renderer);

	// lighting
	bm3::RenderStateManager::instance().SetLightDirection(ml::vector3d(1, 1, 1, 1));
	bm3::RenderStateManager::instance().SetLightColor(ml::color_rgba<float>(1, 1, 1, 1));
	bm3::RenderStateManager::instance().SetAmbientLightColor(ml::color_rgba<float>(0.2f, 0.2f, 0.2f, 1));

	// reset vertex buffer
	vertexManager.ResetCurrentPointer();

}

void BMManager::Begin2D(float width, float height) {

	// view
	ml::matrix44 v;
	v.set_unit();
	SetView(v);

	// projection
	ml::matrix44 p;
	p.orthographic(0, width, 0, height, -100, 100);
	SetProjection(p);

	// begin
	Begin();

}

void BMManager::End() {
}

void BMManager::DeviceLost() {
	// 頂点バッファを解放する
	vertexManager.Release();
}

void BMManager::ResetDevice(IDirect3DDevice9 *device,unsigned int maxVertexBufferSize)
{
	// 現在のを解放
	DeviceLost();
	// register vertex manager & double buffering
	vertexManager.Initialize(device, maxVertexBufferSize, 2);
	bm3::VertexManager::instance().RegisterManager(vertexManager);
}


BMTexture *BMManager::RegisterTexture(std::wstring name, bm3::TextureCategory::Type type) {

	if(textures[name] != NULL) {
		textures[name]->Retain();
		return NULL;
	}

	const std::wstring path = ((type == bm3::TextureCategory::Model) ? GetModelFolder() : GetTextureFolder()) + name + L".dds";

	BMTexture *texture = new BMTexture();
	textures[name] = texture;
	texture->Load(path, device);

	return texture;

}

BMModel *BMManager::RegisterModel(std::wstring name) {

	if(models[name] != NULL) {
		models[name]->Retain();
		return NULL;
	}

	const std::wstring path = GetModelFolder() + name + L".m3r";

	BMModel *model = new BMModel();
	models[name] = model;
	model->Load(path, device);

	return model;

}

void BMManager::SetView(const ml::matrix44 &view) {
	this->view = view;
	viewInverse = view;
	viewInverse.inverse();
}

void BMManager::SetProjection(const ml::matrix44 &projection) {
	this->projection = projection;
}


void BMManager::GetTotalLifeTime(BMBinary *binary){
	
		// このエフェクトの最大寿命を取得します。
		// ランダムが設定されている場合、その最大の値を返します。
		// 無限寿命の場合は-1を返します。
		int total_lifetime	= bm3::fileformat::bmb::TotalLifeTime(binary->Bytes());

		char buf[256];
		sprintf(buf, "Total_lifetime  : %d\n", total_lifetime);
		OutputDebugStringA(buf);

}

void BMManager::GetUserData(BMBinary *binary){
	
	// bmbファイルのルートノードからユーザーデータを取得します。
	const bm3::UserDataType *user_data = bm3::fileformat::bmb::RootUserData(binary->Bytes());
	char buf[256];

	bm3::UserDataType::UserDataElementArrayI i   = user_data->ElementBegin();
	bm3::UserDataType::UserDataElementArrayI end = user_data->ElementEnd();

	OutputDebugStringA("\n\n----------------------------\n");

	for(;i != end;++ i){
		sprintf(buf, "Type  : %s\n", bm3::ToString(i->Type()));
		OutputDebugStringA(buf);
		sprintf(buf, "Name  : %s\n", ml::to_string(i->Name().Get()).c_str());
		OutputDebugStringA(buf);

		switch(i->Type()){
			case bm3::UserDataElementTypeConst_Int:
				sprintf(buf, "Value : %d\n", i->IntValue());
				OutputDebugStringA(buf);
				break;
			case bm3::UserDataElementTypeConst_Float:
				sprintf(buf, "Value : %f\n", i->FloatValue());
				OutputDebugStringA(buf);
				break;
			case bm3::UserDataElementTypeConst_String:
				sprintf(buf, "Value : %s\n", ml::to_string(i->StringValue()).c_str());
				OutputDebugStringA(buf);
				break;
			case bm3::UserDataElementTypeConst_Vector:
				sprintf(buf, "Value : %f,%f,%f\n", i->VectorValue().e_.x_, i->VectorValue().e_.y_, i->VectorValue().e_.z_);
				OutputDebugStringA(buf);
				break;
			case bm3::UserDataElementTypeConst_Color:
				sprintf(buf, "Value : %f,%f,%f,%f\n", i->ColorValue().e_.r_, i->ColorValue().e_.g_, i->ColorValue().e_.b_, i->ColorValue().e_.a_);
				OutputDebugStringA(buf);
				break;
			case bm3::UserDataElementTypeConst_Bool:
				sprintf(buf, "Value : %s\n", (i->BoolValue() ? "true" : "false"));
				OutputDebugStringA(buf);
				break;

			default:
				OutputDebugStringA("unknown type\n");
				break;
		}

		OutputDebugStringA("\n");
	}
}

/*
	追加：全エフェクトを更新
*/
void BMManager::UpdateAllEffect()
{
	if (s_instance == NULL){
		// 既に破棄されているとき
		return;
	}

	// Effect 更新処理
	BMEffectListType::iterator ite = listEffect.begin();
	BMEffectListType::iterator end = listEffect.end();
	for ( ; ite != end ; ++ite ){
		BMEffect* effect = reinterpret_cast<BMEffect*>(*ite);
		if (effect != NULL){
			// Update Effect
			effect->Update();
			// Loop ?
			if(effect->IsExpired() && effect->IsLoop()) {
				effect->Reset();
			}
		}
	}
}


/*
	追加：全エフェクトを描画
*/
void BMManager::DrawAllEffect(const D3DXMATRIX* viewMatrix, const D3DXMATRIX* projMatrix)
{
	if (s_instance == NULL){
		// 既に破棄されているとき
		return;
	}

	// Camera 情報更新
	ml::matrix44 view(
		viewMatrix->_11, viewMatrix->_12, viewMatrix->_13, viewMatrix->_14,
		viewMatrix->_21, viewMatrix->_22, viewMatrix->_23, viewMatrix->_24,
		viewMatrix->_31, viewMatrix->_32, viewMatrix->_33, viewMatrix->_34,
		viewMatrix->_41, viewMatrix->_42, viewMatrix->_43, viewMatrix->_44);
	ml::matrix44 proj(
		projMatrix->_11, projMatrix->_12, projMatrix->_13, projMatrix->_14,
		projMatrix->_21, projMatrix->_22, projMatrix->_23, projMatrix->_24,
		projMatrix->_31, projMatrix->_32, projMatrix->_33, projMatrix->_34,
		projMatrix->_41, projMatrix->_42, projMatrix->_43, projMatrix->_44);

	SetView(view);
	SetProjection(proj);

	// Effect 描画処理
	Begin();
	BMEffectListType::iterator ite = listEffect.begin();
	BMEffectListType::iterator end = listEffect.end();
	for ( ; ite != end ; ++ite ){
		BMEffect* effect = reinterpret_cast<BMEffect*>(*ite);
		if ( effect != NULL ){
			DrawEffect(effect);
		}
	}
	End();
}

/*
	追加：全エフェクトの破棄
*/
void BMManager::DestroyAllEffect()
{
	BMEffectListType::iterator ite = listEffect.begin();
	BMEffectListType::iterator end = listEffect.end();
	for ( ; ite != end ; ++ite ){
		ReleaseEffect(ite);
	}
}


/*
	追加：全破棄
*/
void BMManager::Destroy()
{
	if (s_instance == NULL){
		// 既に破棄されているとき
		return;
	}

	DestroyAllEffect();
	listEffect.clear();

	assert(binaries.size() == 0);	//<- このassert にかかったら解放漏れがある
	assert(textures.size() == 0);	//<- このassert にかかったら解放漏れがある
	assert(models.size() == 0);		//<- このassert にかかったら解放漏れがある

	if (hash) {
		delete [] hash;
		hash = NULL;
	}
	vertexManager.Release();
	bm3::RenderStateManager::instance().Finalize();
	bm3::System::instance().Finalize();
	bm3::System::finalize();

	device = NULL;
	s_instance = NULL;
}

