//------------------------------------------------------------------------------
// 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/SceneNode/TranslationSceneNode.h"
#include "Translator/SceneNode/TranslationSceneNodeManager.h"
#include "Translator/Model/TranslationModelManager.h"
#include "Translator/Animation/TranslationAnimationUtility.h"
#include "Graphics/Scene/Scene.h"
#include "Graphics/SceneNode/SceneNodeManager.h"
#include "Graphics/Light/LightManager.h"
#include "Graphics/Model/ModelManager.h"
#include "Animation/VectorInterpolator/VectorArrayInterpolator.h"
#include "Animation/RotationInterpolator/EulerArrayInterpolator.h"
#include "Animation/System/AnimationManager.h"
#include "Animation/System/AnimationSet.h"
#include "Animation/SceneNode/SceneNodeAnimation.h"

namespace LampForMaya{

//------------------------------------------------------------------------------
// コンストラクタ
TranslationSceneNode::TranslationSceneNode(
	const MDagPath& initializePath, const String& initializeName) :
	dagPath_(initializePath), name_(initializeName),
	lodThresholdCount_(0), lodThreshold_(NULL),
	scaleAnimation_(NULL), rotationAnimation_(NULL),
	translationAnimation_(NULL), hasAnimation_(false){
	MStatus result;
	// オブジェクトの取得
	object_ = dagPath_.node(&result);
	MayaStatusCheck(result);
}
//------------------------------------------------------------------------------
// デストラクタ
TranslationSceneNode::~TranslationSceneNode(){
	SafeDelete(translationAnimation_);
	SafeDelete(rotationAnimation_);
	SafeDelete(scaleAnimation_);
	SafeArrayDelete(lodThreshold_);
}
//------------------------------------------------------------------------------
// 分析
bool TranslationSceneNode::analyze(){
	MStatus result;
	String errorString;
	MFnDagNode dagNode(dagPath_, &result);
	MayaStatusCheck(result);
	// lengthが1なら最上位DAGノード
	u_int length = dagPath_.length(&result);
	MayaStatusCheck(result);
	isTopLevelNode_ = (length == 1);

	// 子供の解析
	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;
		}
		// インスタンス化されていればキャンセル
		u_int instanceNumber = childPath.instanceNumber(&result);
		MayaStatusCheck(result);
		if(instanceNumber > 0){ continue; }
		// 名前の取得
		MFnDagNode childNode(childPath, &result);
		MayaStatusCheck(result);
		String childName(childNode.name(&result).asChar());
		MayaStatusCheck(result);
		// モデルかシーンノードか
		bool childIsSceneNode = childPath.hasFn(MFn::kTransform);
		// リストに追加
		if(childIsSceneNode){
			sceneNodes_.add(childName);
		}else{
			// ロケータは無視する
			if(childPath.hasFn(MFn::kLocator)){ continue; }
			if(childPath.hasFn(MFn::kLight)){
				// ライトの追加
				lights_.add(childName);
			}else{
				// モデルの追加
				models_.add(childName);
			}
		}
	}

	// 表示フラグ
	visibility_ = MayaAttributeUtility::getBool(object_, "visibility");

	// トランスフォーム初期化
	MFnTransform transform(dagPath_, &result);
	MayaStatusCheck(result);

	// スケールピボット移動値が0であるかチェック
	MPoint scalePivotTrans =
		transform.scalePivotTranslation(MSpace::kTransform, &result);
	MayaStatusCheck(result);
	if(!zeroCheck(scalePivotTrans)){
		errorString.format("TranslationSceneNode::analyze() "
			"scalePivotTranslationが0でない ( %8f, %8f, %8f ) %s",
			scalePivotTrans.x, scalePivotTrans.y, scalePivotTrans.z,
			name_.getBytes());
		MayaErrorOut(errorString);
		return false;
	}

	// 回転ピボット移動値が0であるかチェック
	MPoint rotationPivotTrans =
		transform.rotatePivotTranslation(MSpace::kTransform, &result);
	MayaStatusCheck(result);
	if(!zeroCheck(rotationPivotTrans)){
		errorString.format("TranslationSceneNode::analyze() "
			"rotationPivotTransが0でない ( %8f, %8f, %8f ) %s",
			rotationPivotTrans.x, rotationPivotTrans.y,
			rotationPivotTrans.z, name_.getBytes());
		MayaErrorOut(errorString);
		return false;
	}

	// シアーが0であるかチェック
	double shearArray[3];
	result = transform.getShear(shearArray);
	MPoint shear(shearArray);
	if(!zeroCheck(shear)){
		errorString.format("TranslationSceneNode::analyze() "
			"shearが0でない ( %8f, %8f, %8f ) %s",
			shear.x, shear.y, shear.z, name_.getBytes());
		MayaErrorOut(errorString);
		return false;
	}
	
	// スケールピボットとローテーションピボットが同値であることをチェック
	MPoint scalePivot = transform.scalePivot(MSpace::kTransform, &result);
	MayaStatusCheck(result);
	MPoint rotationPivot = transform.rotatePivot(MSpace::kTransform, &result);
	MayaStatusCheck(result);
	MPoint pivotDifference = scalePivot - rotationPivot;
	if(!zeroCheck(pivotDifference)){
		errorString.format("TranslationSceneNode::analyze() "
			"scalePivotとrotationPivotが同じでない "
			"scale ( %8f, %8f, %8f ) rotation ( %8f, %8f, %8f ) %s",
			scalePivot.x, scalePivot.y, scalePivot.z,
			rotationPivot.x, rotationPivot.y, rotationPivot.z,
			name_.getBytes());
		MayaErrorOut(errorString);
		return false;
	}
	pivot_.set((float)rotationPivot.x,
		(float)rotationPivot.y, (float)rotationPivot.z);

	// スケール
	double scale[3];
	result = transform.getScale(scale);
	MayaStatusCheck(result);
	scale_.set((float)scale[0], (float)scale[1], (float)scale[2]);

	// 回転
	MEulerRotation rotation;
	result = transform.getRotation(rotation);
	MayaStatusCheck(result);
	if(rotation.order != MEulerRotation::kXYZ){
		errorString.format("TranslationSceneNode::analyze() "
			"回転順序はXYZしかサポートしていません %s", name_.getBytes());
		MayaErrorOut(errorString);
		return false;
	}
	rotation_.set((float)rotation.x, (float)rotation.y, (float)rotation.z);

	// 移動
	MVector translation = transform.translation(MSpace::kTransform, &result);
	MayaStatusCheck(result);
	translation_.set(
		(float)translation.x, (float)translation.y, (float)translation.z);

	// LOD
	if(object_.hasFn(MFn::kLodGroup)){
		MObject thresholdAttribute = dagNode.attribute("threshold", &result);
		MayaStatusCheck(result);
		MPlug thresholdPlug(object_, thresholdAttribute);
		u_int thresholdValueCount = thresholdPlug.numElements();
		lodThresholdCount_ = thresholdValueCount + 2;// 最大値、最小値分
		lodThreshold_ = new float[lodThresholdCount_];
		lodThreshold_[0] = 0.f;
		lodThreshold_[lodThresholdCount_ - 1] = Limit::floatMax;
		for(u_int i = 0; i < thresholdValueCount; i++){
			MPlug thresholdValuePlug = thresholdPlug[i];
			double doubleValue;
			MayaStatusCheck(thresholdValuePlug.getValue(doubleValue));
			lodThreshold_[i + 1] = (float)doubleValue;
		}
		// 最小値、最大値
		MObject minMaxAttribute = dagNode.attribute("minMaxDistance", &result);
		MayaStatusCheck(result);
		MPlug minMaxPlug(object_, minMaxAttribute);
		bool minMax;
		MayaStatusCheck(minMaxPlug.getValue(minMax));
		if(minMax){
			// 最小値
			MObject minAttribute = dagNode.attribute("minDistance", &result);
			MayaStatusCheck(result);
			MPlug minPlug(object_, minAttribute);
			double minValue;
			MayaStatusCheck(minPlug.getValue(minValue));
			lodThreshold_[0] = (float)minValue;
			if(lodThreshold_[0] < 0.f){ lodThreshold_[0] = 0.f; }
			// 最大値
			MObject maxAttribute = dagNode.attribute("maxDistance", &result);
			MayaStatusCheck(result);
			MPlug maxPlug(object_, maxAttribute);
			double maxValue;
			MayaStatusCheck(maxPlug.getValue(maxValue));
			lodThreshold_[lodThresholdCount_ - 1] = (float)maxValue;
		}
	}
	return true;
}
//------------------------------------------------------------------------------
// ゼロチェック
bool TranslationSceneNode::zeroCheck(const MPoint& point){
	if(	(Math::abs((float)point.x) > Math::epsilon) ||
		(Math::abs((float)point.y) > Math::epsilon) ||
		(Math::abs((float)point.z) > Math::epsilon)){
		return false;
	}
	return true;
}
//------------------------------------------------------------------------------
// ピボットのコンパイル
bool TranslationSceneNode::compilePivot(
	TranslationSceneNodeManager* sceneNodeManager,
	TranslationModelManager* modelManager){
	// ピボットのコンパイル
	translation_ += pivot_;
	// 子ノードのコンパイル
	for(int i = 0; i < sceneNodes_.getCount(); i++){
		String sceneNodeName = sceneNodes_.get(i);
		TranslationSceneNode* sceneNode =
			sceneNodeManager->search(sceneNodeName);
		if(sceneNode == NULL){
			MayaErrorOut(String("TranslationSceneNode::compilePivot() ") +
				name_ + "の子シーンノードが見つかりません " + sceneNodeName);
			return false;
		}
		sceneNode->translation_ -= pivot_;
	}
	// 子モデルのコンパイル
	for(int i = 0; i < models_.getCount(); i++){
		String modelName = models_.get(i);
		TranslationModel* model = modelManager->search(modelName);
		if(model == NULL){
			MayaErrorOut(String("TranslationSceneNode::compilePivot() ") +
				name_ + "の子モデルが見つかりません " + modelName);
			return false;
		}
		model->compilePivot(pivot_);
	}
// 子ライトのコンパイル必要？
	pivot_.set(0.f, 0.f, 0.f);
	return true;
}
//------------------------------------------------------------------------------
// アニメーションの分析
bool TranslationSceneNode::analyzeAnimation(){
	// シーケンスの分析、シーケンスが無ければアニメーション無し
	if(!sequence_.analyze(object_)){ return true; }
	hasAnimation_ = false;
	int startTime = sequence_.getStartTime(0);
	int endTime = sequence_.getEndTime(sequence_.getSequenceCount() - 1);
	// スケールアニメーション
	scaleAnimation_ =
		TranslationAnimationUtility::analyzeVectorAnimation(
		object_, "scale", scale_, startTime, endTime);
	if(scaleAnimation_ != NULL){ hasAnimation_ = true; }
	// 回転アニメーション
	rotationAnimation_ =
		TranslationAnimationUtility::analyzeRotationAnimation(
		object_, "rotate", rotation_, startTime, endTime);
	if(rotationAnimation_ != NULL){ hasAnimation_ = true; }
	// 移動アニメーション
	translationAnimation_ =
		TranslationAnimationUtility::analyzeVectorAnimation(
		object_, "translate", (translation_ - pivot_), startTime, endTime);
	if(translationAnimation_ != NULL){ hasAnimation_ = true; }
	// ピボット足しこみ
	if(translationAnimation_ != NULL){
		int size = translationAnimation_->getSize();
		for(int i = 0; i < size; i++){
			Vector3 value = translationAnimation_->getValue(i);
			value += pivot_;
			translationAnimation_->setValue(i, value);
		}
	}
	return true;
}
//------------------------------------------------------------------------------
// Lampへの変換
bool TranslationSceneNode::convertToLamp(Scene* scene){
	SceneNodeManager* sceneNodeManager = scene->getSceneNodeManager();
	if(lodThresholdCount_ != 0){
		// LODシーンノード設定
		LODSceneNode* lodSceneNode =
			sceneNodeManager->createLODSceneNode(name_);
		lodSceneNode->setLODThresholdCount(lodThresholdCount_);
		for(int i = 0; i < lodThresholdCount_; i++){
			lodSceneNode->setLODThreshold(i, lodThreshold_[i]);
		}
		sceneNode_ = lodSceneNode;
	}else{
		sceneNode_ = sceneNodeManager->createSceneNode(name_);
	}
	sceneNode_->setScale(scale_);
	sceneNode_->setRotationXYZ(rotation_);
	sceneNode_->setTranslation(translation_);
	sceneNode_->setEnabled(visibility_);
	return true;
}
//------------------------------------------------------------------------------
// リンク接続
bool TranslationSceneNode::linkConnect(Scene* scene){
	// ルートノードとのリンク
	SceneNodeManager* sceneNodeManager = scene->getSceneNodeManager();
	if(isTopLevelNode_){
		scene->getRootNode()->addSceneNode(sceneNode_);
	}
	// シーンノードとのリンク
	for(int i = 0; i < sceneNodes_.getCount(); i++){
		SceneNode* sceneNode = sceneNodeManager->search(sceneNodes_.get(i));
		if(sceneNode == NULL){
			MayaErrorOut(String("TranslationSceneNode::linkConnect() ") +
				name_ + "の子シーンノードが見つかりません " +
				sceneNodes_.get(i));
			return false;
		}
		sceneNode_->addSceneNode(sceneNode);
	}
	// ライトとのリンク
	LightManager* lightManager = scene->getLightManager();
	for(int i = 0; i < lights_.getCount(); i++){
		Light* light = lightManager->search(lights_[i]);
		if(light == NULL){
			MayaErrorOut(String("TranslationSceneNode::linkConnect() ") +
				name_ + "の子ライトが見つかりません " + lights_[i]);
			return false;
		}
		sceneNode_->addSceneLeaf(light);
	}
	// モデルとのリンク
	ModelManager* modelManager = scene->getModelManager();
	for(int i = 0; i < models_.getCount(); i++){
		Model* model = modelManager->search(models_[i]);
		if(model == NULL){
			MayaErrorOut(String("TranslationSceneNode::linkConnect() ") +
				name_ + "の子モデルが見つかりません " + models_.get(i));
			return false;
		}
		sceneNode_->addSceneLeaf(model);
	}
	return true;
}
//------------------------------------------------------------------------------
// アニメーションの変換
bool TranslationSceneNode::convertAnimation(
	AnimationManager* animationManager, AnimationSet* animationSet){
	if(!hasAnimation_){ return true; }
	SceneNodeAnimation* animation = animationManager->createSceneNode(name_);
	if(animation->getName() != name_){
		MayaErrorOut(String("TranslationSceneNode::convertAnimation() ") +
			name_ + "の名前が重複しています ");
		return false;
	}
	SceneNodeAnimationData* data =
		animationManager->createSceneNodeData(name_);
	if(data->getName() != name_){
		MayaErrorOut(String("TranslationSceneNode::convertAnimation() ") +
			name_ + "の名前が重複しています ");
		return false;
	}
	// アニメーションの設定
	animation->setTargetName(name_);
	animation->setSceneNodeAnimationData(data);
	animationSet->addAnimation(animation);
	// アニメーションデータの設定
	int sequenceCount = sequence_.getSequenceCount();
	data->setSequenceCount(sequenceCount);
	for(int i = 0; i < sequenceCount; i++){
		int startTime = sequence_.getStartTime(i);
		int endTime = sequence_.getEndTime(i);
		int size = endTime - startTime + 1;
		// スケールアニメーションの準備
		VectorArrayInterpolator* scale = NULL;
		if(scaleAnimation_ != NULL){
			scale = new VectorArrayInterpolator();
			scale->setSize(size);
			for(int j = 0; j < size; j++){
				scale->setValue(j, scaleAnimation_->getValue(startTime + j));
			}
		}
		// 回転アニメーションの準備
		EulerArrayInterpolator* rotation = NULL;
		if(rotationAnimation_ != NULL){
			rotation = new EulerArrayInterpolator();
			rotation->setSize(size);
			for(int j = 0; j < size; j++){
				rotation->setValue(j,
					rotationAnimation_->getValue(startTime + j));
			}
		}
		// 移動アニメーションの準備
		VectorArrayInterpolator* translation = NULL;
		if(translationAnimation_ != NULL){
			translation = new VectorArrayInterpolator();
			translation->setSize(size);
			for(int j = 0; j < size; j++){
				translation->setValue(j,
					translationAnimation_->getValue(startTime + j));
			}
		}
		// データの設定
		if(scale != NULL){ data->setScale(i, scale); }
		if(rotation != NULL){ data->setRotation(i, rotation); }
		if(translation != NULL){
			data->setTranslation(i, translation);
		}
		data->setLooped(i, sequence_.isLooped(i));
	}
	return true;
}
//------------------------------------------------------------------------------
} // End of namespace LampForMaya
//------------------------------------------------------------------------------
