//------------------------------------------------------------------------------
// 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 "Graphics/SceneNode/SceneNode.h"
#include "Graphics/Scene/Scene.h"
#include "Graphics/Camera/Camera.h"
#include "Graphics/SceneNode/SceneNodeManager.h"
#include "Graphics/Model/Model.h"

namespace Lamp{

//------------------------------------------------------------------------------
// コンストラクタ
SceneNode::SceneNode(const String& name, Scene* scene) :
	SceneObject(name, scene), worldMatrix_(Matrix34::unit),
	parent_(NULL), enabled_(true), globalEnabled_(true), globalScaled_(false),
	globalChanged_(true){
}
//------------------------------------------------------------------------------
// デストラクタ
SceneNode::~SceneNode(){
}
//------------------------------------------------------------------------------
// コピー
SceneNode* SceneNode::copy(u_int copyMask) const{
	SceneNodeManager* manager = scene_->getSceneNodeManager();
	SceneNode* destination = manager->createSceneNode(manager->rename(name_));
	copySceneNodeValue(destination, copyMask);
	return destination;
}
//------------------------------------------------------------------------------
// 再帰的破棄
int SceneNode::recursiveDestroy(SceneNode* sceneNode){
	Assert(sceneNode != sceneNode->getScene()->getRootNode());
	int result = recursiveDestroyChildren(sceneNode);
	// 親から削除
	SceneNode* parent = sceneNode->getParent();
	if(parent != NULL){ parent->removeSceneNode(sceneNode); }
	// 引数の破棄
	SceneNodeManager* manager = sceneNode->getScene()->getSceneNodeManager();
	if(manager->destroy(sceneNode) == 0){ result++; }
	return result;
}
//------------------------------------------------------------------------------
// 子の再帰的破棄
int SceneNode::recursiveDestroyChildren(SceneNode* sceneNode){
	Assert(sceneNode != NULL);
	int result = 0;
	// シーンリーフの破棄
	for(int i = sceneNode->getSceneLeafCount() - 1; i >= 0; i--){
		result += SceneLeaf::recursiveDestroy(sceneNode->getSceneLeaf(i));
	}
	// シーンノードの破棄
	for(int i = sceneNode->getSceneNodeCount() - 1; i >= 0; i--){
		result += recursiveDestroy(sceneNode->getSceneNode(i));
	}
	return result;
}
//------------------------------------------------------------------------------
// シーンノードの値コピー
void SceneNode::copySceneNodeValue(
	SceneNode* destination, u_int copyMask) const{
	destination->axis_ = axis_;
	destination->setEnabled(enabled_);
	// シーンリーフのコピー
	int sceneLeafCount = getSceneLeafCount();
	for(int i = 0; i < sceneLeafCount; i++){
		destination->addSceneLeaf(getSceneLeaf(i)->copy(copyMask));
	}
	// シーンノードのコピー
	int sceneNodeCount = getSceneNodeCount();
	for(int i = 0; i < sceneNodeCount; i++){
		destination->addSceneNode(getSceneNode(i)->copy(copyMask));
	}
}
//------------------------------------------------------------------------------
// 走査
void SceneNode::traverse(){
	const Vector3& cameraPosition = scene_->getCurrentCamera()->getPosition();
	if(parent_ == NULL){
		traverse(Matrix34::unit, cameraPosition, true, false, false);
	}else{
// parent_->traverseでは？微妙2通り用意する？
		traverse(parent_->getWorldMatrix(), cameraPosition,
			parent_->isGlobalEnabled(), parent_->isGlobalScaled(),
			parent_->isGlobalChanged());
	}
}
//------------------------------------------------------------------------------
// 走査
void SceneNode::traverse(const Matrix34& parentMatrix,
	const Vector3& cameraPosition, bool parentEnabled, bool parentScaled,
	bool parentChanged){
	bool preGlobalEnabled = isGlobalEnabled();
	bool globalEnabled = (parentEnabled && isEnabled());
	setGlobalEnabled(globalEnabled);
	// 前回がdisabledでdisabledのままならトラバースしない
	if((!preGlobalEnabled) && (!globalEnabled)){ return; }

	// 行列の計算
	bool globalChanged = calcMatrix(parentMatrix, parentChanged);
	setGlobalChanged(globalChanged);
	// グローバルスケールフラグ設定
	bool globalScaled = (parentScaled || isScaled());
	setGlobalScaled(globalScaled);
	// シーンノードを呼ぶ
	int sceneNodeCount = getSceneNodeCount();
	for(int i = 0; i < sceneNodeCount; i++){
		getSceneNode(i)->traverse(worldMatrix_, cameraPosition,
			globalEnabled, globalScaled, globalChanged);
	}
	// シーンリーフを呼ぶ
	int sceneLeafCount = getSceneLeafCount();
	for(int i = 0; i < sceneLeafCount; i++){
		getSceneLeaf(i)->traverse(worldMatrix_,
			globalEnabled, globalScaled, globalChanged);
	}
}
//------------------------------------------------------------------------------
// 行列の計算
bool SceneNode::calcMatrix(const Matrix34& parentMatrix, bool parentChanged){
	// ローカル行列に変更があれば計算して変更有りを返す
	if(axis_.buildMatrix()){
		worldMatrix_ = parentMatrix * getLocalMatrix();
		return true;
	}
	// ローカル行列に変更が無く、親に変更があれば乗算して変更有りを返す
	if(parentChanged){
		// 親との乗算
		worldMatrix_ = parentMatrix * getLocalMatrix();
		return true;
	}
	// ローカル行列に変更が無く、親にも変更が無ければ変更無しを返す
	Assert((parentMatrix * getLocalMatrix()).epsilonEquals(
		worldMatrix_, 0.0001f));
	return false;
}
//------------------------------------------------------------------------------
// シーンリーフの追加
void SceneNode::addSceneLeaf(SceneLeaf* sceneLeaf){
	sceneLeaf->setParent(this);
	sceneLeafs_.add(sceneLeaf);
}
//------------------------------------------------------------------------------
// シーンリーフの削除
void SceneNode::removeSceneLeaf(SceneLeaf* sceneLeaf){
	sceneLeaf->removeParent(this);
	sceneLeafs_.removeByValue(sceneLeaf);
}
//------------------------------------------------------------------------------
} // End of namespace Lamp
//------------------------------------------------------------------------------
