//DEBUG-Meldungen vorhanden

#include "model.h"

using namespace std;

/*
 * default constructor
 */
Model::Model()
{
	mesh = NULL;
	material = NULL;
}

/*
 * default deconstructor
 */
Model::~Model()
{
	//delete [] vbo;
	delete mesh;
	delete material;
}

/*
 * Load a model declaration
 * char* filepath - path to the model information
 */
bool Model::loadMod(char* filepath)
{
	std::vector<float*> vertexPos;
	std::vector<float*> normals;
	std::vector<float*> textureCoordinates;
	std::vector<int*> triangles;

	FILE **file = new FILE*[1];
	char line[100];
	std::string lineString;
	std::string subStr1;
	std::string subStr2;
	std::string subStr3;

	int ind1 = 0;
	int ind2 = 0;
	int ind3 = 0;

	float x, y, z;
	x = y = z = 0.0f;

	float u, v;
	u = v = 0.0f;

	float* vertex;
	float* textureCoord;
	float* normal;
	int* triangle;

	int cnt = 0;

	//open the file
	fopen_s(file, filepath, "rb");

	while(fgets(line, 100, *file))
	{
		lineString = std::string(line);	//get one line of the file

		//case distinction
		if(lineString[0] == 'v')
		{
			//case v, vt, vn
			if(lineString[1] == ' ')
			{
				//case v
				//e.g. v 1.0000 -1.00000 -1.0000
				ind1 = lineString.find_first_of(' ', 2);
				subStr1 = lineString.substr(2, ind1 - 2);
				x = StringToFloat(subStr1, 1.0f);
				
				ind2 = lineString.find_first_of(' ', ind1 + 1);
				subStr2 = lineString.substr(ind1 + 1, ind2-ind1-1);
				y = StringToFloat(subStr2, 1.0f);

				subStr3 = lineString.substr(ind2+1);
				z = StringToFloat(subStr3, 1.0f);

				vertex = new float[3];
				vertex[0] = x;
				vertex[1] = y;
				vertex[2] = z;
			
				vertexPos.push_back(vertex);	//add the vertex to the vector
			
			}//end of if(lineString[1] == ' ' )
			else if (lineString[1] == 't')
			{
				//case t
				//eg vt 0.32222 0.45000
				
				ind1 = lineString.find_first_of(' ', 3);
				subStr1 = lineString.substr(3, ind1 - 3);
				u = StringToFloat(subStr1, 1.0f);

				ind2 = lineString.find_first_of(' ', ind1 + 1);
				subStr2 = lineString.substr(ind1 + 1, ind2 - ind1 - 1);
				v = StringToFloat(subStr2, 1.0f);

				textureCoord = new float[2];
				textureCoord[0] = u;
				textureCoord[1] = v;

				textureCoordinates.push_back(textureCoord);	//add the coordinates to the vector

			}//end of else if(lineString[1] == 't')
			else if(lineString[1] == 'n')
			{
				//case n
				//eg vn 1.00000 1.00000 -1.0000

				ind1 = lineString.find_first_of(' ', 3);
				subStr1 = lineString.substr(3, ind1 - 3);
				
				x = StringToFloat(subStr1, 1.0f);

				ind2 = lineString.find_first_of(' ', ind1+1);
				subStr2 = lineString.substr(ind1+1, ind2-ind1-1);
				y = StringToFloat(subStr2, 1.0f);

				subStr3 = lineString.substr(ind2+1);
				z = StringToFloat(subStr3, 1.0f);

				normal = new float[3];
				normal[0] = x;
				normal[1] = y;
				normal[2] = z;
				//cout << "DEBUG string # " << subStr1 << " " << subStr2 << " " << subStr3 << endl;
				//cout << "DEBUG normal # " << x << " " << y << " " << z << endl;
			
				normals.push_back(normal);	//add the normal to the vector
			
			}//end of else if(lineString[1] == 'n'
		
		
		}//end of if(lineString[0] == 'v'
		else if(lineString[0] == 'f')
		{
			//case faces
			//eg f 5/2/1 1/3/3 4/2/1
			triangle = new int[9];

			//f 5/2/1#1/3/3 4/2/1
			ind1 = lineString.find_first_of(' ', 2);

			//the first vertex with his information
			//"5/2/1"   v/t/n
			subStr1 = lineString.substr(2, ind1-2);

			//5#2/1
			ind2 = subStr1.find_first_of('/');

			subStr2 = subStr1.substr(0, ind2);
			triangle[0] = StringToInt(subStr2, 1);

			//5/2#1
			ind3 = subStr1.find_first_of('/', ind2+1);
			subStr3 = subStr1.substr(ind2+1, ind3 - ind2 - 1);
			triangle[1] = StringToInt(subStr3, 1);

			subStr3 = subStr1.substr(ind3 + 1);
			triangle[2] = StringToInt(subStr3, 1);
		

			//f 5/2/1 1/3/3#4/2/1
			ind2 = lineString.find_first_of(' ', ind1 +1);

			//second vertex
			//"1/3/3"
			subStr1 = lineString.substr(ind1 +1, ind2 - ind1-1);
			ind1 = ind2;

			//1#3/3
			ind2 = subStr1.find_first_of('/');

			subStr2 = subStr1.substr(0, ind2);
			triangle[3] = StringToInt(subStr2, 1);

			//1/3#3
			ind3 = subStr1.find_first_of('/', ind2+1);
			subStr3 = subStr1.substr(ind2 + 1, ind3 - ind2 - 1);
			triangle[4] = StringToInt(subStr3, 1);
			
			subStr3 = subStr1.substr(ind3 + 1);
			triangle[5] = StringToInt(subStr3, 1);


			//third vertex
			//'4/2/1'
			subStr1 = lineString.substr(ind1 +1);

			//4#2/1
			ind2 = subStr1.find_first_of('/');

			subStr2 = subStr1.substr(0, ind2);
			triangle[6] = StringToInt(subStr2, 1);

			//4/2#1
			ind3 = subStr1.find_first_of('/', ind2+1);
			subStr2 = subStr1.substr(ind2 + 1, ind3 - ind2 - 1);
			triangle[7] = StringToInt(subStr2, 1);

			subStr3 = subStr1.substr(ind3+1, subStr1.size() - ind3 - 3);
			triangle[8] = StringToInt(subStr3, 1);	//add the information

			triangles.push_back(triangle);	//add the triangle to the vector
		
			cnt++;
		}//end of else if(lineString[0] == 'f')
	
	}//end of while(fgets(line, 100, *file))



	//cout << "DEBUG: vertexPos.size(): " << vertexPos.size() << endl;
	//std::vector<float*>::iterator tmpVertex = vertexPos.begin();
	//cout << "DEBUG: " << vertexPos.at(0)[2] << endl;
	
	//(*tmpVertex)+2;
	//cout << "DEBUG: vertexpos # " << (*tmpVertex)[0] << " " << (*tmpVertex)[1] << " " << (*tmpVertex)[2] << endl;
	//for(; tmpVertex != vertexPos.end(); tmpVertex++)
	//{
	//	cout << "tmpVertex: " << (*tmpVertex) << endl;
	//	cout << "DEBUG: vertexpos # " << (*tmpVertex)[0] << " " << (*tmpVertex)[1] << " " << (*tmpVertex)[2] << endl;
	//}
	//cout << "DEBUG: normals.size(): " << normals.size() << endl;
	//std::vector<float*>::iterator tmp = normals.begin();
	//for(; tmp != normals.end(); tmp++)
	//{
	//	cout << "DEBUG: normals # " << (*tmp)[0] << " " << (*tmp)[1] << " " << (*tmp)[2] << endl;
	//}
	//cout << "DEBUG: textureCoordinates.size(): " << textureCoordinates.size() << endl;
	//std::vector<float*>::iterator tmp = textureCoordinates.begin();
	//for(; tmp != textureCoordinates.end(); tmp++)
	//{
	//	cout << "DEBUG: texCoord # " << (*tmp)[0] << " " << (*tmp)[1] <<  endl;
	//}
	//cout << "DEBUG: triangles.size(): " << triangles.size() << endl;
	//std::vector<int*>::iterator tmp = triangles.begin();
	//for(; tmp != triangles.end(); tmp++)
	//{
	//	cout << "DEBUG: triangle # "<< (*tmp)[0] << "/" << (*tmp)[1] << "/" << (*tmp)[2] << " " <<
	//		(*tmp)[3] << "/" << (*tmp)[4] << "/" << (*tmp)[5] << " " <<
	//		(*tmp)[6] << "/" << (*tmp)[7] << "/" << (*tmp)[8] << endl;
	//}


	//get the dimension of the model
	float* dimensions = new float[4];
	getDimension(vertexPos, dimensions);

	//now construct the arrays and the important indices!
	int currentIndex = 0;
	int insertIndex = 0;
	int storedIndex = 0;

	std::set<int> vHashSet;
	std::map<int, int> hastToIndexMap;

	int vHash = 0;

	std::vector<float*> newVertexPos;
	std::vector<float*> newNormals;
	std::vector<float*> newTextureCoord;
	//std::vector<float*> tangents;
	std::vector<glm::vec4> tangent;
	GLuint* indices = new GLuint[triangles.size() * 3];
	int indicesArraySize = triangles.size() * 3;

	//construct the indices array and the new arrays
	for(int i = 0; i < static_cast<int>(triangles.size()); i++)
	{
		//cout << "DEBUG: triangles[i] " << triangles[i] << endl;
		//add the first vertex of the triangle
		addVertex(triangles[i], vHashSet, hastToIndexMap, vertexPos, newVertexPos,
			textureCoordinates, newTextureCoord, normals, newNormals, indices, insertIndex,
			currentIndex, storedIndex);

		//add the second vertex of the triangle
		addVertex(triangles[i] + 3, vHashSet, hastToIndexMap, vertexPos, newVertexPos,
			textureCoordinates, newTextureCoord, normals, newNormals, indices, insertIndex,
			currentIndex, storedIndex);

		//add the third vertex of the triangle
		addVertex(triangles[i] + 6, vHashSet, hastToIndexMap, vertexPos, newVertexPos,
			textureCoordinates, newTextureCoord, normals, newNormals, indices, insertIndex,
			currentIndex, storedIndex);
	
	
	}//end of for(inti = 0; i < static_cast<int>(triangles.size()); i++)

	//cout << newVertexPos.at(0)[0]<<  newVertexPos.at(0)[1] << newVertexPos.at(0)[2] << endl;
	//cout << newVertexPos.at(1)[0]<<  newVertexPos.at(1)[1] << newVertexPos.at(1)[2] << endl;
	//cout << newVertexPos.at(2)[0]<<  newVertexPos.at(2)[1] << newVertexPos.at(2)[2] << endl;

	//TODO!!! vielleicht der counter nicht korrekt?

	//Erzeugen der Tangentenvektoren
	//for(int j = 0; j < static_cast<int>(triangles.size()); j++)
	//{
	//	calcTangentVec(triangles[j], triangles[j] + 3, triangles[j] + 6, vertexPos, textureCoordinates, normals, tangents);
	//}

	//cout << "DEBUG: tangents size " << static_cast<int>(tangents.size()) << endl;
	//cout << "DEBUG: indicesArraySize " << indicesArraySize << endl;


	//DEBUG
	//for(int i = 0; i < 36; i++)
	//{
	//	cout << indices[i] << " ";

	//	if((i+1) % 3 == 0)
	//	{
	//		cout << endl;
	//	}
	//
	//}

	//now build the final arrays
	int arraySize = static_cast<int>(newVertexPos.size());

	float* finalVertex = new float[arraySize * 4];
	float* finalNormal = new float[arraySize * 3];
	float* finalTexCoord = new float[arraySize * 2];
	float* finalTangents = new float[indicesArraySize * 4];

	int vertexIndex, normalIndex, textureCoordIndex, tangentIndex;

	//
	//
	//
	//
	//Calculate thge tangents
	//calcTangent(arraySize, newVertexPos, newNormals, newTextureCoord, 
		//static_cast<int>(triangles.size()), triangles, tangent);
	calcTangent(arraySize, vertexPos, newNormals, textureCoordinates, 
		static_cast<int>(triangles.size()), triangles, tangent);



	vertexIndex = normalIndex = textureCoordIndex = tangentIndex = 0;

	std::vector<float*>::iterator itVertex = newVertexPos.begin();
	std::vector<float*>::iterator itNormal = newNormals.begin();
	std::vector<float*>::iterator itTexCoor = newTextureCoord.begin();
	//std::vector<float*>::iterator itTangent = tangents.begin();
	std::vector<glm::vec4>::iterator itTangents = tangent.begin();

	//DEBUG
	//cout <<"newVertexPos.size(): "<<newVertexPos.size() <<endl;
	//cout <<"newNormals.size(): " <<newNormals.size() << endl;
	//cout <<"newTextureCoord.size(): " << newTextureCoord.size() << endl;
	//cout << "tangent.size(): " << tangent.size() << endl;
	//for(; itVertex != newVertexPos.end(); itVertex++)
	//{
	//	//DEBUG
	//	cout << (*itVertex)[0] << (*itVertex)[1] << (*itVertex)[2] << endl;
	//}
	//for(; itTangents != tangent.end(); itTangents++)
	//{
	//	cout << (*itTangents).x  <<"/" <<(*itTangents).y << "/"<<(*itTangents).z <<"/"<< (*itTangents).w<< endl;
	//}



	//get the information into the new arrays
	for(; itVertex != newVertexPos.end(); itVertex++, itNormal++, itTexCoor++, itTangents++)
	{
		finalVertex[vertexIndex++] = (*itVertex)[0];
		finalVertex[vertexIndex++] = (*itVertex)[1];
		finalVertex[vertexIndex++] = (*itVertex)[2];
		finalVertex[vertexIndex++] = 1.0f;

		finalNormal[normalIndex++] = (*itNormal)[0];
		finalNormal[normalIndex++] = (*itNormal)[1];
		finalNormal[normalIndex++] = (*itNormal)[2];

		finalTexCoord[textureCoordIndex++] = (*itTexCoor)[0];
		finalTexCoord[textureCoordIndex++] = (*itTexCoor)[1];

		finalTangents[tangentIndex++] = (*itTangents).x;
		finalTangents[tangentIndex++] = (*itTangents).y;
		finalTangents[tangentIndex++] = (*itTangents).z;
		finalTangents[tangentIndex++] = (*itTangents).w;
		//cout << "DEBUG: tangent # " << (*itTangents).x << "/" << (*itTangents).y << "/" << (*itTangents).z << "/" << (*itTangents).w << endl;
	
	}//end of for(; itVertex != newVertexPos.end(); itVertex++; itNormal++; itTexCoord++)

	//cout << "DEBUG: " << finalVertex[0] << "/" << finalVertex[1] << "/" << finalVertex[2] << endl;



	//for(int i = 0; i < indicesArraySize; itTangent++, i++)
	//{
	//	finalTangents[tangentIndex++] = (*itTangent)[0];
	//	finalTangents[tangentIndex++] = (*itTangent)[1];
	//	finalTangents[tangentIndex++] = (*itTangent)[2];
	//	finalTangents[tangentIndex++] = (*itTangent)[3];
	//}

	//cout << "DEBUG: for erzeugen eines neuen Mesh" << endl;
	//create a new Mesh!!!!
	mesh = new Mesh(finalVertex, finalNormal, finalTexCoord, indices, indicesArraySize, arraySize, finalTangents, dimensions);


	//now clean up the different things
	vHashSet.clear();
	hastToIndexMap.clear();

	//two iterators for the different arrays to clean up
	std::vector<int*>::iterator itInt;
	std::vector<float*>::iterator itFloat;

	//vertex
	for(itFloat = vertexPos.begin(); itFloat != vertexPos.end(); itFloat++)
	{
		delete [] (*itFloat);
	}
	vertexPos.clear();

	//new vertex
	for(itFloat = newVertexPos.begin(); itFloat != newVertexPos.end(); itFloat++)
	{
		delete [] (*itFloat);
	}
	newVertexPos.clear();

	//normals
	for(itFloat = normals.begin(); itFloat != normals.end(); itFloat++)
	{
		delete [] (*itFloat);
	}
	normals.clear();

	//new normals
	for(itFloat = newNormals.begin(); itFloat != newNormals.end(); itFloat++)
	{
		delete [] (*itFloat);
	}
	newNormals.clear();

	//texture coordinates
	for(itFloat = textureCoordinates.begin(); itFloat != textureCoordinates.end(); itFloat++)
	{
		delete [] (*itFloat);
	}
	textureCoordinates.clear();

	//new texture coordinates
	for(itFloat = newTextureCoord.begin(); itFloat != newTextureCoord.end(); itFloat++)
	{
		delete [] (*itFloat);
	}
	newTextureCoord.clear();

	//tangents
	//for(itFloat = tangents.begin(); itFloat != tangents.end(); itFloat++)
	//{
	//	delete [] (*itFloat);
	//}

	//triangles
	for(itInt = triangles.begin(); itInt != triangles.end(); itInt++)
	{
		delete [] (*itInt);
	}
	triangles.clear();

	
	return false;

}//end of bool Model::loadMoad(char* filepath)

