#include "EPSG/Mesh.h"

#include <stdio.h>
#include <stdlib.h>

#ifdef _WIN32
#include "Windows.h"
#endif

#include "GL/gl.h"

//////////////////////////////////////////////////////////////////////////
// CONSTRUCTORS                                                         //
//////////////////////////////////////////////////////////////////////////

EPSG::Mesh::Mesh( const char* filename )
{
	FILE* meshFile;
	meshFile = fopen( filename, "rt" );
	if( meshFile == NULL )
		return;

	// Inicializa a Bounding Box
	_bBox.xmax = 0; _bBox.xmin = 0;
	_bBox.ymax = 0; _bBox.ymin = 0;
	_bBox.zmax = 0; _bBox.zmin = 0;

	fscanf( meshFile, "%d", &_nVertices );
	fscanf( meshFile, "%d", &_nTriangles );

	// Aloca o vetor de vértices
	_vertices = (Vertex*)malloc( sizeof(Vertex) * _nVertices );
	for( int i = 0; i < _nVertices; i++ )
	{
		int id;
		float x, y, z;

		fscanf( meshFile, "%d  %f   %f   %f", &id, &x, &y, &z );
		_vertices[id].pos.Set( x, y, z );
		_vertices[id].normal.Set( 0, 0, 0 );

		// "Campeão" para bounding box
		if( x > _bBox.xmax )
			_bBox.xmax = x;
		else if( x < _bBox.xmin )
			_bBox.xmin = x;
		if( y > _bBox.ymax )
			_bBox.ymax = y;
		else if( y < _bBox.ymin )
			_bBox.ymin = y;
		if( z > _bBox.zmax )
			_bBox.zmax = z;
		else if( z < _bBox.zmin )
			_bBox.zmin = z;
	}

	// Aloca vetor de triângulos
	_triangles = (Triangle*)malloc( sizeof(Triangle) * _nTriangles );
	for( int i = 0; i < _nTriangles; i++ )
	{
		int id, v1, v2, v3;
		Triangle t;

		fscanf( meshFile, "%d   %d   %d   %d", &id, &v1, &v2, &v3 );
		t.id = id;
		t.v1 = &_vertices[v1];
		t.v2 = &_vertices[v2];
		t.v3 = &_vertices[v3];

		// Calcula normal por triângulo
		t.normal = calcNormal( &t );
		t.normal.Normalize();
		_triangles[i] = t;

		// Calcula normal por vértice
		t.v1->normal += t.normal;
		t.v2->normal += t.normal;
		t.v3->normal += t.normal;
	}

	fclose( meshFile );

	// Normaliza as normais de cada vértice
	for( int i = 0; i < _nVertices; i++ )
		_vertices[i].normal.Normalize();

	// Aloca vetor de vértices por triângulo (para VertexPointer)
	_vertexArray = (Vertex*)malloc( sizeof(Vertex) * _nTriangles * 3 );
	for( int i = 0; i < _nTriangles; i++ )
	{
		_vertexArray[i * 3 + 0] = *(_triangles[i].v1);
		_vertexArray[i * 3 + 1] = *(_triangles[i].v2);
		_vertexArray[i * 3 + 2] = *(_triangles[i].v3);
	}

	// Configura Vertex e Normal pointers
	glVertexPointer( 3, GL_FLOAT, sizeof(Vertex), _vertexArray );
	glNormalPointer( GL_FLOAT, sizeof(Vertex), &(_vertexArray[0].normal) );

	setSmooth( false );
}

EPSG::Mesh::~Mesh()
{

}

//////////////////////////////////////////////////////////////////////////
// PUBLIC FUNCTIONS                                                     //
//////////////////////////////////////////////////////////////////////////

void EPSG::Mesh::setSmooth( bool smooth )
{
	_smooth = smooth;
}

void EPSG::Mesh::getBBox( float* xmin, float* xmax, float* ymin, float* ymax, float* zmin, float* zmax )
{
	*xmin = _bBox.xmin;
	*xmax = _bBox.xmax;
	*ymin = _bBox.ymin;
	*ymax = _bBox.ymax;
	*zmin = _bBox.zmin;
	*zmax = _bBox.zmax;
}

void EPSG::Mesh::draw()
{
	glPushClientAttrib( GL_CLIENT_VERTEX_ARRAY_BIT );
	glEnable( GL_NORMALIZE );
	if( _smooth )
	{
		glEnableClientState( GL_VERTEX_ARRAY );
		glEnableClientState( GL_NORMAL_ARRAY );
		glDrawArrays( GL_TRIANGLES, 0, _nTriangles * 3 );
	}
	else
	{
		glBegin( GL_TRIANGLES );
		for( int i = 0; i < _nTriangles; i++ )
		{
			glNormal3f( _triangles[i].normal.x, _triangles[i].normal.y, _triangles[i].normal.z );
			glVertex3f( _triangles[i].v1->pos.x, _triangles[i].v1->pos.y, _triangles[i].v1->pos.z );
			glVertex3f( _triangles[i].v2->pos.x, _triangles[i].v2->pos.y, _triangles[i].v2->pos.z );
			glVertex3f( _triangles[i].v3->pos.x, _triangles[i].v3->pos.y, _triangles[i].v3->pos.z );
		}
		glEnd();
	}

	glPopClientAttrib();
}

//////////////////////////////////////////////////////////////////////////
// PRIVATE FUNCTIONS                                                    //
//////////////////////////////////////////////////////////////////////////

VVector EPSG::Mesh::calcNormal( Triangle* t )
{
	VVector v = t->v2->pos; v -= t->v1->pos;
	VVector w = t->v3->pos; w -= t->v1->pos;
	VVector n = Cross( v, w );

	return n;
}