//------------------------------------------------------------------------------
// 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/SceneFilter/BuildIndexedTriangleFilter/\
BuildIndexedTriangleFilter.h"
#include "Core/Utility/StringTokenizer.h"
#include "Graphics/MeshData/MeshDataManager.h"

namespace Lamp{

//------------------------------------------------------------------------------
// コンストラクタ
BuildIndexedTriangleFilter::BuildIndexedTriangleFilter(Scene* scene) :
	SceneFilterInterface(scene){
}
//------------------------------------------------------------------------------
// デストラクタ
BuildIndexedTriangleFilter::~BuildIndexedTriangleFilter(){
}
//------------------------------------------------------------------------------
// フィルタ
bool BuildIndexedTriangleFilter::filter(const String& command){
	StringTokenizer tokenizer_(command);
	if(!tokenizer_.hasMoreTokens()){
		ErrorOut("BuildIndexedTriangleFilter::filter() "
			"Not found filter name");
		return false;
	}
	String filterName = tokenizer_.getNextToken();
	if(filterName != "BuildIndexedTriangle"){
		ErrorOut("BuildIndexedTriangleFilter::filter() "
			"Invalid filter name %s",
			filterName.getBytes());
		return false;
	}
	return filterScene();
}
//------------------------------------------------------------------------------
// シーンのフィルタ
bool BuildIndexedTriangleFilter::filterScene(){
	if(!filterMeshData()){ return false; }
	return true;
}
//------------------------------------------------------------------------------
// メッシュデータのフィルタ
bool BuildIndexedTriangleFilter::filterMeshData(){
	int count = meshDataManager_->getCount();
	// 各メッシュデータのチェック
	for(int i = 0; i < count; i++){
		if(!filterMeshData(meshDataManager_->get(i))){ return false; }
	}
	return true;
}
//------------------------------------------------------------------------------
// メッシュデータのフィルタ
bool BuildIndexedTriangleFilter::filterMeshData(MeshData* meshData){
	if(meshData->getPrimitiveType() != Mesh::triangleList){ return true; }
	clear();
	// 元データの読み出し
	sourceVertexCount_ = meshData->getVertexCount();
	if(sourceVertexCount_ <= 0){ return true; }
	texCoordSetCount_ = meshData->getTexCoordSetCount();
	texCoordTypeArray_ = meshData->getTexCoordTypeArray();
	weightsPerVertex_ = meshData->getWeightsPerVertex();
	bonesPerVertex_ = meshData->getBonesPerVertex();
	sourcePosition_ = meshData->getPositionArray();
	sourceNormal_ = meshData->getNormalArray();
	sourceColor_ = meshData->getColorArray();
	sourceTexCoord_ = meshData->getTexCoordArray();
	sourceBoneIndex_ = meshData->getBoneIndexArray();
	sourceWeight_ = meshData->getWeightArray();
	// バッファの確保
	allocateBuffer();
	// インデックストライアングルの構築
	if(!buildIndexedTriangle()){
		freeBuffer();
		return false;
	}
	// データの設定
	meshData->setPrimitiveType(Mesh::indexedTriangleList);
	meshData->setVertexIndexCount(sourceVertexCount_);
	for(int i = 0; i < sourceVertexCount_; i++){
		meshData->setVertexIndex(i, indices_[i]);
	}
	meshData->setVertexCount(vertexCount_);
	meshData->setTexCoordSetCount(texCoordSetCount_);
	for(int i = 0; i < texCoordSetCount_; i++){
		meshData->setTexCoordType(i, texCoordTypeArray_[i]);
	}
	meshData->setBonesPerVertex(bonesPerVertex_);
	for(int i = 0; i < vertexCount_; i++){
		meshData->setPosition(i, positions_[i]);
		if(sourceNormal_ != NULL){ meshData->setNormal(i, normals_[i]); }
		if(sourceColor_ != NULL){ meshData->setColor(i, colors_[i]); }
		for(int j = 0; j < texCoordSetCount_; j++){
			meshData->setTexCoord(i, j,
				&(texCoords_[j][texCoordTypeArray_[j] * i]),
				texCoordTypeArray_[j]);
		}
		for(int j = 0; j < bonesPerVertex_; j++){
			int offset = i * bonesPerVertex_;
			if(sourceBoneIndex_ != NULL){
				meshData->setBoneIndex(i, j, boneIndices_[offset + j]);
			}
		}
		for(int j = 0; j < weightsPerVertex_; j++){
			int offset = i * weightsPerVertex_;
			if(sourceWeight_ != NULL){
				meshData->setWeight(i, j, weights_[offset + j]);
			}
		}
	}
	// バッファの解放
	freeBuffer();
	return true;
}
//------------------------------------------------------------------------------
// クリア
void BuildIndexedTriangleFilter::clear(){
	sourceVertexCount_ = 0;
	sourcePosition_ = NULL;
	sourceNormal_ = NULL;
	sourceColor_ = NULL;
	sourceTexCoord_ = NULL;
	sourceBoneIndex_ = NULL;
	sourceWeight_ = NULL;
	vertexCount_ = 0;
	texCoordSetCount_ = 0;
	texCoordTypeArray_ = NULL;
	bonesPerVertex_ = 0;
	weightsPerVertex_ = 0;
	positions_ = NULL;
	normals_ = NULL;
	colors_ = NULL;
	for(int i = 0; i < TexCoord::maxSetCount; i++){ texCoords_[i] = NULL; }
	boneIndices_ = NULL;
	weights_ = NULL;
	indices_ = NULL;
}
//------------------------------------------------------------------------------
// インデックストライアングルの構築
bool BuildIndexedTriangleFilter::buildIndexedTriangle(){
	for(int i = 0; i < sourceVertexCount_; i++){
		// すでに頂点が無いか探す
		int index = findIndex(i);
		// 頂点の追加
		if(index == -1){
			index = vertexCount_;
			vertexCount_++;
			if(index >= maxIndex){
				ErrorOut("BuildIndexedTriangleFilter::buildIndexedTriangle() "
					"Index overflow");
				return false;
			}
			positions_[index] = sourcePosition_[i];
			if(sourceNormal_ != NULL){ normals_[index] = sourceNormal_[i]; }
			if(sourceColor_ != NULL){ colors_[index] = sourceColor_[i]; }
			for(int j = 0; j < texCoordSetCount_; j++){
				int numFloat = texCoordTypeArray_[j];
				for(int k = 0; k < numFloat; k++){
					texCoords_[j][index * numFloat + k] =
						sourceTexCoord_[j][i * numFloat + k];
				}
			}
			for(int j = 0; j < bonesPerVertex_; j++){
				int offset = index * bonesPerVertex_;
				int sourceOffset = i * bonesPerVertex_;
				if(sourceBoneIndex_ != NULL){
					boneIndices_[offset + j] =
						sourceBoneIndex_[sourceOffset + j];
				}
			}
			for(int j = 0; j < weightsPerVertex_; j++){
				int offset = index * weightsPerVertex_;
				int sourceOffset = i * weightsPerVertex_;
				if(sourceWeight_ != NULL){
					weights_[offset + j] =
						sourceWeight_[sourceOffset + j];
				}
			}
		}
		indices_[i] = index;
	}
	return true;
}
//------------------------------------------------------------------------------
// すでに頂点が無いか探す
int BuildIndexedTriangleFilter::findIndex(int source){
	for(int i = 0; i < vertexCount_; i++){
		// 位置の比較
		if(positions_[i].notEpsilonEquals(
			sourcePosition_[source], Math::epsilon)){ continue; }
		// 法線の比較
		if((sourceNormal_ != NULL) &&
			(normals_[i].notEpsilonEquals(
			sourceNormal_[source], Math::epsilon))){ continue; }
		// 頂点カラーの比較
		if((sourceColor_ != NULL) && (colors_[i] != sourceColor_[source])){
			continue;
		}
		// テクスチャ座標の比較
		bool result = false;
		for(int j = 0; j < texCoordSetCount_; j++){
			int numFloat = texCoordTypeArray_[j];
			for(int k = 0; k < numFloat; k++){
				if(Math::abs(texCoords_[j][i * numFloat + k] -
					sourceTexCoord_[j][source * numFloat + k]) >
					Math::epsilon){
					result = true;
				}
			}
		}
		if(result){ continue; }
		// ボーンインデックスの比較
		for(int j = 0; j < bonesPerVertex_; j++){
			int offset = i * bonesPerVertex_;
			int sourceOffset = source * bonesPerVertex_;
			if((sourceBoneIndex_ != NULL) &&
				(boneIndices_[offset + j] !=
				sourceBoneIndex_[sourceOffset+ j])){ result = true; }
		}
		if(result){ continue; }
		// ウェイトの比較
		for(int j = 0; j < weightsPerVertex_; j++){
			int offset = i * weightsPerVertex_;
			int sourceOffset = source * weightsPerVertex_;
			if((sourceWeight_ != NULL) &&
				(Math::abs(weights_[offset + j] -
				sourceWeight_[sourceOffset+ j]) > Math::epsilon)){
				result = true;
			}
		}
		if(result){ continue; }
		return i;
	}
	return -1;
}
//------------------------------------------------------------------------------
// バッファのアロケート
void BuildIndexedTriangleFilter::allocateBuffer(){
	positions_ = new Vector3[sourceVertexCount_];
	if(sourceNormal_ != NULL){ normals_ = new Vector3[sourceVertexCount_]; }
	if(sourceColor_ != NULL){ colors_ = new Color4c[sourceVertexCount_]; }
	for(int i = 0; i < texCoordSetCount_; i++){
		texCoords_[i] = new float[texCoordTypeArray_[i] * sourceVertexCount_];
	}
	if(sourceBoneIndex_ != NULL){
		boneIndices_ = new u_char[sourceVertexCount_ * bonesPerVertex_];
	}
	if(sourceWeight_ != NULL){
		weights_ = new float[sourceVertexCount_ * weightsPerVertex_];
	}
	indices_ = new u_short[sourceVertexCount_];
}
//------------------------------------------------------------------------------
// バッファの解放
void BuildIndexedTriangleFilter::freeBuffer(){
	SafeArrayDelete(indices_);
	SafeArrayDelete(weights_);
	SafeArrayDelete(boneIndices_);
//	SafeArrayDelete(uvs_);
	for(int i = 0; i < texCoordSetCount_; i++){
		SafeArrayDelete(texCoords_[i]);
	}
	SafeArrayDelete(colors_);
	SafeArrayDelete(normals_);
	SafeArrayDelete(positions_);
}
//------------------------------------------------------------------------------
} // End of namespace Lamp
//------------------------------------------------------------------------------
