//------------------------------------------------------------------------------
// Lamp : Open source game middleware
// Copyright (C) 2004  Junpei Ohtani ( Email : junpee@users.sourceforge.jp )
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; either
// version 2.1 of the License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
//------------------------------------------------------------------------------

/** @file
 * 変換インスタンスマネージャ実装
 * @author Junpee
 */

#include "System/stdafx.h"
#include "Translator/Instance/TranslationInstanceManager.h"
#include "Translator/Instance/TranslationSceneNodeInstance.h"
#include "Translator/Instance/TranslationLightInstance.h"
#include "Translator/Instance/TranslationModelInstance.h"
#include "Translator/SceneNode/TranslationSceneNodeManager.h"
#include "Graphics/Scene/Scene.h"
#include "Graphics/SceneNode/SceneNodeManager.h"
#include "Graphics/Model/Model.h"

namespace LampForMaya{

//------------------------------------------------------------------------------
// コンストラクタ
TranslationInstanceManager::TranslationInstanceManager() :
	array_(256), convertedArray_(256){
}
//------------------------------------------------------------------------------
// デストラクタ
TranslationInstanceManager::~TranslationInstanceManager(){
	Assert(array_.getCount() == 0);
	Assert(convertedArray_.getCount() == 0);
	if((array_.getCount() != 0) || (convertedArray_.getCount() != 0)){
		clear();
	}
}
//------------------------------------------------------------------------------
// クリア
int TranslationInstanceManager::clear(){
	int result = 0;
	// 配列のクリア
	int count = array_.getCount();
	Assert(count == 0);
	for(int i = 0; i < count; i++){ delete array_.get(i); }
	array_.clear();
	result += count;
	// コンバート済み配列のクリア
	int convertedCount = convertedArray_.getCount();
	for(int i = 0; i < convertedCount; i++){ delete convertedArray_.get(i); }
	convertedArray_.clear();
	result += convertedCount;
	return result;
}
//------------------------------------------------------------------------------
// インスタンスの収集
bool TranslationInstanceManager::collectInstances(){
	MStatus result;
	MItDag dagIterator(MItDag::kBreadthFirst, MFn::kInvalid, &result);
	MayaStatusCheck(result);
	MDagPath dagPath;
	for( ; !dagIterator.isDone(); dagIterator.next()){
		result = dagIterator.getPath(dagPath);
		MayaStatusCheck(result);
		MFnDagNode dagNode(dagPath, &result);
		MayaStatusCheck(result);
		// 最上階層以下になったらループを抜ける
		u_int length = dagPath.length(&result);
		MayaStatusCheck(result);
		if(length > 1){ break; }
		// 有効なDagノードかチェック、シーンノードのチェック使用
		if(!TranslationSceneNodeManager::checkValidDagNode(dagPath)){ 
			continue;
		}
		// 最上階層かつトランスフォームファンクションを持っていなければキャンセル
		// これでworldオブジェクトをはじける
		if(!dagPath.hasFn(MFn::kTransform)){ continue; }
		// グラウンドプレーンならキャンセル
		if(MayaDAGUtility::getName(dagPath) == "groundPlane_transform"){
			continue;
		}
		// インスタンスの解析
		if(!analysisInstance(dagPath)){ return false; }
	}
	return true;
}
//------------------------------------------------------------------------------
// インスタンスの解析
bool TranslationInstanceManager::analysisInstance(MDagPath dagPath){
	MStatus result;
	MFnDagNode dagNode(dagPath, &result);
	MayaStatusCheck(result);

	// インスタンスオブジェクトかどうか
	u_int instanceNumber = dagPath.instanceNumber(&result);
	MayaStatusCheck(result);
	if(instanceNumber > 0){
		// インスタンスオブジェクトの作成し、それ以上の子はたどらない
		String instanceName = MayaDAGUtility::getName(dagPath);
		TranslationInstance* instance;
		if(dagPath.hasFn(MFn::kTransform)){
			// シーンノードインスタンス
			instance = new TranslationSceneNodeInstance(dagPath, instanceName);
		}else if(dagPath.hasFn(MFn::kLight)){
			// ライトインスタンス
			instance = new TranslationLightInstance(dagPath, instanceName);
		}else{
			// モデルインスタンス
			instance = new TranslationModelInstance(dagPath, instanceName);
		}
		// 各インスタンスの解析
		if(!instance->analyze()){
			delete instance;
			return false;
		}
		array_.add(instance);
		return true;
	}

	// インスタンスではなかったので子をたどる
	u_int length = dagPath.length(&result);
	MItDag childIterator(MItDag::kBreadthFirst, MFn::kInvalid, &result);
	MayaStatusCheck(result);
	result = childIterator.reset(
		dagPath, MItDag::kBreadthFirst, MFn::kInvalid);
	MayaStatusCheck(result);
	// 自分自身を読み飛ばす
	childIterator.next();
	MDagPath childPath;
	for( ; !childIterator.isDone(); childIterator.next()){
		result = childIterator.getPath(childPath);
		MayaStatusCheck(result);
		// 一階層下以下になったらループを抜ける
		u_int childLength = childPath.length(&result);
		MayaStatusCheck(result);
		if(childLength > length + 1){ break; }
		// 有効なDagノードかチェック、シーンノードのチェック使用
		if(!TranslationSceneNodeManager::checkValidDagNode(childPath)){ 
			continue;
		}
		// 子の分析
		analysisInstance(childPath);
	}
	return true;
}
//------------------------------------------------------------------------------
// Lampへの変換
//------------------------------------------------------------------------------
// Lampへの変換
bool TranslationInstanceManager::convertToLamp(Scene* scene){
	// コピー元およびその子供のSceneNode,Modelがインスタンス対象になっていないか
	//		インスタンス対象になっていれば、それをさらに同じチェックにかける
	//		インスタンス対象になっていなければ、インスタンス解決
	// 全てのインスタンスオブジェクトが片付くまで繰り返す
	while(true){
		if(array_.getCount() == 0){ break; }
		TranslationInstance* instance =
			searchValidInstance(scene, array_.get(0));
		// インスタンスの解決を行う
		if(!instance->convertToLamp(scene)){ return false; }
		array_.removeByValue(instance);
		convertedArray_.add(instance);
	}
	return true;
}
//------------------------------------------------------------------------------
// インスタンスの解決
TranslationInstance* TranslationInstanceManager::searchValidInstance(
	Scene* scene, TranslationInstance* instance){
	SceneNodeManager* sceneNodeManager = scene->getSceneNodeManager();
	// シーンノードインスタンスでないなら無条件でインスタンス解決を行ってよい
	if(!instance->isTranslationSceneNodeInstance()){ return instance; }
	// コピー元が他のインスタンスを含んでいないか調査
	int instanceCount = array_.getCount();
	TranslationInstance* includeInstance = NULL;
	for(int i = 0; i < instanceCount; i++){
		TranslationInstance* target = array_.get(i);
		// 自分自身は無視
		if(target == instance){ continue; }
		// 同じオブジェクトをインスタンス化しようとしている時は無視
		if(instance->getName() == target->getName()){ continue; }
		SceneNode* sceneNode = sceneNodeManager->search(instance->getName());
		if(searchSceneNode(sceneNode, target->getName())){
			// ターゲットと同じ名前のオブジェクトが見つかった
			includeInstance = target;
			break;
		}
	}
	// 他のインスタンスを先に解決する
	if(includeInstance != NULL){
		return searchValidInstance(scene, includeInstance);
	}
	return instance;
}
//------------------------------------------------------------------------------
// シーンノードの検索
bool TranslationInstanceManager::searchSceneNode(
	SceneNode* sceneNode, const String& targetName){
	if(sceneNode->getName().equals(targetName)){ return true; }
	// シーンリーフの検索
	int sceneLeafCount = sceneNode->getSceneLeafCount();
	for(int i = 0; i < sceneLeafCount; i++){
		SceneLeaf* sceneLeaf = sceneNode->getSceneLeaf(i);
		if(sceneLeaf->getName().equals(targetName)){ return true; }
	}
	// 子シーンノードの検索
	int sceneNodeCount = sceneNode->getSceneNodeCount();
	for(int i = 0; i < sceneNodeCount; i++){
		bool result = searchSceneNode(sceneNode->getSceneNode(i), targetName);
		if(result){ return true; }
	}
	return false;
}
//------------------------------------------------------------------------------
// アニメーション
//------------------------------------------------------------------------------
// アニメーションの変換
bool TranslationInstanceManager::convertAnimation(
	AnimationManager* animationManager, AnimationSet* animationSet){
	int count = convertedArray_.getCount();
	for(int i = 0; i < count; i++){
		if(!convertedArray_.get(i)->convertAnimation(
			animationManager, animationSet)){
			return false;
		}
	}
	return true;
}
//------------------------------------------------------------------------------
} // End of namespace LampForMaya
//------------------------------------------------------------------------------