/*
 *Get a hashcode out of the vertex-information
 * int* vertex - vertex to calculate the hash information
 */
int Model::getVertexHash(int* vertex)
{
	int prim = 97;
	int result = 1;

	//cout << "DEBUG: getVertexHash " << vertex[0] << "/" <<vertex[1] << "/" << vertex[2] << endl;

	result = prim * result + vertex[0];
	result = prim * result + vertex[1];
	result = prim * result + vertex[2];

	return result;


}//end of int Model::getVertexHash(int* vertex)

/*
 * Add a vertex combination to the indices array and create the new correct arrays for the vertices, normals, texture coordinates
 */
void Model::addVertex(int* pVertex, std::set<int> &vHashSet, std::map<int,int> &hashToIndexMap, 
					  std::vector<float*> &vertexPositions, std::vector<float*> &newVertexPositions, 
					  std::vector<float*> &textureCoordinates, std::vector<float*> &newTextureCoordinates, 
					  std::vector<float*> &normals, std::vector<float *> &newNormals, 
					  GLuint* indices, int &inserIndex, int &currIndex, int &storedIndex)
{
	//cout << "DEBUG: pVertex " << (*pVertex) << endl;
	int vHash = getVertexHash(pVertex);
	//cout << "DEBUG: vHash - " << vHash << endl;

	if(vHashSet.find(vHash) == vHashSet.end())
	{
		//vertex, that is unique and not in the list
		vHashSet.insert(vHash);

		float* vert = new float[3];
		vert[0] = (vertexPositions[*pVertex - 1])[0];	//vertices sind an stelle 0
		vert[1] = (vertexPositions[*pVertex - 1])[1];
		vert[2] = (vertexPositions[*pVertex - 1])[2];
	
		newVertexPositions.push_back(vert);

		float* textureCoord = new float[2];
		textureCoord[0] = (textureCoordinates[*(pVertex+1)-1])[0];	//texture coordinates sind an stelle 1
		textureCoord[1] = (textureCoordinates[*(pVertex+1)-1])[1];

		newTextureCoordinates.push_back(textureCoord);

		float* normal = new float[3];
		normal[0] = (normals[*(pVertex+2)-1])[0];	//normals sind an stelle 2
		normal[1] = (normals[*(pVertex+2)-1])[1];
		normal[2] = (normals[*(pVertex+2)-1])[2];

		newNormals.push_back(normal);

		hashToIndexMap[vHash] = inserIndex;

		indices[currIndex] = inserIndex;

		inserIndex++;
		currIndex++;

	}//end of if(vHashSet.find(vHash) == vHashSet.end())
	else
	{
		//the unique vertex is already in the list, just get the index
		storedIndex = hashToIndexMap.find(vHash)->second;
		indices[currIndex] = storedIndex;
		currIndex++;
	
	}//end of else

}//end of Model::addVertex(...)

