//------------------------------------------------------------------------------
// 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/System/CollisionScene.h"
#include "Core/Renamer/CountRenamer.h"
#include "Collision/System/CollisionNode.h"
#include "Collision/Leaf/StaticSphereCollision.h"
#include "Collision/Leaf/StaticDeformedMeshCollision.h"

namespace Lamp{

//------------------------------------------------------------------------------
// 生成、破棄
//------------------------------------------------------------------------------
// コンストラクタ
CollisionScene::CollisionScene() : nodeDatabase_(256, 0.75f), nodeArray_(256),
	leafDatabase_(256, 0.75f), leafArray_(256), tick_(0){
	// リネーマの初期化
	nodeRenamer_ = new CountRenamer();
	nodeRenamerCallback_.scene_ = this;
	leafRenamer_ = new CountRenamer();
	leafRenamerCallback_.scene_ = this;
	// ルートノードの初期化
	rootNode_ = createCollisionNode("RootNode");
}
//------------------------------------------------------------------------------
// デストラクタ
CollisionScene::~CollisionScene(){
	Assert(nodeDatabase_.getCount() == 1);
	Assert(nodeArray_.getCount() == 1);
	Assert(leafDatabase_.getCount() == 0);
	Assert(leafArray_.getCount() == 0);
	// ルートノードの後始末
	if(nodeArray_.removeByValue(rootNode_) == -1){
		ErrorOut("CollisionScene::~CollisionScene() "
			"Not found RootNode in array");
	}
	if(nodeDatabase_.remove(rootNode_->getName()) == NULL){
		ErrorOut("CollisionScene::~CollisionScene() "
			"Not found RootNode in hashmap");
	}
	SafeDelete(rootNode_);
	// リネーマの後始末
	SafeDelete(leafRenamer_);
	SafeDelete(nodeRenamer_);
}
//------------------------------------------------------------------------------
// シーン関連
//------------------------------------------------------------------------------
// 走査
void CollisionScene::traverse(){
	// チックのインクリメント
	tick_++;
	getRootNode()->traverse();
}
//------------------------------------------------------------------------------
// 交差
//------------------------------------------------------------------------------
// 球交差
void CollisionScene::intersection(
	IntersectionResult* result, const Sphere& sphere, u_int collisionMask){
	int leafCount = getLeafCount();
	for(int i = 0; i < leafCount; i++){
		getLeaf(i)->intersection(result, sphere, collisionMask);
	}
}
//------------------------------------------------------------------------------
// 球コリジョン交差
void CollisionScene::intersection(IntersectionResult* result,
	StaticSphereCollision* sphere, u_int collisionMask){
	Assert(sphere->isGlobalEnabled());
	int leafCount = getLeafCount();
	for(int i = 0; i < leafCount; i++){
		CollisionLeaf* leaf = getLeaf(i);
		leaf->intersection(result, sphere, collisionMask);
	}
}
//------------------------------------------------------------------------------
// コリジョンノード生成
//------------------------------------------------------------------------------
// コリジョンノードの作成
CollisionNode* CollisionScene::createCollisionNode(const String& name){
	// 名前の長さチェック
	if(name.getSize() == 0){
		ErrorOut("CollisionScene::createCollisionNode() 名前が空文字列です");
		return NULL;
	}
	// 名前の重複チェック
	if(existNodeName(name)){
		ErrorOut("CollisionScene::createCollisionNode() "
			"名前が重複しています %s", name.getBytes());
		return NULL;
	}
	CollisionNode* collisionNode = new CollisionNode(name, this);
	nodeDatabase_.put(name, collisionNode);
	nodeArray_.add(collisionNode);
	return collisionNode;
}
//------------------------------------------------------------------------------
// 静的コリジョンリーフ生成
//------------------------------------------------------------------------------
// 静的球コリジョンの作成
StaticSphereCollision* CollisionScene::createStaticSphereCollision(
	const String& name){
	if(!checkLeafName(name)){ return NULL; }
	StaticSphereCollision* sphere = new StaticSphereCollision(name, this);
	leafDatabase_.put(name, sphere);
	leafArray_.add(sphere);
	return sphere;
}
//------------------------------------------------------------------------------
// 静的変形メッシュコリジョンの作成
StaticDeformedMeshCollision* CollisionScene::createStaticDeformedMeshCollision(
	const String& name){
	if(!checkLeafName(name)){ return NULL; }
	StaticDeformedMeshCollision* mesh =
		new StaticDeformedMeshCollision(name, this);
	leafDatabase_.put(name, mesh);
	leafArray_.add(mesh);
	return mesh;
}
//------------------------------------------------------------------------------
// ユーティリティ
//------------------------------------------------------------------------------
// リーフの名前チェック
bool CollisionScene::checkLeafName(const String& name){
	// 名前の長さチェック
	if(name.getSize() == 0){
		ErrorOut("CollisionScene::checkLeafName() 名前が空文字列です");
		return false;
	}
	// 名前の重複チェック
	if(existLeafName(name)){
		ErrorOut("CollisionScene::checkLeafName() 名前が重複しています %s",
			name.getBytes());
		return false;
	}
	return true;
}
//------------------------------------------------------------------------------
// コリジョンオブジェクト破棄
//------------------------------------------------------------------------------
// ノードオブジェクトの破棄
void CollisionScene::destroyNode(CollisionNode* node){
	Assert(node != rootNode_);
	if(nodeArray_.removeByValue(node) == -1){
		ErrorOut("CollisionScene::~destroyNode() Not found node in array");
	}
	if(nodeDatabase_.remove(node->getName()) == NULL){
		ErrorOut("CollisionScene::~destroyNode() Not found node in hashmap");
	}
	delete node;
}
//------------------------------------------------------------------------------
// リーフオブジェクトの破棄
void CollisionScene::destroyLeaf(CollisionLeaf* leaf){
	if(leafArray_.removeByValue(leaf) == -1){
		ErrorOut("CollisionScene::~destroyLeaf() Not found leaf in array");
	}
	if(leafDatabase_.remove(leaf->getName()) == NULL){
		ErrorOut("CollisionScene::~destroyLeaf() Not found leaf in hashmap");
	}
	delete leaf;
}
//------------------------------------------------------------------------------
// クリア
int CollisionScene::clear(){
	// ルートノードの子をクリア
	int childCount = rootNode_->getChildCount();
	for(int i = childCount - 1; i >= 0; i--){
		rootNode_->removeChild(rootNode_->getChild(i));
	}
	// リーフの削除
	int result = getLeafCount();
	for(int i = getLeafCount() - 1; i >= 0; i--){ delete getLeaf(i); }
	// ルートノード以外を破棄
	result += getNodeCount();
	for(int i = getNodeCount() - 1; i >= 1; i--){ delete getNode(i); }
	leafArray_.clear();
	leafDatabase_.clear();
	nodeArray_.clear();
	nodeDatabase_.clear();
	nodeArray_.add(rootNode_);
	nodeDatabase_.put(rootNode_->getName(), rootNode_);
	return (result - 1);
}
//------------------------------------------------------------------------------
} // End of namespace Lamp
//------------------------------------------------------------------------------
