//------------------------------------------------------------------------------
// 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
 * Lampトランスレータ実装
 * @author Junpee
 */

#include "System/stdafx.h"
#include "Translator/Core/LampTranslator.h"
#include "Translator/Scene/TranslationScene.h"
#include <Core/Utility/Timer.h>
#include <Core/Utility/StringTokenizer.h>
#include <Graphics/System/LampGraphics.h>
#include <Graphics/InputOutput/TextSceneSaver.h>
#include <Graphics/InputOutput/BinarySceneSaver.h>
#include <Graphics/SceneFilter/SceneFilter.h>
#include <Animation/System/AnimationManager.h>
#include <Animation/System/AnimationSet.h>
#include <Animation/Utility/AnimationCompressor.h>
#include <Animation/InputOutput/TextAnimationSaver.h>
#include <Animation/InputOutput/BinaryAnimationSaver.h>

namespace LampForMaya{

//------------------------------------------------------------------------------
// コンストラクタ
LampTranslator::LampTranslator() :
	translationScene_(NULL), scene_(NULL), animationManager_(NULL),
	animationSet_(NULL), exportSceneFlag_(true), picturePath_("pictures/"),
	meshOptimizeFlag_(true), characterScale_(1.f), exportAnimationFlag_(true),
	deleteStaticChannelsFlag_(false){
}
//------------------------------------------------------------------------------
// デストラクタ
LampTranslator::~LampTranslator(){
	Assert(translationScene_ == NULL);
	Assert(scene_ == NULL);
	Assert(animationManager_ == NULL);
	Assert(animationSet_ == NULL);
}
//------------------------------------------------------------------------------
// 書き出し
MStatus LampTranslator::write(const MFileObject& file,
	const MString& optionsString, MPxFileTranslator::FileAccessMode mode,
	bool textFlag){
	Timer::Tick startTime = Timer::getTick();
	textExportFlag_ = textFlag;
	// 全てエクスポートのみ受け付ける
	if(mode != MPxFileTranslator::kExportAccessMode){
		MayaErrorOut("\"Export All\"しかサポートしていません。");
		return errorCleanup();
	}
	// オプション解析
	analyzeOption(String(optionsString.asChar()));
	// 開始メッセージ出力
	outputStartMessage();

	// 静的チャンネル削除
	if(exportAnimationFlag_ && deleteStaticChannelsFlag_){
		MGlobal::executeCommand("delete -all -staticChannels;", false, true);
	}

	//--------------------------------------------------------------------------
	// 変換用シーンによる情報の収集
	Timer::Tick startSceneCollectionTime = Timer::getTick();
	translationScene_ = new TranslationScene();
	bool result = translationScene_->collection();
	if(!result){ return errorCleanup(); }
	float sceneCollectionTime =
		Timer::getInterval(startSceneCollectionTime)  * 0.001f;


	// 変換用シーンによるアニメーション情報の収集
	Timer::Tick startAnimationCollectionTime = Timer::getTick();
	result = translationScene_->collectAnimation();
	if(!result){ return errorCleanup(); }
	float animationCollectionTime =
		Timer::getInterval(startAnimationCollectionTime)  * 0.001f;

	//--------------------------------------------------------------------------
	// Lampシーンへの変換
	Timer::Tick startSceneTranslateTime = Timer::getTick();
	scene_ = LampGraphics::createScene("TranslateScene");
	result = translationScene_->convertToLamp(scene_);
	if(!result){ return errorCleanup(); }
	float sceneTranslateTime =
		Timer::getInterval(startSceneTranslateTime)  * 0.001f;

	// Lampアニメーションへの変換
	Timer::Tick startAnimationTranslateTime = Timer::getTick();
	animationManager_ = new AnimationManager();
	String animationName = file.name().asChar();
	if(textFlag){
		Assert(animationName.endsWith("." + getTextSceneExtension()));
	}else{
		Assert(animationName.endsWith("." + getBinarySceneExtension()));
	}
	animationName = animationName.getSubstring(0, animationName.getSize() - 4);
	animationSet_ = animationManager_->createAnimationSet(animationName);
	result = translationScene_->convertAnimation(
		animationManager_, animationSet_);
	if(!result){ return errorCleanup(); }
	float animationTranslateTime =
		Timer::getInterval(startAnimationTranslateTime)  * 0.001f;

	// アニメーションの圧縮
	Timer::Tick startAnimationCompressTime = Timer::getTick();
	AnimationCompressor animationCompressor;
	animationCompressor.compress(animationSet_);
	float animationCompressTime =
		Timer::getInterval(startAnimationCompressTime)  * 0.001f;

	//--------------------------------------------------------------------------
	// 変換用シーンの削除
	translationScene_->clear();
	delete translationScene_;
	translationScene_ = NULL;

	//--------------------------------------------------------------------------
	// シーンへのフィルタ処理
	Timer::Tick startFilterTime = Timer::getTick();
	SceneFilter sceneFilter(scene_);
	// ピクチャパス変更
	if(exportSceneFlag_){
		if(!sceneFilter.filter(String("ChangePicturePath ") + picturePath_)){
			return errorCleanup();
		}
		// バウンディング算出
		String scaleString;
		scaleString.format("characterScale %.8f", characterScale_);
		// バウンディングボックス算出
		if(!sceneFilter.filter("CalculateBoundingBox " + scaleString)){
			return errorCleanup();
		}
		// バウンディングスフィア算出
		if(!sceneFilter.filter("CalculateBoundingSphere " + scaleString)){
			return errorCleanup();
		}
		// メッシュ最適化
		if(meshOptimizeFlag_){
			// インデックストライアングル構築
			if(!sceneFilter.filter("BuildIndexedTriangle")){
				return errorCleanup();
			}
		}
	}
	// シーンの論理チェック
	if(!sceneFilter.filter("SceneLogicCheck")){ return errorCleanup(); }
	float filterTime = Timer::getInterval(startFilterTime)  * 0.001f;

	//--------------------------------------------------------------------------
	// シーン出力
	Timer::Tick startSceneExportTime = Timer::getTick();
	if(exportSceneFlag_){
		if(textExportFlag_){
			TextSceneSaver* textSceneSaver = new TextSceneSaver();
			textSceneSaver->save(file.fullName().asChar(), scene_);
			delete textSceneSaver;
		}else{
			BinarySceneSaver* binarySceneSaver = new BinarySceneSaver();
			binarySceneSaver->save(file.fullName().asChar(), scene_);
			delete binarySceneSaver;
		}
	}
	float sceneExportTime = Timer::getInterval(startSceneExportTime)  * 0.001f;

	// Lampシーンの破棄
	scene_->clear();
	LampGraphics::destroyScene(scene_);
	scene_ = NULL;

	//--------------------------------------------------------------------------
	// アニメーション出力
	Timer::Tick startAnimationExportTime = Timer::getTick();
	if(exportAnimationFlag_ && (animationSet_->getAnimationCount() != 0)){
		String animationFileName = file.fullName().asChar();
		if(textExportFlag_){
			Assert(animationFileName.endsWith("." + getTextSceneExtension()));
			animationFileName = animationFileName.getSubstring(
				0, animationFileName.getSize() - 3);
			animationFileName += getTextAnimationExtension();
			TextAnimationSaver* textAnimationSaver = new TextAnimationSaver();
			textAnimationSaver->save(animationFileName, animationManager_);
			delete textAnimationSaver;
		}else{
			Assert(animationFileName.endsWith("." + getBinarySceneExtension()));
			animationFileName = animationFileName.getSubstring(
				0, animationFileName.getSize() - 3);
			animationFileName += getBinaryAnimationExtension();
			BinaryAnimationSaver* binaryAnimationSaver =
				new BinaryAnimationSaver();
			binaryAnimationSaver->save(animationFileName, animationManager_);
			delete binaryAnimationSaver;
		}
	}
	float animationExportTime =
		Timer::getInterval(startAnimationExportTime)  * 0.001f;

	// Lampアニメーションの破棄
	animationManager_->clear();
	delete animationManager_;
	animationSet_ = NULL;
	animationManager_ = NULL;

	//--------------------------------------------------------------------------
	// 終了メッセージ
	float totalTime = Timer::getInterval(startTime)  * 0.001f;
	float otherTime = totalTime - (
		sceneCollectionTime + animationCollectionTime +
		sceneTranslateTime + animationTranslateTime + animationCompressTime +
		filterTime + sceneExportTime + animationExportTime);
	String finishMessage;
	finishMessage.format("\nエクスポート終了  トータル　%.2f秒\n"
		"　シーン情報収集  %.2f秒\n"
		"　アニメーション情報収集  %.2f秒\n"
		"　シーン変換  %.2f秒\n"
		"　アニメーション変換  %.2f秒\n"
		"　アニメーション圧縮  %.2f秒\n"
		"　フィルタ  %.2f秒\n"
		"　シーン出力  %.2f秒\n"
		"　アニメーション出力  %.2f秒\n"
		"　その他  %.2f秒\n",
		totalTime, sceneCollectionTime, animationCollectionTime,
		sceneTranslateTime, animationTranslateTime, animationCompressTime,
		filterTime, sceneExportTime, animationExportTime, otherTime);
	MayaMessageOut(finishMessage);
	return MStatus(MStatus::kSuccess);
}
//------------------------------------------------------------------------------
// エラー後始末
MStatus LampTranslator::errorCleanup(){
	if(translationScene_ != NULL){
		translationScene_->clear();
		delete translationScene_;
		translationScene_ = NULL;
	}
	if(scene_ != NULL){
		scene_->clear();
		LampGraphics::destroyScene(scene_);
		scene_ = NULL;
	}
	if(animationManager_ != NULL){
		animationManager_->clear();
		delete animationManager_;
		animationSet_ = NULL;
		animationManager_ = NULL;
	}
	return MStatus(MStatus::kFailure);
}
//------------------------------------------------------------------------------
// オプション解析
void LampTranslator::analyzeOption(const String& options){
	StringTokenizer optionList(options, ";");
	while(optionList.hasMoreTokens()){
		StringTokenizer option(optionList.getNextToken(), "=");
		if(!option.hasMoreTokens()){ continue; }
		String optionName = option.getNextToken();
		if(!option.hasMoreTokens()){ continue; }
		String optionData = option.getNextToken();
		if(optionName == "scene"){
			exportSceneFlag_ = (optionData != "0");
		}else if(optionName == "picturePath"){
			picturePath_ = optionData;
			if(!picturePath_.endsWith("/")){ picturePath_ += "/"; }
		}else if(optionName == "meshOptimize"){
			meshOptimizeFlag_ = (optionData != "0");
		}else if(optionName == "characterScale"){
			if(!optionData.parseFloat(&characterScale_)){
				characterScale_ = 1.f;
			}
		}else if(optionName == "animation"){
			exportAnimationFlag_ = (optionData != "0");
		}else if(optionName == "deleteStaticChannnels"){
			deleteStaticChannelsFlag_ = (optionData != "0");
		}
	}
}
//------------------------------------------------------------------------------
// 開始メッセージ出力
void LampTranslator::outputStartMessage(){
	String startMessage;
	if(textExportFlag_){ startMessage += "\nテキストエクスポート開始\n"; }
	else{ startMessage += "\nバイナリエクスポート開始\n"; }
	if(exportSceneFlag_){ startMessage += "　シーン出力　有効\n"; }
	else{ startMessage += "　シーン出力　無効\n"; }
	if(exportSceneFlag_){
		startMessage += "　　ピクチャ出力パス　" + picturePath_ + "\n";
		if(meshOptimizeFlag_){ startMessage += "　　メッシュ最適化　有効\n"; }
		else{ startMessage += "　　メッシュ最適化　無効\n"; }
		String characterScaleString;
		characterScaleString.format(
			"　　キャラクタバウンディングスケール　%.8f\n", characterScale_);
		startMessage += characterScaleString;
	}
	if(exportAnimationFlag_){ startMessage += "　アニメーション出力　有効\n"; }
	else{ startMessage += "　アニメーション出力　無効\n"; }
	if(exportAnimationFlag_){
		if(deleteStaticChannelsFlag_){
			startMessage += "　　静的チャンネル削除　有効\n";
		}else{
			startMessage += "　　静的チャンネル削除　無効\n";
		}
	}
	startMessage += "\n";
	MayaMessageOut(startMessage);
}
//------------------------------------------------------------------------------
} // End of namespace Lamp
//------------------------------------------------------------------------------