/*
 * Load the material properties of the model
 * char* filepath path of the material properties
 */
bool Model::loadMatProp(char* filepath)
{
	FILE **file = new FILE*[1];
	char line[40];
	std::string lineString;
	std::string subs1;
	std::string subs2;

	//Create a new default material
	material = new Material();

	float r,g,b;
	r = g = b = 0.0f;

	int ind1 = 0;
	int ind2 = 0;

	fopen_s(file, filepath, "rb");	//open the file

	while(fgets(line, 40, *file))
	{
		lineString = std::string(line);

		//case distinction
		if(lineString[0] == 'N' && lineString[1] == 's')
		{
			//specular exponent
			//e.g. Ns 96.078431
			material->setSpecularExpo(StringToFloat(lineString.substr(3, lineString.size()-3), 1.0f));
		}
		else if(lineString[0] == 'K')
		{
			if(lineString[1] == 'a')
			{
				//ambient light color
				//e.g. Ka 0.000000 0.000000 0.000000
				ind1 = lineString.find_first_of(' ', 3);
				subs1 = lineString.substr(3, ind1 - 3);
				r = StringToFloat(subs1, 1.0f);

				ind2 = lineString.find_first_of(' ', ind1 + 1);
				subs2 = lineString.substr(ind1 + 1, ind2 - ind1 - 1);
				g = StringToFloat(subs2,1.0f);

				b = StringToFloat(lineString.substr(ind2 + 1), 1.0f);

				material->setAmbientColor(r, g, b);	//set the ambient color
			
			}//end of if(lineString[1] == 'a'
			else if (lineString[1] == 'd')
			{
				//diffuse light color
				//e.g. Kd 0.640000 0.640000 0.640000
				ind1 = lineString.find_first_of(' ', 3);
				subs1 = lineString.substr(3, ind1 - 3);
				r = StringToFloat(subs1, 1.0f);

				ind2 = lineString.find_first_of(' ', ind1+1);
				subs2 = lineString.substr(ind1 + 1, ind2 - ind1 - 1);
				g = StringToFloat(subs2, 1.0f);

				b = StringToFloat(lineString.substr(ind2+1), 1.0f);

				material->setDiffuseColor(r, g, b);
			
			}//end of else if(lineString[1] == 'd')
			else if(lineString[1] == 's')
			{
				//sepecular light color
				//e.g. Ks 0.500000 0.500000 0.500000
				ind1 = lineString.find_first_of(' ', 3);
				subs1 = lineString.substr(3, ind1 - 3);
				r = StringToFloat(subs1, 1.0f);

				ind2 = lineString.find_first_of(' ', ind1 + 1);
				subs2 = lineString.substr(ind1+1, ind2-ind1-1);
				g = StringToFloat(subs2, 1.0f);

				b = StringToFloat(lineString.substr(ind2 + 1), 1.0f);

				material->setSpecularColor(r, g, b);
			
			
			}//end of else if(lineString[1] == 's')
		
		}//end of else if(lineString[0] == 'K')
	
	}//end of while(fgets(line, 40, *file))

	return true;

}//end of Model::loadMatProp(char* filepath)

