#include "heightMap.h"


HeightMapImporter::HeightMapImporter( float scaleX, float scaleY, float scaleZ )
{
	this->size[0] = 0;
	this->size[1] = 0;

	this->scale[0] = scaleX;
	this->scale[1] = scaleY;
	this->scale[2] = scaleZ;
}
void HeightMapImporter::init( unsigned int sizeX, unsigned int sizeY )
{
	this->size[0] = sizeX;
	this->size[1] = sizeY;

	unsigned int polyCount = (sizeX-1) * (sizeY-1) * 2; 
	this->faces.resize( polyCount * 3 );

	this->normals.resize( sizeX * sizeY * 3 );
	this->tCoords.resize( sizeX * sizeY * 2 );
	this->vertices.resize(sizeX * sizeY * 3 );

	this->initFaces();
	this->initVertices();

}

void HeightMapImporter::initFaces(void)
{
	for ( int y = 0; y< this->size[1]-1; ++y )
	{
		for ( int x = 0; x< this->size[0]-1; ++x )
		{
			// even polygons
			this->faces[ ((y * (this->size[0]-1) + x) * 3 * 2 + 0) ] = ( (y+0) * this->size[0]) + (x+0);	//up left corner
			this->faces[ ((y * (this->size[0]-1) + x) * 3 * 2 + 1) ] = ( (y+1) * this->size[0]) + (x+1);	//down right corner
			this->faces[ ((y * (this->size[0]-1) + x) * 3 * 2 + 2) ] = ( (y+1) * this->size[0]) + (x+0);	//down left corner

			// odd polygons
			this->faces[ ((y * (this->size[0]-1) + x) * 3 * 2 + 3) ] = ( (y+0) * this->size[0]) + (x+0);	//up left corner
			this->faces[ ((y * (this->size[0]-1) + x) * 3 * 2 + 4) ] = ( (y+0) * this->size[0]) + (x+1);	//up right corner
			this->faces[ ((y * (this->size[0]-1) + x) * 3 * 2 + 5) ] = ( (y+1) * this->size[0]) + (x+1);	//down right corner
		}
	}
}
void HeightMapImporter::initVertices(void)
{
	for ( int y = 0; y< this->size[1]; ++y )
	{
		for ( int x = 0; x< this->size[0]; ++x )
		{
			this->vertices[ (y * this->size[0] + x) * 3 + 0 ] = x * this->scale[0];
			this->vertices[ (y * this->size[0] + x) * 3 + 1 ] = y * this->scale[1];
			this->vertices[ (y * this->size[0] + x) * 3 + 2 ] = 0;
		}
	}
}

void HeightMapImporter::setVertex( unsigned int indexX, unsigned int indexY, float height )
{
	this->vertices[ (indexY* this->size[0] + indexX) * 3 +2 ] = height * this->scale[2];
}
void HeightMapImporter::setNormal( unsigned int indexX, unsigned int indexY, float normal[3] )
{
	for ( int i=0; i<3; ++i)
	{
		this->normals[ (indexY* this->size[0] + indexX) * 3 + i ] = normal[i];
	}
}
void HeightMapImporter::setTCoord( unsigned int indexX, unsigned int indexY, float tCoord[2] )
{
	for ( int i=0; i<2; ++i)
	{
		this->tCoords[ (indexY* this->size[0] + indexX) * 2 + i ] = tCoord[i];
	}
}

