//------------------------------------------------------------------------------
// 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 "Collision/InputOutput/TextCollisionLoader.h"
#include "Core/InputOutput/TextFileReader.h"
#include "Core/InputOutput/StreamTokenizer.h"
#include "Core/InputOutput/FilePath.h"
#include "Collision/System/CollisionScene.h"
#include "Collision/System/CollisionNode.h"
#include "Collision/Leaf/StaticSphereCollision.h"
#include "Collision/Leaf/StaticDeformedMeshCollision.h"

namespace Lamp{

//------------------------------------------------------------------------------
// コンストラクタ
TextCollisionLoader::TextCollisionLoader(){
}
//------------------------------------------------------------------------------
// デストラクタ
TextCollisionLoader::~TextCollisionLoader(){
}
//------------------------------------------------------------------------------
// ロード
void TextCollisionLoader::load(const String& filePath, CollisionScene* scene){
	load(filePath, scene, scene->getRootNode());
}
//------------------------------------------------------------------------------
// ロード
void TextCollisionLoader::load(const String& filePath, CollisionScene* scene,
	CollisionNode* node){
	FilePath path(filePath);
	if(!path.existFile()){
		ErrorOut("TextCollisionLoader::load() ファイルが見つかりませんでした %s",
			filePath.getBytes());
		return;
	}
	TextFileReader* textFileReader = new TextFileReader(filePath);
	load(textFileReader, scene, node);
	delete textFileReader;
}
//------------------------------------------------------------------------------
// ロード
void TextCollisionLoader::load(TextReader* textReader, CollisionScene* scene){
	load(textReader, scene, scene->getRootNode());
}
//------------------------------------------------------------------------------
// ロード
void TextCollisionLoader::load(TextReader* textReader, CollisionScene* scene,
	CollisionNode* node){
	// 初期化
	tokenizer_ = new StreamTokenizer(textReader);
	scene_ = scene;
	rootNode_ = node;

	// ルートノードを無効にしておく
	bool rootNodeEnabled = rootNode_->isEnabled();
	rootNode_->setEnabled(false);

	// ヘッダの読み込み
	readHeader();

	while(true){
		// トークン読み終わり
		if(!tokenizer_->nextToken()){ break; }
		String chunkName = tokenizer_->getToken();
		if(chunkName == "CollisionNode"){
			readCollisionNodeList();
		}else if(chunkName == "CollisionLeaf"){
			readCollisionLeafList();
		}else if(chunkName == "CollisionNodeLink"){
			readCollisionLinkList();
		}else{
			ErrorOut("TextCollisionLoader::load() "
				"invalid chunk %s (line %d)",
				chunkName.getBytes(), tokenizer_->getLineNumber());
		}
	}
	delete tokenizer_;

	rootNode_->setEnabled(rootNodeEnabled);
}
///------------------------------------------------------------------------------
// ヘッダの読み込み
void TextCollisionLoader::readHeader(){
	skipWord("Header");
	openChunk();
	skipWord("type");
	skipWord("LampTextCollisionFormat");
	skipWord("version");
	skipWord("0_1_0");
	closeChunk();
}
//------------------------------------------------------------------------------
// コリジョンノード
//------------------------------------------------------------------------------
// コリジョンノードリストの読み込み
void TextCollisionLoader::readCollisionNodeList(){
	openChunk();
	while(true){
		String token = readToken("readCollisionNodeList()");
		if(token == "}"){ break; }
		readCollisionNode(token);
	}
}
//------------------------------------------------------------------------------
// コリジョンノードの読み込み
void TextCollisionLoader::readCollisionNode(const String& name){
	CollisionNode* node = scene_->createCollisionNode(name);
	openChunk();
	// スケール
	skipWord("scale");
	node->setScale(readVector3());
	// 回転
	skipWord("rotation");
	node->setRotationXYZ(readVector3());
	// 移動
	skipWord("translation");
	node->setTranslation(readVector3());
	// 有効、無効フラグ
	skipWord("enabled");
	node->setEnabled(readBool());
	closeChunk();
}
//------------------------------------------------------------------------------
// コリジョンリーフ
//------------------------------------------------------------------------------
// コリジョンリーフリストの読み込み
void TextCollisionLoader::readCollisionLeafList(){
	openChunk();
	while(true){
		String token = readToken("readCollisionLeafList()");
		if(token == "}"){ break; }
		openChunk();
		skipWord("type");
		String type = readToken("readCollisionLeafList()");
		if(type == "StaticSphere"){
			readStaticSphere(token);
		}else if(type == "StaticDeformedMesh"){
			readStaticDeformedMesh(token);
		}else{
			ErrorOut("TextCollisionLoader::readCollisionLeafList() "
				"不正なタイプです %s (line %d)",
				type.getBytes(), tokenizer_->getLineNumber());
		}
		closeChunk();
	}
}
//------------------------------------------------------------------------------
// 静的球コリジョンの読み込み
void TextCollisionLoader::readStaticSphere(const String& name){
	StaticSphereCollision* sphere = scene_->createStaticSphereCollision(name);
	// コリジョンマスク
	skipWord("collisionMask");
	sphere->setCollisionMask(readUInt());
	// 有効、無効フラグ
	skipWord("enabled");
	sphere->setEnabled(readBool());
	// 球
	skipWord("sphere");
	sphere->setSphere(readSphere());
}
//------------------------------------------------------------------------------
// 静的変形メッシュコリジョンの読み込み
void TextCollisionLoader::readStaticDeformedMesh(const String& name){
	StaticDeformedMeshCollision* mesh =
		scene_->createStaticDeformedMeshCollision(name);
	// コリジョンマスク
	skipWord("collisionMask");
	mesh->setCollisionMask(readUInt());
	// 有効、無効フラグ
	skipWord("enabled");
	mesh->setEnabled(readBool());
	// バウンディングボックス
	skipWord("boundingBox");
	mesh->setBoundingBox(readAxisAlignedBox());
	// バウンディングスフィア
	skipWord("boundingSphere");
	mesh->setBoundingSphere(readSphere());
	// トライアングル数
	skipWord("triangleCount");
	int triangleCount = readInt();
	mesh->setTriangleCount(triangleCount);
	openChunk();
	for(int i = 0; i < triangleCount; i++){
		mesh->setTriangle(i, readTriangle());
	}
	closeChunk();
}
//------------------------------------------------------------------------------
// コリジョンリンク
//------------------------------------------------------------------------------
// コリジョンリンクリストの読み込み
void TextCollisionLoader::readCollisionLinkList(){
	openChunk();
	while(true){
		String token = readToken("readCollisionLinkList()");
		if(token == "}"){ break; }
		readCollisionLink(token);
	}
}
//------------------------------------------------------------------------------
// コリジョンリンクの読み込み
void TextCollisionLoader::readCollisionLink(const String& name){
	CollisionNode* node;
	if(name.equals("RootNode")){
		node = rootNode_;
	}else{
		node = scene_->searchNode(name);
		if(node == NULL){
			ErrorOut("TextCollisionLoader::readCollisionLink() "
				"親ノードが見つかりません %s (line %d)",
				name.getBytes(), tokenizer_->getLineNumber());
		}
	}

	openChunk();
	while(true){
		String token = readToken("readCollisionLink()");
		if(token == "}"){ break; }
		if(token == "node"){
			String childName = readToken("readCollisionLink()");
			CollisionNode* child = scene_->searchNode(childName);
			if(child == NULL){
				ErrorOut("TextCollisionLoader::readCollisionLink() "
					"子ノードが見つかりません %s (line %d)",
					childName.getBytes(), tokenizer_->getLineNumber());
			}
			node->addChild(child);
		}else if(token == "leaf"){
			String childName = readToken("readCollisionLink()");
			CollisionLeaf* child = scene_->searchLeaf(childName);
			if(child == NULL){
				ErrorOut("TextCollisionLoader::readCollisionLink() "
					"子リーフが見つかりません %s (line %d)",
					childName.getBytes(), tokenizer_->getLineNumber());
			}
			node->addChild(child);
		}else{
			ErrorOut("TextCollisionLoader::readCollisionLink() "
				"不正なトークンです %s (line %d)",
				token.getBytes(), tokenizer_->getLineNumber());
		}
	}
}
//------------------------------------------------------------------------------
// 値の読み込み
//------------------------------------------------------------------------------
// bool値の読み込み
bool TextCollisionLoader::readBool(){
	String token = readToken("readBool()");
	if(token == "true"){ return true; }
	else if(token == "false"){ return false; }
	ErrorOut("TextCollisionLoader::readBool() 不正なトークンです %s (line %d)",
		token.getBytes(), tokenizer_->getLineNumber());
	return false;
}
//------------------------------------------------------------------------------
// int値の読み込み
int TextCollisionLoader::readInt(){
	String token = readToken("readInt()");
	int value = 0;
	bool result = token.parseInt(&value);
	if(!result){
		ErrorOut("TextCollisionLoader::readInt() "
			"不正なトークンです %s (line %d)",
			token.getBytes(), tokenizer_->getLineNumber());
	}
	return value;
}
//------------------------------------------------------------------------------
// u_int値の読み込み
u_int TextCollisionLoader::readUInt(){
	String token = readToken("readInt()");
	u_int value = 0;
	bool result = token.parseUInt(&value);
	if(!result){
		ErrorOut("TextCollisionLoader::readUInt() "
			"不正なトークンです %s (line %d)",
			token.getBytes(), tokenizer_->getLineNumber());
	}
	return value;
}
//------------------------------------------------------------------------------
// float値の読み込み
float TextCollisionLoader::readFloat(){
	String token = readToken("readFloat()");
	float value = 0.f;
	bool result = token.parseFloat(&value);
	if(!result){
		ErrorOut("TextCollisionLoader::readFloat() "
			"不正なトークンです %s (line %d)",
			token.getBytes(), tokenizer_->getLineNumber());
	}
	return value;
}
//------------------------------------------------------------------------------
// Vector3値の読み込み
Vector3 TextCollisionLoader::readVector3(){
	Vector3 result;
	openChunk();
	result.x = readFloat();
	result.y = readFloat();
	result.z = readFloat();
	closeChunk();
	return result;
}
//------------------------------------------------------------------------------
// 軸沿いボックスの読み込み
AxisAlignedBox TextCollisionLoader::readAxisAlignedBox(){
	AxisAlignedBox result;
	Vector3 minimum, maximum;
	openChunk();
	minimum.x = readFloat();
	minimum.y = readFloat();
	minimum.z = readFloat();
	closeChunk();
	openChunk();
	maximum.x = readFloat();
	maximum.y = readFloat();
	maximum.z = readFloat();
	closeChunk();
	result.set(minimum, maximum);
	return result;
}
//------------------------------------------------------------------------------
// 球の読み込み
Sphere TextCollisionLoader::readSphere(){
	Sphere result;
	Vector3 vector;
	openChunk();
	vector.x = readFloat();
	vector.y = readFloat();
	vector.z = readFloat();
	result.setCenter(vector);
	result.setRadius(readFloat());
	closeChunk();
	return result;
}
//------------------------------------------------------------------------------
// 三角の読み込み
Triangle TextCollisionLoader::readTriangle(){
	Triangle result;
	Vector3 vector;
	for(int i = 0; i < 3; i++){
		openChunk();
		vector.x = readFloat();
		vector.y = readFloat();
		vector.z = readFloat();
		result.setVertex(i, vector);
		closeChunk();
	}
	return result;
}
//------------------------------------------------------------------------------
// ユーティリティ
//------------------------------------------------------------------------------
// トークンの読み込み
String TextCollisionLoader::readToken(const String& caller){
	bool hasNext = tokenizer_->nextToken();
	if(!hasNext){
		ErrorOut("TextCollisionLoader::%s トークンがありません (line %d)",
			caller.getBytes(), tokenizer_->getLineNumber());
	}
	return tokenizer_->getToken();
}
//------------------------------------------------------------------------------
// 指定ワードの読み飛ばし
void TextCollisionLoader::skipWord(const String& word){
	String token = readToken("skipWord()");
	if(token != word){
		ErrorOut("TextCollisionLoader::skipWord(%s) "
			"不正なトークンです %s (line %d)",
			word.getBytes(), token.getBytes(), tokenizer_->getLineNumber());
	}
}
//------------------------------------------------------------------------------
// チャンクの読み飛ばし
void TextCollisionLoader::skipChunk(){
	int chunkLine = tokenizer_->getLineNumber();
	int chunkCounter = 0;
	while(true){
		bool hasNext = tokenizer_->nextToken();
		if(!hasNext){
			ErrorOut("TextCollisionLoader::skipChunk(%d) "
				"トークンがありません (line %d)",
				chunkLine, tokenizer_->getLineNumber());
		}
		String token = tokenizer_->getToken();
		if(token == "{"){
			chunkCounter++;
		}else if(token == "}"){
			chunkCounter--;
			// デクリメント時にループを抜けるチェックを行う
			if(chunkCounter == 0){ break; }
			if(chunkCounter < 0){
				ErrorOut("TextCollisionLoader::skipChunk(%d) "
					"不正なチャンクです (line %d)",
					chunkLine, tokenizer_->getLineNumber());
			}
		}
	}
}
//------------------------------------------------------------------------------
} // End of namespace Lamp
//------------------------------------------------------------------------------