/*
 * Set the material of the model
 * Material *_material pointer to the information of the material
 */
void Model::setMaterial(Material *_material)
{
	material = _material;
}

/*
 * Set the mesh of the model
 * Mesh *_mesh pointer to the information of the mesh
 */
void Model::setMesh(Mesh *_mesh)
{
	mesh = _mesh;
}

/*
 * Set the vbos of the model
 * GLuint* vboArray vbos to be set
 */
//void Model::setVBOArray(GLuint *vboArray)
//{
//	vbo = vboArray;
//}

/*
 * Get the vbos of the model
 * return pointer to the vbos of the model
 */
//GLuint* Model::getVBO()
//{
//	return vbo;
//}

/*
 * Get the mesh of the model
 * return pointer to the mesh of the model
 */
Mesh* Model::getMesh()
{
	return mesh;
}

/*
 * Get the material of the model
 * return pointer to the material of the model
 */
Material* Model::getMaterial()
{
	return material;
}

/*
 * Cast a string to an integer
 * string& str_input - string input
 * int default_value - default value for error security issues
 */
int Model::StringToInt(const std::string& str_input, const int default_value)
{
  std::stringstream s_str(str_input);
  int value = default_value;
  //now cast it!
  if (!(s_str>>value))
  { //error during conversion -> return given default value
    return default_value;
  }
  return value;
}