void HeightMapImporter::generateNormals( void )
{
	for ( int y = 1; y< this->size[1]-1; ++y ) //ignore first & last vertex ( we will not calculate his normal )
	{
		for ( int x = 1; x< this->size[0]-1; ++x ) //ignore first & last vertex ( we will not calculate his normal )
		{
	
			Point_3<float> neighbour[8]; //vertex neighbours

			//left up
			neighbour[0].x() = this->vertices[ ((y-1) * this->size[0] + (x-1)) * 3 + 0 ];
			neighbour[0].y() = this->vertices[ ((y-1) * this->size[0] + (x-1)) * 3 + 1 ];
			neighbour[0].z() = this->vertices[ ((y-1) * this->size[0] + (x-1)) * 3 + 2 ];

			//center up
			neighbour[1].x() = this->vertices[ ((y-1) * this->size[0] + (x+0)) * 3 + 0 ];
			neighbour[1].y() = this->vertices[ ((y-1) * this->size[0] + (x+0)) * 3 + 1 ];
			neighbour[1].z() = this->vertices[ ((y-1) * this->size[0] + (x+0)) * 3 + 2 ];
			
			//right up
			neighbour[2].x() = this->vertices[ ((y-1) * this->size[0] + (x+1)) * 3 + 0 ];
			neighbour[2].y() = this->vertices[ ((y-1) * this->size[0] + (x+1)) * 3 + 1 ];
			neighbour[2].z() = this->vertices[ ((y-1) * this->size[0] + (x+1)) * 3 + 2 ];


			//left center
			neighbour[3].x() = this->vertices[ ((y+0) * this->size[0] + (x-1)) * 3 + 0 ];
			neighbour[3].y() = this->vertices[ ((y+0) * this->size[0] + (x-1)) * 3 + 1 ];
			neighbour[3].z() = this->vertices[ ((y+0) * this->size[0] + (x-1)) * 3 + 2 ];

			//right center
			neighbour[4].x() = this->vertices[ ((y+0) * this->size[0] + (x+1)) * 3 + 0 ];
			neighbour[4].y() = this->vertices[ ((y+0) * this->size[0] + (x+1)) * 3 + 1 ];
			neighbour[4].z() = this->vertices[ ((y+0) * this->size[0] + (x+1)) * 3 + 2 ];


			//left down
			neighbour[5].x() = this->vertices[ ((y+1) * this->size[0] + (x-1)) * 3 + 0 ];
			neighbour[5].y() = this->vertices[ ((y+1) * this->size[0] + (x-1)) * 3 + 1 ];
			neighbour[5].z() = this->vertices[ ((y+1) * this->size[0] + (x-1)) * 3 + 2 ];

			//center down
			neighbour[6].x() = this->vertices[ ((y+1) * this->size[0] + (x+0)) * 3 + 0 ];
			neighbour[6].y() = this->vertices[ ((y+1) * this->size[0] + (x+0)) * 3 + 1 ];
			neighbour[6].z() = this->vertices[ ((y+1) * this->size[0] + (x+0)) * 3 + 2 ];
			
			//right down
			neighbour[7].x() = this->vertices[ ((y+1) * this->size[0] + (x+1)) * 3 + 0 ];
			neighbour[7].y() = this->vertices[ ((y+1) * this->size[0] + (x+1)) * 3 + 1 ];
			neighbour[7].z() = this->vertices[ ((y+1) * this->size[0] + (x+1)) * 3 + 2 ];

			
			/*
				calculates cross product of horizontal and vertical vector to get 1st normal
				calculates cross product of diagonal vectors to get 2nd normal
				calculates mean normal from normal1 and normal2
			*/
			
			Vector_3<float> vecX;
			vecX = neighbour[3] - neighbour[4];
			vecX.normalize();

			Vector_3<float> vecY;
			vecY = neighbour[1] - neighbour[6];
			vecY.normalize();

			Vector_3<float> normal1;
			normal1.calculate_cross_product( vecX, vecY );
			normal1.normalize();
			

			Vector_3<float> vecDiagonal1;
			vecDiagonal1 = neighbour[0] - neighbour[7];
			vecDiagonal1.normalize();

			Vector_3<float> vecDiagonal2;
			vecDiagonal2 = neighbour[2] - neighbour[5];
			vecDiagonal2.normalize();

			Vector_3<float> normal2;
			normal2.calculate_cross_product( vecDiagonal1, vecDiagonal2 );
			normal2.normalize();

			Vector_3<float> finalNormal;
			finalNormal = normal1+normal2;
			finalNormal.normalize();

			this->normals[ ((y * this->size[0]) + x ) * 3 + 0 ] = finalNormal.x();
			this->normals[ ((y * this->size[0]) + x ) * 3 + 1 ] = finalNormal.y();
			this->normals[ ((y * this->size[0]) + x ) * 3 + 2 ] = finalNormal.z();

			/*
			Point_3<float> vertex;
			vertex.x() = this->vertices[ ((y+0) * this->size[0] + (x+0)) * 3 + 0 ];
			vertex.y() = this->vertices[ ((y+0) * this->size[0] + (x+0)) * 3 + 1 ];
			vertex.z() = this->vertices[ ((y+0) * this->size[0] + (x+0)) * 3 + 2 ];

			Vector_3<float> tmpVec[8];

			for ( int i=0; i<8; ++i)
			{
				tmpVec[i] = neighbour[i] - vertex;
				tmpVec[i].normalize();
			}


			Vector_3<float> finalNormal;
			Vector_3<float> tmpNormal;

			for ( int i=0; i<8; ++i)
			{
				tmpNormal.calculate_cross_product(  tmpVec[i], tmpVec[ (i+1) % 8 ] );

				
				if ( tmpNormal != MAKE_VECTOR<float>(0,0,0) )
				{
					tmpNormal *= 0.4;
					tmpNormal.z() += 0.6;
					tmpNormal.normalize();
				}
				else
				{
					tmpNormal = MAKE_VECTOR<float>(0,0,1); 
				}

				finalNormal += tmpNormal;
			}

			finalNormal = finalNormal / 8;

			if ( finalNormal != MAKE_VECTOR<float>(0,0,0) )
			{
				finalNormal.normalize();
			}
			
			this->normals[ ((y * this->size[0]) + x ) * 3 + 0 ] = -finalNormal.x();
			this->normals[ ((y * this->size[0]) + x ) * 3 + 1 ] = -finalNormal.y();
			this->normals[ ((y * this->size[0]) + x ) * 3 + 2 ] = -finalNormal.z();
			*/

		}
	}
}
void HeightMapImporter::generateTCoords( void )
{
	double offset[2] = { 1.0/(this->size[0]-1), 1.0/(this->size[1]-1) };

	for ( int y = 0; y< this->size[1]; ++y )
	{
		for ( int x = 0; x< this->size[0]; ++x )
		{
			this->tCoords[ (y * this->size[0] + x) * 2 + 0 ] =  x * offset[0];
			this->tCoords[ (y * this->size[0] + x) * 2 + 1 ] =  y * offset[1];
		}
	}
}


vector<float>& HeightMapImporter::verticesRef(void)
{
	return this->vertices;
}
vector<float>& HeightMapImporter::normalsRef(void)
{
	return this->normals;
}
vector<float>& HeightMapImporter::tCoordsRef(void)
{
	return this->tCoords;
}
vector<int>& HeightMapImporter::facesRef(void)
{
	return this->faces;
}



//----------------

void uploadHeightMapGeometry( HeightMapImporter heightMapImporter, VBOmanager& vbo_manager, const string &vbo_name, vector<MeshObject> &dst_meshes)
{
	vector<vector<float>> multiTCoordBuffer;
	multiTCoordBuffer.push_back( heightMapImporter.tCoordsRef() );

	VBOids vboID;

	vbo_manager.createNewVBO(vbo_name, heightMapImporter.verticesRef(), heightMapImporter.normalsRef(), multiTCoordBuffer, heightMapImporter.facesRef());
	bool ok= vbo_manager.getVBOdata(vbo_name, vboID);
	assert( ok && "VBO not found!" ); 

	vboID.from_index=0;
	vboID.to_index= heightMapImporter.facesRef().size() / 3;

	MeshObject mesh;
	mesh.vbo_data = vboID;
	dst_meshes.push_back( mesh );
}