/*
 * Copyright (C) 2004 Clemens Fuchslocher
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program 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 General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
 * USA
 *
 */

#include <math.h>

#include <GL/gl.h>
#include <GL/glu.h>

#include "Utility.h"
#include "GlCompileSmoothVisitor.h"


namespace nsSceneGraph {

GlCompileSmoothVisitor::~GlCompileSmoothVisitor(void)
{
	DEBUG_MESSAGE("GlCompileSmoothVisitor::~GlCompileSmoothVisitor()");
}


void GlCompileSmoothVisitor::visit(VertexArray *array)
{
	DEBUG_MESSAGE("GlCompileSmoothVisitor::visit(VertexArray *array)");

	if (array->isNodeCompiled())
	{
		return;
	}

	array->setCompiledStatus(true);
	array->setCompiledDisplayList(glGenLists(1));
	glNewList(array->getCompiledDisplayList(), GL_COMPILE);

	if (array->normalIndex.size() == 0)
	{
		calculateSmoothNormals(array);
	}

	unsigned int index[array->vertexIndex.size() * 3];
	float vertex[array->vertexArray.size() * 3];
	float normals[array->normalArray.size() * 3];

	enum
	{
		X = 0,
		Y = 1,
		Z = 2,
	};

	enum
	{
		A = 0,
		B = 1,
		C = 2
	};

	int end = array->vertexIndex.size();
	for (int n = 0; n < end; n++)
	{
		int indexA = array->vertexIndex[n].a;
		int indexB = array->vertexIndex[n].b;
		int indexC = array->vertexIndex[n].c;

		vertex[indexA * 3 + X] = array->vertexArray[indexA].x;
		vertex[indexA * 3 + Y] = array->vertexArray[indexA].y;
		vertex[indexA * 3 + Z] = array->vertexArray[indexA].z;

		vertex[indexB * 3 + X] = array->vertexArray[indexB].x;
		vertex[indexB * 3 + Y] = array->vertexArray[indexB].y;
		vertex[indexB * 3 + Z] = array->vertexArray[indexB].z;

		vertex[indexC * 3 + X] = array->vertexArray[indexC].x;
		vertex[indexC * 3 + Y] = array->vertexArray[indexC].y;
		vertex[indexC * 3 + Z] = array->vertexArray[indexC].z;

		index[n * 3 + A] = indexA;
		index[n * 3 + B] = indexB;
		index[n * 3 + C] = indexC;
	}

	end = array->normalIndex.size();
	for (int n = 0; n < end; n++)
	{
		int indexA = array->normalIndex[n].a;
		int indexB = array->normalIndex[n].b;
		int indexC = array->normalIndex[n].c;

		normals[indexA * 3 + X] = array->normalArray[indexA].x;
		normals[indexA * 3 + Y] = array->normalArray[indexA].y;
		normals[indexA * 3 + Z] = array->normalArray[indexA].z;

		normals[indexB * 3 + X] = array->normalArray[indexB].x;
		normals[indexB * 3 + Y] = array->normalArray[indexB].y;
		normals[indexB * 3 + Z] = array->normalArray[indexB].z;

		normals[indexC * 3 + X] = array->normalArray[indexC].x;
		normals[indexC * 3 + Y] = array->normalArray[indexC].y;
		normals[indexC * 3 + Z] = array->normalArray[indexC].z;
	}

	glNormalPointer(GL_FLOAT, 0, normals);
	glVertexPointer(3, GL_FLOAT, 0, vertex);
	glDrawElements(GL_TRIANGLES, array->vertexIndex.size() * 3, GL_UNSIGNED_INT, index);

	glEndList();

	GL_ERROR_CHECK
}


/*
 * Bei einem "Drahtgittermodell" kann ein Vertex teil mehrerer Polygone
 * sein. D.h. unterschiedliche Polygone teilen sich einen Vertex mit-
 * einander. Diese Vertices besitzten in jedem dieser Polygone einen
 * anderen Normalenvektor. Das Problem dabei ist, dass sich mit Hilfe
 * eines VertexArrays nur ein Normalenvektor pro Vertex über die OpenGL
 * Funktion glNormalPointer() festlegen lässt, da folgendes gilt:
 *      index = vertexIndex[n]
 *      vertex = vertexArray[index]
 *      normal = normalArray[index]
 *
 * Allerdings stellt dies nur bei GL_FLAT ein Problem dar, bei GL_SMOOTH
 * wird der Normalenvektor eines Vertex über die Normalenvektoren der
 * umliegenden Polygone gemittelt. Und somit besitzt ein Vertex nur einen
 * Normalenvektor egal von wievelen Polygonen der Vertex verwendet wird.
 */
void GlCompileSmoothVisitor::calculateSmoothNormals(VertexArray *array)
{
	array->normalIndex.reserve(array->vertexIndex.size());
	array->normalArray.reserve(array->vertexArray.size());

	Index3I index(-42, -42, -42);
	Vector3F vector(42, 42, 42);

	int end = array->vertexIndex.size();
	for (int n = 0; n < end; n++)
	{
		array->normalIndex.push_back(index);
	}

	end = array->vertexArray.size();
	for (int n = 0; n < end; n++)
	{
		array->normalArray.push_back(vector);
	}

	// Calculate one normal for each face.
	std::vector<Vector3F> normalPerTriangle;
	this->calculateOneNormalForEachFace(array, &normalPerTriangle);

	/*
	 * Create a table for triangle lookup. So we can determine -- in a
	 * very fast way -- which triangles are containing a given vertex.
	 * The complexity of the previous solution was O(N^2). But now the
	 * use of a multimap is resulting in O(log N). Cool! :-)
	 */

	/* GlCompileSmoothVisitorMemoryLeak.cpp */
	VertexTriangleTable vertexTriangleTable;

	end = array->vertexIndex.size();
	for (int n = 0; n < end; n++)
	{
		VertexTriangleTableEntry entry = { n, 0 };
		Vertex3F vertex;

		vertex = array->vertexArray[array->vertexIndex[n].a];
		vertexTriangleTable.insert(VertexTriangleTablePair(vertex, entry));

		vertex = array->vertexArray[array->vertexIndex[n].b];
		entry.index++;
		vertexTriangleTable.insert(VertexTriangleTablePair(vertex, entry));

		vertex = array->vertexArray[array->vertexIndex[n].c];
		entry.index++;
		vertexTriangleTable.insert(VertexTriangleTablePair(vertex, entry));
	}

	/*
	 * Calculate one normal per vertex for GL_SMOOTH shading.
	 *
	 * Interactive Computer Graphics, Second Edition(Addision Wesley)
	 *    Chapter 6: Shading
	 *    6.5.2 Interpolative and Gouraud Shading, Page 260
	 *
	 * OpenGL Programming Guide, Third Edition(Addision Wesley)
	 *    Appendix E: Calculating Normal Vectors
	 *    Finding Normals from Polygonal Data, Page 667
	 */
	end = array->vertexArray.size();
	for (int n = 0; n < end; n++)
	{
		Vertex3F *vertex = &array->vertexArray[n];

		std::vector<VertexTriangleTableEntry> result;
		VertexTriangleTable::iterator iter = vertexTriangleTable.lower_bound(*vertex);
		VertexTriangleTable::iterator end = vertexTriangleTable.upper_bound(*vertex);
		for (; iter != end; iter++)
		{
			const Vertex3F *v = &iter->first;
			// MARK 1 (See MARK 0 comment in VertexArray.h.)
			if ((v->x != vertex->x) || (v->y != vertex->y) || (v->z != vertex->z))
			{
				continue;
			}

			result.push_back(iter->second);
		}

		// Calculate average normal for the current vertex.
		Vector3F normal;

		int e = result.size();
		for (int nn = 0; nn < e; nn++)
		{
			normal.x += normalPerTriangle[result[nn].triangle].x;
			normal.y += normalPerTriangle[result[nn].triangle].y;
			normal.z += normalPerTriangle[result[nn].triangle].z;
		}

		double magnitude = sqrt(
			normal.x * normal.x +
			normal.y * normal.y +
			normal.z * normal.z
		);

		normal.x /= magnitude;
		normal.y /= magnitude;
		normal.z /= magnitude;

		for (int nn = 0; nn < e; nn++)
		{
			if (result[nn].index == 0) // a
			{
				array->normalIndex[result[nn].triangle].a = array->vertexIndex[result[nn].triangle].a;

				array->normalArray[array->vertexIndex[result[nn].triangle].a].x = normal.x;
				array->normalArray[array->vertexIndex[result[nn].triangle].a].y = normal.y;
				array->normalArray[array->vertexIndex[result[nn].triangle].a].z = normal.z;
			}
			else if (result[nn].index == 1) // b
			{
				array->normalIndex[result[nn].triangle].b = array->vertexIndex[result[nn].triangle].b;

				array->normalArray[array->vertexIndex[result[nn].triangle].b].x = normal.x;
				array->normalArray[array->vertexIndex[result[nn].triangle].b].y = normal.y;
				array->normalArray[array->vertexIndex[result[nn].triangle].b].z = normal.z;
			}
			else if (result[nn].index == 2) // c
			{
				array->normalIndex[result[nn].triangle].c = array->vertexIndex[result[nn].triangle].c;

				array->normalArray[array->vertexIndex[result[nn].triangle].c].x = normal.x;
				array->normalArray[array->vertexIndex[result[nn].triangle].c].y = normal.y;
				array->normalArray[array->vertexIndex[result[nn].triangle].c].z = normal.z;
			}
		}
	}
}


}