/*
 * Cast a string to a float
 * string& str_input - string input
 * float default_value - default value for error security issues
 */
float Model::StringToFloat(const std::string& str_input, const float default_value)
{
  std::stringstream s_str(str_input);
  float value = default_value;
  //now cast it
  if (!(s_str>>value))
  { //error during conversion -> return given default value
    return default_value;
  }
  return value;
}

//build the vertex buffer objects of the model
bool Model::buildVBOs(const Shader &shader)
{
	//generate the vao
	glGenVertexArrays(1, &vao_id);

	//bind the vao
	glBindVertexArray(vao_id);

	//gererate the vbos
	glGenBuffers(1, &vbo_vertices);
	glGenBuffers(1, &vbo_normals);
	glGenBuffers(1, &vbo_texture);
	glGenBuffers(1, &vbo_indices);

	//vertices
	glBindBuffer(GL_ARRAY_BUFFER, vbo_vertices);
	glBufferData(GL_ARRAY_BUFFER, 4 * mesh->getElementCount() * sizeof(GLfloat), mesh->getVertices(), GL_STATIC_DRAW);
	loc_vertices = shader.get_attrib_location("in_position");
	glEnableVertexAttribArray(loc_vertices);
	glVertexAttribPointer(loc_vertices, 4, GL_FLOAT, GL_FALSE, 0, NULL);

	//normals
	glBindBuffer(GL_ARRAY_BUFFER, vbo_normals);
	glBufferData(GL_ARRAY_BUFFER, 3 * mesh->getElementCount() * sizeof(GLfloat), mesh->getNormals(), GL_STATIC_DRAW);
	loc_normals = shader.get_attrib_location("in_normal");
	glEnableVertexAttribArray(loc_normals);
	glVertexAttribPointer(loc_normals, 3, GL_FLOAT, GL_FALSE, 0, NULL);

	//texture
	glBindBuffer(GL_ARRAY_BUFFER, vbo_texture);
	glBufferData(GL_ARRAY_BUFFER, 2 * mesh->getElementCount() * sizeof(GLfloat), mesh->getTextureCoordinates(), GL_STATIC_DRAW);
	loc_texture = shader.get_attrib_location("in_texcoord");
	glEnableVertexAttribArray(loc_texture);
	glVertexAttribPointer(loc_texture, 2, GL_FLOAT, GL_FALSE, 0, NULL);

	get_errors();

	//indices
	glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, vbo_indices);
	glBufferData(GL_ELEMENT_ARRAY_BUFFER, mesh->getIndicesCount() * sizeof(GLuint), mesh->getIndices(), GL_STATIC_DRAW);

	//unbind vao
	glBindVertexArray(0);

	//unbind vbos
	glBindBuffer(GL_ARRAY_BUFFER, 0);

	if(!(glGetError() == GL_NO_ERROR))
	{
		cout << "Error: after creating the model vbos" << endl;
		return false;
	}

	return true;

}//end of bool Model::buildVBOs(const Shader &shader)


//build the vertex buffer objects including the tangents object
bool Model::buildVBOsNorm(const Shader &shader)
{
	//generate the vao
	glGenVertexArrays(1, &vao_id);

	//bind the vao
	glBindVertexArray(vao_id);

	//gererate the vbos
	glGenBuffers(1, &vbo_vertices);
	glGenBuffers(1, &vbo_normals);
	glGenBuffers(1, &vbo_texture);
	glGenBuffers(1, &vbo_tangents);
	glGenBuffers(1, &vbo_indices);

	//vertices
	glBindBuffer(GL_ARRAY_BUFFER, vbo_vertices);
	glBufferData(GL_ARRAY_BUFFER, 4 * mesh->getElementCount() * sizeof(GLfloat), mesh->getVertices(), GL_STATIC_DRAW);
	loc_vertices = shader.get_attrib_location("in_position");
	glEnableVertexAttribArray(loc_vertices);
	glVertexAttribPointer(loc_vertices, 4, GL_FLOAT, GL_FALSE, 0, NULL);

	//normals
	glBindBuffer(GL_ARRAY_BUFFER, vbo_normals);
	glBufferData(GL_ARRAY_BUFFER, 3 * mesh->getElementCount() * sizeof(GLfloat), mesh->getNormals(), GL_STATIC_DRAW);
	loc_normals = shader.get_attrib_location("in_normal");
	glEnableVertexAttribArray(loc_normals);
	glVertexAttribPointer(loc_normals, 3, GL_FLOAT, GL_FALSE, 0, NULL);

	//texture
	glBindBuffer(GL_ARRAY_BUFFER, vbo_texture);
	glBufferData(GL_ARRAY_BUFFER, 2 * mesh->getElementCount() * sizeof(GLfloat), mesh->getTextureCoordinates(), GL_STATIC_DRAW);
	loc_texture = shader.get_attrib_location("in_texcoord");
	glEnableVertexAttribArray(loc_texture);
	glVertexAttribPointer(loc_texture, 2, GL_FLOAT, GL_FALSE, 0, NULL);

	//tangents
	glBindBuffer(GL_ARRAY_BUFFER, vbo_tangents);
	glBufferData(GL_ARRAY_BUFFER, 4 * mesh->getElementCount() * sizeof(GLfloat), mesh->getTangents(), GL_STATIC_DRAW);
	loc_tangents = shader.get_attrib_location("in_tangent");
	glEnableVertexAttribArray(loc_tangents);
	glVertexAttribPointer(loc_tangents, 4, GL_FLOAT, GL_FALSE, 0, NULL);

	get_errors();

	//indices
	glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, vbo_indices);
	glBufferData(GL_ELEMENT_ARRAY_BUFFER, mesh->getIndicesCount() * sizeof(GLuint), mesh->getIndices(), GL_STATIC_DRAW);

	//unbind vao
	glBindVertexArray(0);

	//unbind vbos
	glBindBuffer(GL_ARRAY_BUFFER, 0);

	if(!(glGetError() == GL_NO_ERROR))
	{
		cout << "Error: after creating the model vbos normal mapping" << endl;
		return false;
	}

	return true;

}//end of bool Model::buildVBOsNorm(const Shader &shader)

