//------------------------------------------------------------------------------
// 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 "LampBasic.h"
#include "Animation/InputOutput/BinaryAnimationSaver.h"
#include "Core/InputOutput/BinaryFileWriter.h"
#include "Animation/VectorInterpolator/VectorConstantInterpolator.h"
#include "Animation/VectorInterpolator/VectorArrayInterpolator.h"
#include "Animation/VectorInterpolator/VectorLinearInterpolator.h"
#include "Animation/RotationInterpolator/RotationConstantInterpolator.h"
#include "Animation/RotationInterpolator/EulerArrayInterpolator.h"
#include "Animation/RotationInterpolator/QuaternionArrayInterpolator.h"
#include "Animation/RotationInterpolator/QuaternionLinearInterpolator.h"

#include "Animation/System/AnimationManager.h"
#include "Animation/System/AnimationSet.h"
#include "Animation/Camera/CameraAnimation.h"
#include "Animation/SceneNode/SceneNodeAnimation.h"
#include "Animation/Model/CharacterModelAnimation.h"

namespace Lamp{

//------------------------------------------------------------------------------
// コンストラクタ
BinaryAnimationSaver::BinaryAnimationSaver(){
}
//------------------------------------------------------------------------------
// デストラクタ
BinaryAnimationSaver::~BinaryAnimationSaver(){
}
//------------------------------------------------------------------------------
// セーブ
//------------------------------------------------------------------------------
// セーブ
void BinaryAnimationSaver::save(
	const String& filePath, AnimationManager* manager){
	BinaryFileWriter* binaryFileWriter = new BinaryFileWriter(filePath);
	save(binaryFileWriter, manager);
	delete binaryFileWriter;
}
//------------------------------------------------------------------------------
// セーブ
void BinaryAnimationSaver::save(
	BinaryWriter* binaryWriter, AnimationManager* manager){
	Assert((binaryWriter != NULL) && (manager != NULL))
	writer_ = binaryWriter;
	manager_ = manager;

	// ヘッダの書き出し
	writeHeader();
	// オブジェクトのカウント
	if(countObjects() == 0){ return; }

	// アニメーションリストの書き出し
	writeAnimationList();
	// リンクの書き出し
	writeLink();
}
//------------------------------------------------------------------------------
// ヘッダの書き出し
void BinaryAnimationSaver::writeHeader(){
	writeString("LampBinaryAnimation");
	u_int version = 0x00000900;
	writer_->writeUInt(version);
	// アライメント
	align();
}
//------------------------------------------------------------------------------
// オブジェクトのカウント
int BinaryAnimationSaver::countObjects(){
	// アニメーションカウントクリア
	clearObjectCount();
	// 各アニメーションのカウント
	animationCount_ = manager_->getCount();
	for(int i = 0; i < animationCount_; i++){
		Animation* animation = manager_->get(i);
		if(animation->isAnimationSet()){
			animationSetCount_++;
		}else if(animation->isCameraAnimation()){
			cameraCount_++;
		}else if(animation->isSceneNodeAnimation()){
			sceneNodeCount_++;
		}else if(animation->isCharacterModelAnimation()){
			characterModelCount_++;
		}else{
			ErrorOut("BinaryAnimationSaver::countObjects() "
				"Unsupported animation type");
		}
	}
	// 各アニメーションデータのカウント
	animationDataCount_ = manager_->getDataCount();
	for(int i = 0; i < animationDataCount_; i++){
		AnimationData* data = manager_->getData(i);
		if(data->isCameraAnimationData()){
			cameraDataCount_++;
		}else if(data->isSceneNodeAnimationData()){
			sceneNodeDataCount_++;
		}else if(data->isCharacterModelAnimationData()){
			characterModelDataCount_++;
		}else{
			ErrorOut("BinaryAnimationSaver::countObjects() "
				"Unsupported animation data type");
		}
	}
	return (animationCount_ + animationDataCount_);
}
//------------------------------------------------------------------------------
// アニメーションリスト
//------------------------------------------------------------------------------
// アニメーションリストの書き出し
void BinaryAnimationSaver::writeAnimationList(){
	// アニメーションセットの書き出し
	if(animationSetCount_ != 0){
		int startAddress = startBlock("AnimationSet");
		for(int i = 0; i < animationCount_; i++){
			Animation* animation = manager_->get(i);
			if(animation->isAnimationSet()){
				writeAnimationSet(animation->castAnimationSet());
			}
		}
		endBlock(startAddress, animationSetCount_);
	}
	// カメラアニメーションの書き出し
	if(cameraCount_ != 0){
		int startAddress = startBlock("Camera");
		for(int i = 0; i < animationCount_; i++){
			Animation* animation = manager_->get(i);
			if(animation->isCameraAnimation()){
				writeCameraAnimation(animation->castCameraAnimation());
			}
		}
		endBlock(startAddress, cameraCount_);
	}
	// カメラアニメーションデータの書き出し
	if(cameraDataCount_ != 0){
		int startAddress = startBlock("CameraData");
		for(int i = 0; i < animationDataCount_; i++){
			AnimationData* data = manager_->getData(i);
			if(data->isCameraAnimationData()){
				writeCameraAnimationData(
					data->castCameraAnimationData());
			}
		}
		endBlock(startAddress, cameraDataCount_);
	}
	// シーンノードアニメーションの書き出し
	if(sceneNodeCount_ != 0){
		int startAddress = startBlock("SceneNode");
		for(int i = 0; i < animationCount_; i++){
			Animation* animation = manager_->get(i);
			if(animation->isSceneNodeAnimation()){
				writeSceneNodeAnimation(animation->castSceneNodeAnimation());
			}
		}
		endBlock(startAddress, sceneNodeCount_);
	}
	// シーンノードアニメーションデータの書き出し
	if(sceneNodeDataCount_ != 0){
		int startAddress = startBlock("SceneNodeData");
		for(int i = 0; i < animationDataCount_; i++){
			AnimationData* data = manager_->getData(i);
			if(data->isSceneNodeAnimationData()){
				writeSceneNodeAnimationData(
					data->castSceneNodeAnimationData());
			}
		}
		endBlock(startAddress, sceneNodeDataCount_);
	}
	// キャラクタモデルアニメーションの書き出し
	if(characterModelCount_ != 0){
		int startAddress = startBlock("CharacterModel");
		for(int i = 0; i < animationCount_; i++){
			Animation* animation = manager_->get(i);
			if(animation->isCharacterModelAnimation()){
				writeCharacterModelAnimation(
					animation->castCharacterModelAnimation());
			}
		}
		endBlock(startAddress, characterModelCount_);
	}
	// キャラクタモデルアニメーションデータの書き出し
	if(characterModelDataCount_ != 0){
		int startAddress = startBlock("CharacterModelData");
		for(int i = 0; i < animationDataCount_; i++){
			AnimationData* data = manager_->getData(i);
			if(data->isCharacterModelAnimationData()){
				writeCharacterModelAnimationData(
					data->castCharacterModelAnimationData());
			}
		}
		endBlock(startAddress, characterModelDataCount_);
	}
}
//------------------------------------------------------------------------------
// アニメーションセット
//------------------------------------------------------------------------------
// アニメーションセットの書き出し
void BinaryAnimationSaver::writeAnimationSet(AnimationSet* animation){
	// 名前
	writeString(animation->getName());
	// 有効、無効
	writer_->writeBool(animation->isEnabled());
	// アライメント
	align();
}
//------------------------------------------------------------------------------
// カメラ
//------------------------------------------------------------------------------
// カメラアニメーションの書き出し
void BinaryAnimationSaver::writeCameraAnimation(CameraAnimation* animation){
	// 名前
	writeString(animation->getName());
	// 有効、無効
	writer_->writeBool(animation->isEnabled());
	// ターゲット名
	writeString(animation->getTargetName());
	// アライメント
	align();
}
//------------------------------------------------------------------------------
// カメラアニメーションデータの書き出し
void BinaryAnimationSaver::writeCameraAnimationData(
	CameraAnimationData* data){
	// 名前
	writeString(data->getName());
	// シーケンス数
	int sequenceCount = data->getSequenceCount();
	writer_->writeInt(sequenceCount);
	// アニメーションデータ
	for(int i = 0; i < sequenceCount; i++){
		// 回転
		writeRotationInterpolator(data->getRotation(i));
		// 移動
		writeVectorInterpolator(data->getTranslation(i));
	}
	// ループフラグ
	for(int i = 0; i < sequenceCount; i++){
		writer_->writeBool(data->isLooped(i));
	}
	// アライメント
	align();
}
//------------------------------------------------------------------------------
// シーンノード
//------------------------------------------------------------------------------
// シーンノードアニメーションの書き出し
void BinaryAnimationSaver::writeSceneNodeAnimation(
	SceneNodeAnimation* animation){
	// 名前
	writeString(animation->getName());
	// 有効、無効
	writer_->writeBool(animation->isEnabled());
	// ターゲット名
	writeString(animation->getTargetName());
	// アライメント
	align();
}
//------------------------------------------------------------------------------
// シーンノードアニメーションデータの書き出し
void BinaryAnimationSaver::writeSceneNodeAnimationData(
	SceneNodeAnimationData* data){
	// 名前
	writeString(data->getName());
	// シーケンス数
	int sequenceCount = data->getSequenceCount();
	writer_->writeInt(sequenceCount);
	// アニメーションデータ
	for(int i = 0; i < sequenceCount; i++){
		// スケール
		writeVectorInterpolator(data->getScale(i));
		// 回転
		writeRotationInterpolator(data->getRotation(i));
		// 移動
		writeVectorInterpolator(data->getTranslation(i));
	}
	// ループフラグ
	for(int i = 0; i < sequenceCount; i++){
		writer_->writeBool(data->isLooped(i));
	}
	// アライメント
	align();
}
//------------------------------------------------------------------------------
// キャラクタモデル
//------------------------------------------------------------------------------
// キャラクタモデルアニメーションの書き出し
void BinaryAnimationSaver::writeCharacterModelAnimation(
	CharacterModelAnimation* animation){
	// 名前
	writeString(animation->getName());
	// 有効、無効
	writer_->writeBool(animation->isEnabled());
	// ターゲット名
	writeString(animation->getTargetName());
	// ボーン数
	int boneCount = animation->getBoneCount();
	writer_->writeInt(boneCount);
	// ボーン名
	for(int i = 0; i < boneCount; i++){
		writeString(animation->getBoneName(i));
	}
	// アライメント
	align();
}
//------------------------------------------------------------------------------
// キャラクタモデルアニメーションデータの書き出し
void BinaryAnimationSaver::writeCharacterModelAnimationData(
	CharacterModelAnimationData* data){
	// 名前
	writeString(data->getName());
	// シーケンス数
	int sequenceCount = data->getSequenceCount();
	writer_->writeInt(sequenceCount);
	// ボーン数
	int boneCount = data->getBoneCount();
	writer_->writeInt(boneCount);
	// アニメーションデータ
	for(int i = 0; i < sequenceCount; i++){
		for(int j = 0; j < boneCount; j++){
			// スケール
			writeVectorInterpolator(data->getScale(i, j));
			// 回転
			writeRotationInterpolator(data->getRotation(i, j));
			// 移動
			writeVectorInterpolator(data->getTranslation(i, j));
		}
	}
	// ループフラグ
	for(int i = 0; i < sequenceCount; i++){
		writer_->writeBool(data->isLooped(i));
	}
	// アライメント
	align();
}
//------------------------------------------------------------------------------
// リンク
//------------------------------------------------------------------------------
// リンクの書き出し
void BinaryAnimationSaver::writeLink(){
	// アニメーションセットリンク
	if(animationSetCount_ != 0){
		int startAddress = startBlock("AnimationSetLink");
		for(int i = 0; i < animationCount_; i++){
			Animation* animation = manager_->get(i);
			if(animation->isAnimationSet()){
				writeAnimationSetLink(animation->castAnimationSet());
			}
		}
		endBlock(startAddress, animationSetCount_);
	}
	// カメラリンク
	if(cameraCount_ != 0){
		int startAddress = startBlock("CameraLink");
		for(int i = 0; i < animationCount_; i++){
			Animation* animation = manager_->get(i);
			if(animation->isCameraAnimation()){
				writeCameraAnimationLink(
					animation->castCameraAnimation());
			}
		}
		endBlock(startAddress, cameraCount_);
	}
	// シーンノードリンク
	if(sceneNodeCount_ != 0){
		int startAddress = startBlock("SceneNodeLink");
		for(int i = 0; i < animationCount_; i++){
			Animation* animation = manager_->get(i);
			if(animation->isSceneNodeAnimation()){
				writeSceneNodeAnimationLink(
					animation->castSceneNodeAnimation());
			}
		}
		endBlock(startAddress, sceneNodeCount_);
	}
	// キャラクタモデルリンク
	if(characterModelCount_ != 0){
		int startAddress = startBlock("CharacterModelLink");
		for(int i = 0; i < animationCount_; i++){
			Animation* animation = manager_->get(i);
			if(animation->isCharacterModelAnimation()){
				writeCharacterModelAnimationLink(
					animation->castCharacterModelAnimation());
			}
		}
		endBlock(startAddress, characterModelCount_);
	}
}
//------------------------------------------------------------------------------
// アニメーションセットリンクの書き出し
void BinaryAnimationSaver::writeAnimationSetLink(AnimationSet* animation){
	// 名前
	writeString(animation->getName());
	// 子供
	int childCount = animation->getAnimationCount();
	writer_->writeInt(childCount);
	for(int i = 0; i < childCount; i++){
		writeString(animation->getAnimation(i)->getName());
	}
	// アライメント
	align();
}
//------------------------------------------------------------------------------
// カメラアニメーションリンクの書き出し
void BinaryAnimationSaver::writeCameraAnimationLink(
	CameraAnimation* animation){
	// 名前
	writeString(animation->getName());
	// データの名前
	writeString(animation->getAnimationData()->getName());
	// アライメント
	align();
}
//------------------------------------------------------------------------------
// シーンノードアニメーションリンクの書き出し
void BinaryAnimationSaver::writeSceneNodeAnimationLink(
	SceneNodeAnimation* animation){
	// 名前
	writeString(animation->getName());
	// データの名前
	writeString(animation->getAnimationData()->getName());
	// アライメント
	align();
}
//------------------------------------------------------------------------------
// キャラクタモデルアニメーションリンクの書き出し
void BinaryAnimationSaver::writeCharacterModelAnimationLink(
	CharacterModelAnimation* animation){
	// 名前
	writeString(animation->getName());
	// データの名前
	writeString(animation->getAnimationData()->getName());
	// アライメント
	align();
}
//------------------------------------------------------------------------------
// 値の書き出し
//------------------------------------------------------------------------------
// 文字列の書き出し
void BinaryAnimationSaver::writeString(const String& string){
	int writeSize = string.getSize() + 1;
	writer_->writeInt(writeSize);
	writer_->writeBytes(string.getBytes(), writeSize);
	align();
}
//------------------------------------------------------------------------------
// 三次元ベクトルの書き出し
void BinaryAnimationSaver::writeVector3(const Vector3& vector){
	writer_->writeBytes(vector.array, sizeof(Vector3));
}
//------------------------------------------------------------------------------
// 四元数の書き出し
void BinaryAnimationSaver::writeQuaternion(const Quaternion& quaternion){
	writer_->writeBytes(quaternion.array, sizeof(Quaternion));
}
//------------------------------------------------------------------------------
// ベクトル補間
//------------------------------------------------------------------------------
// ベクトル補間の書き出し
void BinaryAnimationSaver::writeVectorInterpolator(
	VectorInterpolator* interpolator){
	// NULL対応
	if(interpolator == NULL){
		// NULL対応
		writer_->writeInt(0);
	}else if(interpolator->isVectorConstantInterpolator()){
		writer_->writeInt(1);
		writeVectorConstantInterpolator(
			interpolator->castVectorConstantInterpolator());
	}else if(interpolator->isVectorArrayInterpolator()){
		writer_->writeInt(2);
		writeVectorArrayInterpolator(
			interpolator->castVectorArrayInterpolator());
	}else if(interpolator->isVectorLinearInterpolator()){
		writer_->writeInt(3);
		writeVectorLinearInterpolator(
			interpolator->castVectorLinearInterpolator());
	}else{
		ErrorOut("BinaryAnimationSaver::writeVectorInterpolator() "
			"Unsupported interpolater");
	}
}
//------------------------------------------------------------------------------
// ベクトル定数補間の書き出し
void BinaryAnimationSaver::writeVectorConstantInterpolator(
	VectorConstantInterpolator* interpolator){
	// 長さ
	writer_->writeFloat(interpolator->getLength());
	// 値
	writeVector3(interpolator->getValue());
}
//------------------------------------------------------------------------------
// ベクトル配列補間の書き出し
void BinaryAnimationSaver::writeVectorArrayInterpolator(
	VectorArrayInterpolator* interpolator){
	// 値
	int size = interpolator->getSize();
	writer_->writeInt(size);
	for(int i = 0; i < size; i++){ writeVector3(interpolator->getValue(i)); }
}
//------------------------------------------------------------------------------
// ベクトル線形補間の書き出し
void BinaryAnimationSaver::writeVectorLinearInterpolator(
	VectorLinearInterpolator* interpolator){
	// 値
	int keyCount = interpolator->getKeyCount();
	writer_->writeInt(keyCount);
	for(int i = 0; i < keyCount; i++){
		writer_->writeFloat(interpolator->getTime(i));
		writeVector3(interpolator->getValue(i));
	}
}
//------------------------------------------------------------------------------
// 回転補間
//------------------------------------------------------------------------------
// 回転補間の書き出し
void BinaryAnimationSaver::writeRotationInterpolator(
	RotationInterpolator* interpolator){
	if(interpolator == NULL){
		// NULL対応
		writer_->writeInt(0);
	}else if(interpolator->isRotationConstantInterpolator()){
		writer_->writeInt(1);
		writeRotationConstantInterpolator(
			interpolator->castRotationConstantInterpolator());
	}else if(interpolator->isEulerArrayInterpolator()){
		writer_->writeInt(2);
		writeEulerArrayInterpolator(
			interpolator->castEulerArrayInterpolator());
	}else if(interpolator->isQuaternionArrayInterpolator()){
		writer_->writeInt(3);
		writeQuaternionArrayInterpolator(
			interpolator->castQuaternionArrayInterpolator());
	}else if(interpolator->isQuaternionLinearInterpolator()){
		writer_->writeInt(4);
		writeQuaternionLinearInterpolator(
			interpolator->castQuaternionLinearInterpolator());
	}else{
		ErrorOut("BinaryAnimationSaver::writeRotationInterpolator() "
			"Unsupported interpolater");
	}
}
//------------------------------------------------------------------------------
// 回転定数補間の書き出し
void BinaryAnimationSaver::writeRotationConstantInterpolator(
	RotationConstantInterpolator* interpolator){
	// 長さ
	writer_->writeFloat(interpolator->getLength());
	// 値
	writeQuaternion(interpolator->getQuaternion());
}
//------------------------------------------------------------------------------
// オイラー回転配列補間の書き出し
void BinaryAnimationSaver::writeEulerArrayInterpolator(
	EulerArrayInterpolator* interpolator){
	// 値
	int size = interpolator->getSize();
	writer_->writeInt(size);
	for(int i = 0; i < size; i++){ writeVector3(interpolator->getValue(i)); }
}
//------------------------------------------------------------------------------
// 四元数回転配列補間の書き出し
void BinaryAnimationSaver::writeQuaternionArrayInterpolator(
	QuaternionArrayInterpolator* interpolator){
	// 値
	int size = interpolator->getSize();
	writer_->writeInt(size);
	for(int i = 0; i < size; i++){ writeQuaternion(interpolator->getValue(i)); }
}
//------------------------------------------------------------------------------
// 四元数回転線形補間の書き出し
void BinaryAnimationSaver::writeQuaternionLinearInterpolator(
	QuaternionLinearInterpolator* interpolator){
	// 値
	int keyCount = interpolator->getKeyCount();
	writer_->writeInt(keyCount);
	for(int i = 0; i < keyCount; i++){
		writer_->writeFloat(interpolator->getTime(i));
		writeQuaternion(interpolator->getValue(i));
	}
}
//------------------------------------------------------------------------------
// ユーティリティ
//------------------------------------------------------------------------------
// アライメントを取る
void BinaryAnimationSaver::align(){
	// 変更した場合はendBlock()に修正が必要
	writer_->align(16);
}
//------------------------------------------------------------------------------
// ブロックの開始
int BinaryAnimationSaver::startBlock(const String& blockName){
	writeString(blockName);
	// ブロックサイズの予約
	int blockAddress = writer_->getPosition();
	writer_->writeInt(0);
	// オブジェクト数の予約
	writer_->writeInt(0);
	align();
	return blockAddress;
}
//------------------------------------------------------------------------------
// ブロックの終了
void BinaryAnimationSaver::endBlock(int blockStartAddress, int objectCount){
	align();
	int blockEndPosition = writer_->getPosition();
	// ブロックヘッダ内容をブロックサイズから引く、アライメント依存
	int blockSize = blockEndPosition - blockStartAddress - (sizeof(int) * 4);
	writer_->setPosition(blockStartAddress);
	// ブロックサイズ書き込み
	writer_->writeInt(blockSize);
	// オブジェクト数書き込み
	writer_->writeInt(objectCount);
	// カーソルを戻す
	writer_->setPosition(blockEndPosition);
}
//------------------------------------------------------------------------------
} // End of namespace Lamp
//------------------------------------------------------------------------------
