#include "T3DVBO.hpp"

T3DVBO::T3DVBO(T3D *t)
{

	this->model = t;

	this->vertBufferID = 0;
	this->texBufferID = 0;
	this->normalBufferID = 0;

	this->edges = -1;
	
	this->VBOinit = false;
	lightpos = Vector3();
}

T3DVBO::~T3DVBO() 
{ 

}

void T3DVBO::renderVBO()
{

	if(this->VBOinit==false)
	{
		dmsMsg("VBO used, but wasn't initialised!\n");
		return; 
	}		

	glEnableClientState( GL_VERTEX_ARRAY );				
	glEnableClientState( GL_TEXTURE_COORD_ARRAY );		
	glEnableClientState( GL_NORMAL_ARRAY );

    glBindBufferARB( GL_ARRAY_BUFFER_ARB, this->vertBufferID );
	glVertexPointer( 3, GL_FLOAT, 0, (char *) NULL );			// vertexes

	glBindBufferARB( GL_ARRAY_BUFFER_ARB, this->texBufferID );
	glTexCoordPointer( 2, GL_FLOAT, 0, (char *) NULL );			// uvs

	glBindBufferARB( GL_ARRAY_BUFFER_ARB, this->normalBufferID );
	glNormalPointer( GL_FLOAT, 0, (char *) NULL );			// normals

	glDrawArrays( GL_TRIANGLES, 0, model->getFaceCount()*3);			// draw

	glDisableClientState( GL_NORMAL_ARRAY );
	glDisableClientState( GL_VERTEX_ARRAY );				
	glDisableClientState( GL_TEXTURE_COORD_ARRAY );				
}	

void T3DVBO::createVBO() 
{

	this->VBOinit = true;

	int				verts		= model->getFaceCount()*3;
	T3DVertex		*vertexes	= model->getVertexArray();
	T3DVBOVert		*ver		= new T3DVBOVert[verts];
	T3DVBOTexCoord	*tex		= new T3DVBOTexCoord[verts];
	T3DVBOVert		*nor		= new T3DVBOVert[verts];	

	int				objectvertexcount		= model->getVertexCount();
	T3DVertex		*objectvertices			= model->getVertexArray();

	int				objectfacecount			= model->getFaceCount();
	T3DFace			*objectfaces			= model->getFaceArray();
	

	int face = 0;
		for (int i=0;i<verts;i+=3)
		{
			ver[i].x = objectvertices[objectfaces[face].a].position.x;
			ver[i].y = objectvertices[objectfaces[face].a].position.y;
			ver[i].z = objectvertices[objectfaces[face].a].position.z;
			nor[i].x = objectvertices[objectfaces[face].a].normal.x;
			nor[i].y = objectvertices[objectfaces[face].a].normal.y;
			nor[i].z = objectvertices[objectfaces[face].a].normal.z;
			tex[i].u = objectvertices[objectfaces[face].a].u;
			tex[i].v = objectvertices[objectfaces[face].a].v;

			ver[i+1].x = objectvertices[objectfaces[face].b].position.x;
			ver[i+1].y = objectvertices[objectfaces[face].b].position.y;
			ver[i+1].z = objectvertices[objectfaces[face].b].position.z;
			nor[i+1].x = objectvertices[objectfaces[face].b].normal.x;
			nor[i+1].y = objectvertices[objectfaces[face].b].normal.y;
			nor[i+1].z = objectvertices[objectfaces[face].b].normal.z;
			tex[i+1].u = objectvertices[objectfaces[face].b].u;
			tex[i+1].v = objectvertices[objectfaces[face].b].v;

			ver[i+2].x = objectvertices[objectfaces[face].c].position.x;
			ver[i+2].y = objectvertices[objectfaces[face].c].position.y;
			ver[i+2].z = objectvertices[objectfaces[face].c].position.z;
			nor[i+2].x = objectvertices[objectfaces[face].c].normal.x;
			nor[i+2].y = objectvertices[objectfaces[face].c].normal.y;
			nor[i+2].z = objectvertices[objectfaces[face].c].normal.z;
			tex[i+2].u = objectvertices[objectfaces[face].c].u;
			tex[i+2].v = objectvertices[objectfaces[face].c].v;

			face++;
		}

	// Generate And Bind The Vertex Buffer
	glGenBuffersARB( 1, &this->vertBufferID);	
	glBindBufferARB( GL_ARRAY_BUFFER_ARB, this->vertBufferID);
	glBufferDataARB( GL_ARRAY_BUFFER_ARB, verts*3*sizeof(float), ver, GL_STATIC_DRAW_ARB );
	glBindBufferARB( GL_ARRAY_BUFFER_ARB, 0);

	// Generate And Bind The Texture Coordinate Buffer
	glGenBuffersARB( 1, &this->texBufferID );					
	glBindBufferARB( GL_ARRAY_BUFFER_ARB, this->texBufferID);	
	glBufferDataARB( GL_ARRAY_BUFFER_ARB, verts*2*sizeof(float), tex, GL_STATIC_DRAW_ARB );

	// Generate And Bind The Texture Coordinate Buffer
	glGenBuffersARB( 1, &this->normalBufferID );					
	glBindBufferARB( GL_ARRAY_BUFFER_ARB, this->normalBufferID);	
	glBufferDataARB( GL_ARRAY_BUFFER_ARB, verts*3*sizeof(float), nor, GL_STATIC_DRAW_ARB );

//	glBindBufferARB(GL_ARRAY_BUFFER, 0);

	delete [] ver; 
	verts = NULL;
	delete [] tex; 
	tex = NULL;
	delete [] nor;
	nor = NULL;
	
}