//get the vertex array object
GLuint Model::getVAO()
{
	return vao_id;
}

//render the model
void Model::render()
{
	glBindVertexArray(vao_id);

	//draw triangle in vao
	glDrawElements(GL_TRIANGLES, 3 * mesh->getIndicesCount(), GL_UNSIGNED_INT, 0);

	//undbind the vao
	glBindVertexArray(0);

	//undbind the vbos
	glBindBuffer(GL_ARRAY_BUFFER, 0);

}//end of void Model::render()


void Model::calcTangentVec(int* pVert1, int* pVert2, int* pVert3, std::vector<float*>& vertexPositions,
	std::vector<float*>& textureCoordinates, std::vector<float*>& normalPositions, std::vector<float*>& tangents)
{

	//DEBUG WRONG!!!!!!!!!!!!!!!!!!
	//Quelle : http://www.terathon.com/code/tangent.html, http://www.c-plusplus.de/forum/273335-20

	glm::vec3 vertex_1;
	glm::vec3 vertex_2;
	glm::vec3 vertex_3;
	glm::vec2 texcoord_1;
	glm::vec2 texcoord_2;
	glm::vec2 texcoord_3;
	glm::vec3 normal_1;
	glm::vec3 normal_2;
	glm::vec3 normal_3;
	glm::vec4 tangent_1;
	glm::vec4 tangent_2;
	glm::vec4 tangent_3;
	glm::vec4 tangent_face;

	//DEBUG

	

	//anfuellen der variablen mit den werten - damit nicht immer alles ueber pointer gemacht werden muss - 
	//fuer die Uebersicht
	//Vertices
	vertex_1 = glm::vec3((vertexPositions[*pVert1 - 1])[0], (vertexPositions[*pVert1 - 1])[1],(vertexPositions[*pVert1 - 1])[2]);
	//DEBUG
	//cout << (vertexPositions[*pVert1 - 1])[0] << "/" << (vertexPositions[*pVert1 - 1])[1] << "/" << (vertexPositions[*pVert1 - 1])[2] << endl;
	//cout << vertex_1.x << "/" << vertex_1.y << "/" << vertex_1.z << endl;

	vertex_2 = glm::vec3((vertexPositions[*pVert2 - 1])[0], (vertexPositions[*pVert2 - 1])[1],(vertexPositions[*pVert2 - 1])[2]);
	vertex_3 = glm::vec3((vertexPositions[*pVert3 - 1])[0], (vertexPositions[*pVert3 - 1])[1],(vertexPositions[*pVert3 - 1])[2]);

	//Texture coordinates
	texcoord_1 = glm::vec2((textureCoordinates[*(pVert1+1)-1])[0], (textureCoordinates[*(pVert1+1)-1])[1]);
	texcoord_2 = glm::vec2((textureCoordinates[*(pVert2+1)-1])[0], (textureCoordinates[*(pVert2+1)-1])[1]);
	texcoord_3 = glm::vec2((textureCoordinates[*(pVert3+1)-1])[0], (textureCoordinates[*(pVert3+1)-1])[1]);

	normal_1 = glm::vec3((normalPositions[*(pVert1+2)-1])[0], (normalPositions[*(pVert1+2)-1])[1], (normalPositions[*(pVert1+2)-1])[2]);
	normal_2 = glm::vec3((normalPositions[*(pVert2+2)-1])[0], (normalPositions[*(pVert2+2)-1])[1], (normalPositions[*(pVert2+2)-1])[2]);
	normal_3 = glm::vec3((normalPositions[*(pVert3+2)-1])[0], (normalPositions[*(pVert3+2)-1])[1], (normalPositions[*(pVert3+2)-1])[2]);


	//Info - Spitze minus Schaft
	float x1 = vertex_2.x - vertex_1.x;
	float x2 = vertex_3.x - vertex_1.x;
	float y1 = vertex_2.y - vertex_1.y;
	float y2 = vertex_3.y - vertex_1.y;
	float z1 = vertex_2.z - vertex_1.z;
	float z2 = vertex_3.z - vertex_1.z;

	//s,t - als coordinaten, da wir uns im 2D Texture space dann befinden
	float s1 = texcoord_2.x - texcoord_1.x;
	float s2 = texcoord_3.x - texcoord_1.x;
	float t1 = texcoord_2.y - texcoord_1.y;
	float t2 = texcoord_3.y - texcoord_1.y;

	//Normalisierungsfaktor
	float r = 1.0f / (s1*t2 - s2*t1);

	//s-direction, t-direction auf der textur
	glm::vec3 sdir = glm::vec3((t2*x1 - t1*x2) * r, (t2*y1 - t1*y2)*r, (t2*z1 - t1*z2)*r);
	glm::vec3 tdir = glm::vec3((s1*x2 - s2*x1) * r, (s1*y2 - s2*y1)*r, (s1*z2 - s2*z1)*r);


	//nun fuer jeden Vertex - damit dann der Wert fuer jedes Face ermittelt wird, muss später eine gewichtete Interpolation geschehen!!!
	//zuerst die Gram-Schmidt Orthogonalisierung
	tangent_1 = glm::normalize(glm::vec4(sdir - normal_1 * glm::dot(normal_1, sdir), 0.0));
	tangent_2 = glm::normalize(glm::vec4(sdir - normal_2 * glm::dot(normal_2, sdir), 0.0));
	tangent_3 = glm::normalize(glm::vec4(sdir - normal_3 * glm::dot(normal_3, sdir), 0.0));


	//Calculate the handedness - if the face can be seen
	tangent_1.w = (glm::dot(glm::cross(normal_1, sdir), tdir) < 0.0f) ? -1.0f : 1.0f;
	tangent_2.w = (glm::dot(glm::cross(normal_2, sdir), tdir) < 0.0f) ? -1.0f : 1.0f;
	tangent_3.w = (glm::dot(glm::cross(normal_3, sdir), tdir) < 0.0f) ? -1.0f : 1.0f;

	//nicht sehr effizient, aber passend - anfuellen mit werten fuer eine gleiche übergabe - damit nicht zuviele
	//verschiedene Methoden gemischt werden
	float* tan1 = new float[4];
	tan1[0] = tangent_1.x;
	tan1[1] = tangent_1.y;
	tan1[2] = tangent_1.z;
	tan1[3] = tangent_1.w;
	tangents.push_back(tan1);

	float* tan2 = new float[4];
	tan2[0] = tangent_2.x;
	tan2[1] = tangent_2.y;
	tan2[2] = tangent_2.z;
	tan2[3] = tangent_2.w;
	tangents.push_back(tan2);

	float* tan3 = new float[4];
	tan3[0] = tangent_3.x;
	tan3[1] = tangent_3.y;
	tan3[2] = tangent_3.z;
	tan3[3] = tangent_3.w;
	tangents.push_back(tan3);
	

	//TODO Interpolate between the tangents to get one vector per face ? 
	

}//end of void Model::calcTangentVec