void T3DVBO::freeVBO() 
{
	glBindBufferARB(GL_ARRAY_BUFFER, 0);
	glDeleteBuffersARB(1, &this->vertBufferID);
	glDeleteBuffersARB(1, &this->texBufferID);
	glDeleteBuffersARB(1, &this->normalBufferID);
	
	VBOinit = false;

	this->texBufferID = -1;	
	this->vertBufferID = -1;
}




void T3DVBO::constructEdges()
{

	// initialize facing light buffer
	this->facingLight = new bool[model->getFaceCount()];
	unsigned int k;

	for(k=0; k<model->getFaceCount(); k++)
		facingLight[k]=false;


	// create edges
	unsigned long a;

    // Allocate enough space to hold all edges
   T3DEdge *edgeArray;
   edgeArray = new T3DEdge[model->getFaceCount()*3];

   long edgeCount = 0;

   const int faces = model->getFaceCount();
   T3DFace *face = model->getFaceArray();

   // First pass: find edges
   for (a=0; a<faces; a++)
   {
      long i1 = face[a].a;
      long i2 = face[a].b;
      long i3 = face[a].c;

      if (i1 < i2)
      {
         edgeArray[edgeCount].a = i1;
         edgeArray[edgeCount].b = i2;
         edgeArray[edgeCount].face1 = a;
         edgeArray[edgeCount].face2 = -1;
         edgeCount++;
      }

      if (i2 < i3)
      {
         edgeArray[edgeCount].a = i2;
         edgeArray[edgeCount].b = i3;
         edgeArray[edgeCount].face1 = a;
         edgeArray[edgeCount].face2 = -1;
         edgeCount++;
      }

      if (i3 < i1)
      {
         edgeArray[edgeCount].a = i3;
         edgeArray[edgeCount].b = i1;
         edgeArray[edgeCount].face1 = a;
         edgeArray[edgeCount].face2 = -1;
         edgeCount++;
      }
   }

   // Second pass: match triangles to edges
   //triangle = this->face;
   for (a = 0; a<faces; a++)
   {
      long i1 = face[a].a;
      long i2 = face[a].b;
      long i3 = face[a].c;

      if (i1 > i2)
      {
         //edgelist = edgeArray;
         for (long b=0; b<edgeCount; b++)
         {
            if ((edgeArray[b].a == i2) &&
				(edgeArray[b].b == i1) &&
				(edgeArray[b].face2 == -1))
            {
               edgeArray[b].face2 = a;
               break;
            }
         }
      }

      if (i2 > i3)
      {
         //edgelist = edgeArray;
         for (long b=0; b<edgeCount; b++)
         {
            if ((edgeArray[b].a == i3) &&
				(edgeArray[b].b == i2) &&
				(edgeArray[b].face2 == -1))
            {
               edgeArray[b].face2 = a;
               break;
            }
         }
      }

      if (i3 > i1)
      {
         //edgelist = edgeArray;
         for (long b=0; b<edgeCount; b++)
         {
            if ((edgeArray[b].a == i1) &&
				(edgeArray[b].b == i3) &&
				(edgeArray[b].face2 == -1))
            {
               edgeArray[b].face2 = a;
               break;
            }
         }
      }
   }

    this->edges = edgeCount;

	// copy tmpedge to this->edge
	this->edge = new T3DEdge[edgeCount];
	if(!this->edge)
	{
	//	return false;
	}

	for(a=0; a<unsigned(edgeCount); a++) {
		this->edge[a] = edgeArray[a];
	}

	delete [] edgeArray;

	//	return true;
}

void T3DVBO::freeEdges() 
{

	delete [] edge;
	delete [] facingLight;
}


void T3DVBO::computeSilhouette(Vector3 lightPos) 
{
  this->lightpos = lightPos;

  int facecount = model->getFaceCount();
  T3DFace *f    = model->getFaceArray(); 

  for(int i = 0; i<facecount; ++i)
  {
		if(lightPos.dotProduct(f[i].normal) > 0)
			this->facingLight[i] = true;
		else 
			this->facingLight[i] = false;
  }
}

void T3DVBO::drawSilhouette() 
{
		int i;
		const int edgecount = this->getEdgeCount();
		T3DFace *face		= model->getFaceArray(); 
		const int facecount = model->getFaceCount();
		T3DVertex *vertex   = model->getVertexArray(); 

		glBegin(GL_LINES);
		for (i=0;i<edgecount;i++)
		{
			
			if( facingLight[edge[i].face2] != facingLight[edge[i].face1]  )
			{

				Vector3 a,b;
				a = vertex[edge[i].a].position;
				b = vertex[edge[i].b].position;
					
				glVertex3fv((float *)&(a));
				glVertex3fv((float *)&(b));

			}
		}
		glEnd();
}

void T3DVBO::drawShadow(bool frontbackcap) 
{

		const float INFINITY = 999999.99f;
		
		int edgecount		= this->getEdgeCount();
		int i;

		const int facecount = model->getFaceCount();
		T3DFace *face	    = model->getFaceArray(); 

		T3DVertex *vertex   = model->getVertexArray(); 

	// not necessary if camera is not in the shadow volume
	// recheck the STENCIL calculations also if cam needs to enter the shadow
	if(frontbackcap) 
	{

		glBegin(GL_TRIANGLES);
		for (i=0;i<facecount;i++)
		{
			
			if( facingLight[i] )	
			{
					glVertex3fv((float *)&(vertex[face[i].a]));
					glVertex3fv((float *)&(vertex[face[i].b]));
					glVertex3fv((float *)&(vertex[face[i].c]));
			} else {
					Vector3 a = vertex[face[i].a].position;
					Vector3 b = vertex[face[i].b].position;
					Vector3 c = vertex[face[i].c].position;
					glVertex3fv((float *)&(a+(Vector3(a-lightpos).normalize())*INFINITY));
					glVertex3fv((float *)&(b+(Vector3(b-lightpos).normalize())*INFINITY));
					glVertex3fv((float *)&(c+(Vector3(c-lightpos).normalize())*INFINITY));					
			}
		}
		glEnd();
	}

		// Varjon laidat
		
		glBegin(GL_QUADS);	
		for (i=0;i<edgecount;i++)
		{
			if( ( facingLight[edge[i].face2] != facingLight[edge[i].face1] ) 
				// || ( facingLight[edge[i].face1] && edge[i].face2 == -1) ||	
				// ( facingLight[edge[i].face2] && edge[i].face1 == -1)
				)
			{ 

				Vector3 a,b;
				a = vertex[edge[i].a].position;
				b = vertex[edge[i].b].position;

				Vector3 c = b+Vector3(Vector3(b-lightpos).normalize())*INFINITY;
				Vector3 d = a+Vector3(Vector3(a-lightpos).normalize())*INFINITY;

				if(!facingLight[edge[i].face1]) 
				{
					glVertex3fv((float *)&a);
					glVertex3fv((float *)&b);
					glVertex3fv((float *)&c);
					glVertex3fv((float *)&d);

				} else {
					glVertex3fv((float *)&d);
					glVertex3fv((float *)&c);
					glVertex3fv((float *)&b);
					glVertex3fv((float *)&a);
				}
			}
		}
		glEnd();

}

T3DEdge *T3DVBO::getEdgeArray()
{
	return this->edge;
}

int T3DVBO::getEdgeCount() 
{
	return this->edges;
}

bool *T3DVBO::getFacingLightArray()
{
	return this->facingLight;
}	