//calculate the tangents of the object
void Model::calcTangent(int vertexCount, std::vector<float*> &vertexPositions, std::vector<float*> &normalPositions, 
	std::vector<float*> &textureCoordinates, int trianglesCount, std::vector<int*> &triangles, std::vector<glm::vec4> &tangents)
{
	std::set<int> vHashSet;
	std::map<int, int> hastToIndexMap;
	int index = 0;
	int storedIndex = 0;

	//Erzeugen der Tangentenvektoren
	/*for(int j = 0; j < static_cast<int>(triangles.size()); j++)
	{
		calcTangentVec(triangles[j], triangles[j] + 3, triangles[j] + 6, vertexPos, textureCoordinates, normals, tangents);
	}*/

	std::vector<glm::vec3> tan1;
	std::vector<glm::vec3> tan2;

	for(int i = 0; i < trianglesCount; i++)
	{
		glm::vec3 vertex_1;
		glm::vec3 vertex_2;
		glm::vec3 vertex_3;
		glm::vec2 texcoord_1;
		glm::vec2 texcoord_2;
		glm::vec2 texcoord_3;
		glm::vec3 normal_1;
		glm::vec3 normal_2;
		glm::vec3 normal_3;
		
		int* pVert1 = triangles[i];
		int* pVert2 = triangles[i] + 3;
		int* pVert3 = triangles[i] + 6;
	
		vertex_1 = glm::vec3(vertexPositions.at(*pVert1-1)[0], vertexPositions.at(*pVert1-1)[1], vertexPositions.at(*pVert1-1)[2]);
		vertex_2 = glm::vec3(vertexPositions.at(*pVert2-1)[0], vertexPositions.at(*pVert2-1)[1], vertexPositions.at(*pVert2-1)[2]);
		vertex_3 = glm::vec3(vertexPositions.at(*pVert3-1)[0], vertexPositions.at(*pVert3-1)[1], vertexPositions.at(*pVert3-1)[2]);

		//DEBUG
		//cout << "Faceindex? : " << (*pVert1) << "/" << (*pVert2) << "/" << (*pVert3) << endl;
		//cout << "TexIndex: " << (*pVert1+1) << "/" << (*pVert2+1) << "/" << (*pVert3+1) << endl;
		//cout << "vertexPos " << (vertexPositions[*pVert1])[0] << endl;
		//cout << "DEBUG: pVert1-1 " << (*pVert1-1) << endl;
		//cout << "DEBUG: pVert2-1 " << (*pVert2-1)<< endl;
		//cout << "DEBUG: pVert3-1 " << (*pVert3-1) << endl;
		//cout <<"DEBUG: vertex_1: " << vertex_1.x << "/" << vertex_1.y << "/" << vertex_1.z << endl;
		//cout <<"DEBUG: vertex_2: " << vertex_2.x << "/" << vertex_2.y << "/" << vertex_2.z << endl;
		//cout <<"DEBUG: vertex_3: " << vertex_3.x << "/" << vertex_3.y << "/" << vertex_3.z << endl;

		texcoord_1 = glm::vec2((textureCoordinates[*(pVert1+1)-1])[0], (textureCoordinates[*(pVert1+1)-1])[1]);
		texcoord_2 = glm::vec2((textureCoordinates[*(pVert2+1)-1])[0], (textureCoordinates[*(pVert2+1)-1])[1]);
		texcoord_3 = glm::vec2((textureCoordinates[*(pVert3+1)-1])[0], (textureCoordinates[*(pVert3+1)-1])[1]);

		//cout << "DEBUG: texcoord_1 " << texcoord_1.x << "/" << texcoord_1.y << endl;
		//cout << "DEBUG: texcoord_2 " << texcoord_2.x << "/" << texcoord_2.y << endl;
		//cout << "DEBUG: texcoord_3 " << texcoord_3.x << "/" << texcoord_3.y << endl;

		float x1 = vertex_2.x - vertex_1.x;
		float x2 = vertex_3.x - vertex_1.x;
		float y1 = vertex_2.y - vertex_1.y;
		float y2 = vertex_3.y - vertex_1.y;
		float z1 = vertex_2.z - vertex_1.z;
		float z2 = vertex_3.z - vertex_1.z;

		float s1 = texcoord_2.x - texcoord_1.x;
		float s2 = texcoord_3.x - texcoord_1.x;
		float t1 = texcoord_2.y - texcoord_1.y;
		float t2 = texcoord_3.y - texcoord_1.y;

		float r = 1.0f / (s1*t2 - s2*t1);

		glm::vec3 sdir = glm::vec3((t2*x1 - t1*x2) * r, (t2*y1 - t1*y2) * r, (t2*z1 - t1*z2) * r);
		glm::vec3 tdir = glm::vec3((s1*x2 - s2*x1) * r, (s1*y2 - s2*y1) * r, (s1*z2 - s2*z1) * r);

		
		addTDir(pVert1, vHashSet, hastToIndexMap, tan1, tan2, sdir, tdir, index, storedIndex);
		addTDir(pVert2, vHashSet, hastToIndexMap, tan1, tan2, sdir, tdir, index, storedIndex);
		addTDir(pVert3, vHashSet, hastToIndexMap, tan1, tan2, sdir, tdir, index, storedIndex);
	
	}// end of for(int i = 0; i < trianglesCount; i++)

	//cout << "DEBUG: tan1.size() " << tan1.size() << endl;
	//cout << "DEBUG: tan2.size() " << tan2.size() << endl;

	//std::vector<float*>::iterator tmp = normals.begin();
	//for(; tmp != normals.end(); tmp++)
	//{
	//	cout << "DEBUG: normals # " << (*tmp)[0] << " " << (*tmp)[1] << " " << (*tmp)[2] << endl;
	//}

	std::vector<float*>::iterator it = normalPositions.begin();
	//for(; it != normalPositions.end(); it++)
	//{
	//	cout << "DEBUG: normals # "<< (*it)[0] << "/" << (*it)[1] << "/" << (*it)[2] << endl;
	//}
	//it = normalPositions.begin();

	//ueber alle vertices
	for(int i = 0; i < tan1.size(); i++, it++)
	{
		glm::vec3 t = tan1.at(i);
		glm::vec3 normal = glm::vec3((*it)[0], (*it)[1], (*it)[2]);

		//Gram-Schmidt orthogonalize
		glm::vec3 tmp = glm::normalize((t - normal * glm::dot(normal, t)));
		glm::vec4 tmp4 = glm::vec4(tmp, 0.0);
		tmp4.w = (glm::dot(glm::cross(normal, t), tan2.at(i)) < 0.0f) ?  -1.0f : 1.0f;

		//cout <<"DEBUG tmp4 "<< tmp4.x << "/" << tmp4.y << "/" << tmp4.z << "/" << tmp4.w << endl;

		tangents.push_back(tmp4);
	
	}//end of for(int i = 0; i < vertexCount; i++)

	
	//cout << "DEBUG tangents.size(): " << tangents.size() << endl;

}//end void Model:calcTangent


//add the tangents to the hash map
void Model::addTDir(int* pVertex, std::set<int> &vHashSet, std::map<int, int> &hashToIndexMap,
		std::vector<glm::vec3> &tan1, std::vector<glm::vec3> &tan2, glm::vec3 &sdir, glm::vec3 &tdir, int &index, int &storedIndex)
{
	int vHash = getVertexHash(pVertex);

	if(vHashSet.find(vHash) == vHashSet.end())
	{
		//unique vertex, not in the list
		vHashSet.insert(vHash);
	
		tan1.push_back(sdir);
		tan2.push_back(tdir);
	
		hashToIndexMap[vHash] = index;
		index++;
	}
	else
	{
		//the unique vertex is already in the list, just get the index
		storedIndex = hashToIndexMap.find(vHash)->second;
		tan1.at(storedIndex).x += sdir.x;
		tan1.at(storedIndex).y += sdir.y;
		tan1.at(storedIndex).z += sdir.z;

		tan2.at(storedIndex).x += tdir.x;
		tan2.at(storedIndex).y += tdir.y;
		tan2.at(storedIndex).z += tdir.z;

	}



}

//get a hash code out of the tanget information
int Model::getTangHash(int _x, int _y, int _z)
{
	int prim = 97;
	int result = 1;

	//cout << "DEBUG: getVertexHash " << vertex[0] << "/" <<vertex[1] << "/" << vertex[2] << endl;

	result = prim * result + _x;
	result = prim * result + _y;
	result = prim * result + _z;

	return result;


}//end of int Model::getVertexHash(int* vertex)


//get the dimension of the model
void Model::getDimension(std::vector<float*> &vertexPositions, float* dimension)
{

	GLfloat xMax, xMin, yMax, yMin, zMax, zMin;
	xMax = xMin = yMax = yMin = zMax = zMin = 0.0f;
	float* dim = new float[3];

	std::vector<float*>::iterator tmp = vertexPositions.begin();
	for(; tmp != vertexPositions.end(); tmp++)
	{
		if(xMax < (*tmp)[0])
			xMax = (*tmp)[0];
		if(xMin > (*tmp)[0])
			xMin = (*tmp)[0];

		if(yMax < (*tmp)[1])
			yMax = (*tmp)[1];
		if(yMin > (*tmp)[1])
			yMin = (*tmp)[1];
	
		if(zMax < (*tmp)[2])
			zMax = (*tmp)[2];
		if(zMin > (*tmp)[2])
			zMin = (*tmp)[2];
	}//end of for

	//calclculate the width, height, depth of the model
	dimension[0] = glm::abs(xMax) + glm::abs(xMin);
	dimension[1] = glm::abs(yMax) + glm::abs(yMin);
	dimension[2] = glm::abs(zMax) + glm::abs(zMin);
	dimension[3] = 1.0f;

}//end of void Model::getDimension