/*
 * ExNihilo 3D Engine
 * 
 *  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 Library 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.
 *
 * Please read AUTHORS file !!!
 * 
 
 *
 */

#include "ExCHeightMap.h"

ExCHeightMap::ExCHeightMap(void)
{
	m_FogDepthLevel = 55.0f;
	m_MapDetailLevel = 8;
	m_MapResolution = 1024;
	m_Texture2Repetition = 50;
	m_Fog=true;
	m_MapScale=4;
}

ExCHeightMap::~ExCHeightMap(void)
{
}


void ExCHeightMap::Load(std::string FileName)
{
Guard(void ExCHeightMap::Load(std::string FileName))
	g_HeightMap = new byte[m_MapResolution*m_MapResolution];
	char			buffer[255],b[255];	
	std::string buffstring;
	std::ifstream file;

	#ifdef UNIX_SRC
	sprintf(buffer, PREFIX "/ExNihilo/Data/Map/%s", FileName.data());
	#else
	sprintf(buffer, "../Data/Map/%s", FileName.data());
	#endif
	file.open(buffer,std::ios::in);
	if(file.is_open())
	{
		memset(b,0,255);file.getline(b,256,'\n');
		try
		{
			std::cout<<"Plok1"<<std::endl;
			if(ExNihilo::ExtractIntValueFromSring(b,"<FogEnable>","<#FogEnable>")==1)
			{
				m_Fog=true;
			}else
			{
				m_Fog=false;
			}
			memset(b,0,255);file.getline(b,256,'\n');
			m_FogDepthLevel=ExNihilo::ExtractFloatValueFromSring(b,"<FogDepthLevel>","<#FogDepthLevel>");
			memset(b,0,255);file.getline(b,256,'\n');
			m_MapDetailLevel=ExNihilo::ExtractFloatValueFromSring(b,"<MapDetailLevel>","<#MapDetailLevel>");
			memset(b,0,255);file.getline(b,256,'\n');
			m_MapResolution=ExNihilo::ExtractFloatValueFromSring(b,"<MapResolution>","<#MapResolution>");
			memset(b,0,255);file.getline(b,256,'\n');
			m_MapScale=ExNihilo::ExtractFloatValueFromSring(b,"<MapScale>","<#MapScale>");
			memset(b,0,255);file.getline(b,256,'\n');
			m_TextureMap="../Data/Map/"+ExNihilo::ExtracValueFromSring(b,"<HeightTexture>","<#HeightTexture>");
			FILE *pFile = NULL;
			pFile = fopen(m_TextureMap.data(), "rb" );
			if ( pFile == NULL )	
			{
				std::cout<<"Can't find the height map! Error"<<std::endl;
				return;
			}

			fread( g_HeightMap, 1, m_MapResolution * m_MapResolution, pFile );
			if(ferror( pFile ))
			{
				std::cout<<"Can't get data! Error"<<std::endl;
			}
			fclose(pFile);

			m_VecTexture.resize(ManagerTexture->GetMaxMultitexturingLevel());
			memset(b,0,255);file.getline(b,256,'\n');
			SetTextureLand(0,ExNihilo::ExtracValueFromSring(b,"<TextureLand1>","<#TextureLand1>"));
			memset(b,0,255);file.getline(b,256,'\n');
			SetTextureLand(1,ExNihilo::ExtracValueFromSring(b,"<TextureLand2>","<#TextureLand2>"));
			memset(b,0,255);file.getline(b,256,'\n');
			SetTextureLand(2,ExNihilo::ExtracValueFromSring(b,"<TextureLand3>","<#TextureLand3>"));
			memset(b,0,255);file.getline(b,256,'\n');
			SetTextureLand(3,ExNihilo::ExtracValueFromSring(b,"<TextureLand4>","<#TextureLand4>"));

			//Read position
			memset(b,0,255);file.getline(b,256,'\n');
			buffstring=ExNihilo::ExtracValueFromSring(b,"<Position>","<#Position>");
			m_Position.SetX(ExNihilo::ExtractFloatValueFromSring(buffstring,"<X>","<#X>"));
			m_Position.SetY(ExNihilo::ExtractFloatValueFromSring(buffstring,"<Y>","<#Y>"));
			m_Position.SetZ(ExNihilo::ExtractFloatValueFromSring(buffstring,"<Z>","<#Z>"));
			
		}catch(ExCExpStringNotFound){std::cout<<"Bad file type "<<buffer<<std::endl;}
	}else
	{
		std::cout<<"File not found:"<<buffer<<std::endl;
		throw   ExCExpFileNotFound();
	}
	m_Position.SetX(m_Position.GetX()-(m_MapResolution*m_MapScale/2));
	m_Position.SetZ(m_Position.GetZ()-(m_MapResolution*m_MapScale/2));

	//BuildArray();
UnGuard
}

void ExCHeightMap::SetTextureLand(unsigned int Number,std::string FileName)
{
Guard(void ExCHeightMap::SetTextureLand(unsigned int Number,std::string FileName))
	if (m_VecTexture.size()>Number)
	{
		m_VecTexture.at(Number)=FileName;
	}
UnGuard
}

void ExCHeightMap::Draw(void)
{
Guard(void ExCHeightMap::Draw(void))
	glPushAttrib(GL_ALL_ATTRIB_BITS);
		glPushMatrix();
			glTranslatef(m_Position.GetX(),m_Position.GetY(),m_Position.GetZ());
			glScalef(m_MapScale,m_MapScale,m_MapScale);
			glDisable(GL_LIGHTING);
			glEnable(GL_TEXTURE_2D);
			glColor3f(1.0f,1.0f,1.0f);
			/*if(m_Array)
			{
				RenderArray();
			}else*/
			try
			{
				RenderHeightMap();
			}catch (...)
			{
				std::cout<<"Exception render heigtmap"<<std::endl;
			}
		glPopMatrix();
	glPopAttrib();
UnGuard
}

void ExCHeightMap::IncreaseDetailLevel(void)
{
Guard(void ExCHeightMap::IncreaseDetailLevel(void))
	m_MapDetailLevel=m_MapDetailLevel*2;
	if(m_MapDetailLevel>=m_MapResolution)m_MapDetailLevel=m_MapResolution;
UnGuard
}

void ExCHeightMap::DereaseDetailLevel(void)
{
Guard(void ExCHeightMap::DereaseDetailLevel(void))
	m_MapDetailLevel=m_MapDetailLevel/2;
	if (m_MapDetailLevel<=0)m_MapDetailLevel=1;
UnGuard
}

void ExCHeightMap::IncreaseFogDepthLevel(void)
{
Guard(void ExCHeightMap::IncreaseFogDepthLevel(void))
	m_FogDepthLevel++;
UnGuard
}

void ExCHeightMap::DereaseFogDepthLevel(void)
{
Guard(void ExCHeightMap::DereaseFogDepthLevel(void))
	m_FogDepthLevel--;
	if (m_FogDepthLevel<=0)m_FogDepthLevel=1;
UnGuard
}

void ExCHeightMap::IncreaseTexture2Repetition(void)
{
Guard(void ExCHeightMap::IncreaseTexture2Repetition(void))
	m_Texture2Repetition++;
UnGuard
}

void ExCHeightMap::DereaseTexture2Repetition(void)
{
Guard(void ExCHeightMap::DereaseTexture2Repetition(void))
	m_Texture2Repetition--;
	if (m_Texture2Repetition<=0)m_Texture2Repetition=1;
UnGuard
}


///////////////////////////////// HEIGHT \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*
/////
/////	This returns the height into the height map
/////
///////////////////////////////// HEIGHT \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*

int ExCHeightMap::Height(int X, int Y)
{
	// This is used to index into our height map array.
	// When ever we are dealing with arrays, we want to make sure
	// that we don't go outside of them, so we make sure that doesn't
	// happen with a %.  This way x and y will cap out at (MAX_SIZE - 1)

	int x = X % m_MapResolution;					// Error check our x value
	int y = Y % m_MapResolution;					// Error check our y value

	if(!g_HeightMap) return 0;				// Make sure our data is valid

	// Below, we need to treat the single array like a 2D array.
	// We can use the equation: index = (x + (y * arrayWidth) ).
	// This is assuming we are using this assumption array[x][y]
	// otherwise it's the opposite.  Now that we have the correct index,
	// we will return the height in that index.

	return g_HeightMap[x + (y * m_MapResolution)];	// Index into our height array and return the height
}

void ExCHeightMap::SetTextureCoord(float CoordU,float CoordV)
{
Guard(void ExCHeightMap::SetTextureCoord(float CoordU,float CoordV))
	for(unsigned int i=0;i<m_VecTexture.size();i++){ManagerTexture->SetTextureCoord(i,CoordU,CoordV);}
UnGuard
}
void ExCHeightMap::BuildArray(void)
{
	int verticecount=0;
	float xmul=1;
	float ymul=1.5;
	float zmul=1;
	int X = 0, Y = 0;						// Create some variables to walk the array with.
	int x, y, z;							// Create some variables for readability
	float fogY = 0;
	bool bSwitchSides = false;

	ExCVec3D Vec;

	TabVertices= new float[6*(33282)];
	TabTextures= new float[4*(33282)];
	
	
	int i=0;
	for ( X = 0; X <= m_MapResolution; X += m_MapDetailLevel )
	{
		
		if(bSwitchSides)
		{	
			for ( Y = m_MapResolution; Y >= 0; Y -= m_MapDetailLevel )
			{
				x = X;//X+=MapdetailLevel 							
				y = Height(X, Y );	
				z = Y;//Y = m_MapResolution ;Y-=MapdetailLevel							
				//SetTextureCoord((float)x / (float)m_MapResolution,-(float)z / (float)m_MapResolution);
				TabTextures[0+i*4]=(float)x / (float)m_MapResolution;
				TabTextures[1+i*4]=-(float)z / (float)m_MapResolution;
				//glVertex3i(x*xmul, y*ymul, z*zmul);	
				Vec.SetValue(x*xmul, y*ymul, z*zmul);	
				TabVertices[0+i*6]=Vec.GetX();
				TabVertices[1+i*6]=Vec.GetY();
				TabVertices[2+i*6]=Vec.GetZ();
				//std::cout<<Vec;
				verticecount++;
				x = X + m_MapDetailLevel; 
				y = Height(X + m_MapDetailLevel, Y ); 
				z = Y;
				//SetTextureCoord((float)x / (float)m_MapResolution,-(float)z / (float)m_MapResolution);
				TabTextures[2+i*4]=(float)x / (float)m_MapResolution;
				TabTextures[3+i*4]=-(float)z / (float)m_MapResolution;
				//glVertex3i(x*xmul, y*ymul, z*zmul);
				Vec.SetValue(x*xmul, y*ymul, z*zmul);
				TabVertices[3+i*6]=Vec.GetX();
				TabVertices[4+i*6]=Vec.GetY();
				TabVertices[5+i*6]=Vec.GetZ();
				//std::cout<<Vec;
				verticecount++;
				i++;
			}
		}
		else
		{	
			for ( Y = 0; Y <= m_MapResolution; Y += m_MapDetailLevel )
			{
				x = X + m_MapDetailLevel; 
				y = Height(X + m_MapDetailLevel, Y ); 
				z = Y;
				//SetTextureCoord((float)x / (float)m_MapResolution,-(float)z / (float)m_MapResolution);
				TabTextures[0+i*4]=(float)x / (float)m_MapResolution;
				TabTextures[1+i*4]=-(float)z / (float)m_MapResolution;
				//glVertex3i(x, y*ymul, z);
				Vec.SetValue(x, y*ymul, z);
				TabVertices[0+i*6]=Vec.GetX();
				TabVertices[1+i*6]=Vec.GetY();
				TabVertices[2+i*6]=Vec.GetZ();
				//std::cout<<Vec;
				verticecount++;
				x = X;							
				y = Height(X, Y );	
				z = Y;
				//SetTextureCoord((float)x / (float)m_MapResolution,-(float)z / (float)m_MapResolution);
				TabTextures[2+i*4]=(float)x / (float)m_MapResolution;
				TabTextures[3+i*4]=-(float)z / (float)m_MapResolution;
				//glVertex3i(x*xmul, y*ymul, z*zmul);	
				Vec.SetValue(x*xmul, y*ymul, z*zmul);	
				TabVertices[3+i*6]=Vec.GetX();
				TabVertices[4+i*6]=Vec.GetY();
				TabVertices[5+i*6]=Vec.GetZ();
				//std::cout<<Vec;
				verticecount++;
				i++;
			}
		}
		bSwitchSides = !bSwitchSides;
		std::cout<<i<<":"<<verticecount<<std::endl;
	}
	
	m_Array=true;
}
void ExCHeightMap::RenderArray()
{
Guard(void ExCHeightMap::RenderArray())
	glPushMatrix();
		glTranslatef(0,-10,0);
		glScalef(1.4,1.5,3);
		ManagerTexture->SetTexture(0,m_VecTexture.at(0));
		ManagerTexture->SetTexture(1,m_VecTexture.at(1));

		glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE_ARB);
		glTexEnvi(GL_TEXTURE_ENV, GL_RGB_SCALE_ARB, 2);
		glMatrixMode(GL_TEXTURE);
		glPushMatrix();
			glLoadIdentity();
			glScalef((float)m_Texture2Repetition, (float)m_Texture2Repetition, 1);
			glMatrixMode(GL_MODELVIEW);
			ManagerTexture->SetTexture(2,m_VecTexture.at(2));
			ManagerTexture->SetTexture(3,m_VecTexture.at(3));

			//call array
			glTexCoordPointer(2,GL_FLOAT,0,TabTextures);
			glVertexPointer(2,GL_FLOAT,0,TabVertices);
			glDrawArrays( GL_TRIANGLE_STRIP, 0, 11094 );
			
		glMatrixMode(GL_TEXTURE);
		ManagerTexture->SetTexture(1,m_VecTexture.at(1));
		glDisableClientState(GL_TEXTURE_COORD_ARRAY);
		glPopMatrix();
		glMatrixMode(GL_MODELVIEW);
		glMatrixMode(GL_TEXTURE);
		ManagerTexture->SetTexture(2,m_VecTexture.at(2));
		glDisableClientState(GL_TEXTURE_COORD_ARRAY);
		glPopMatrix();
		glMatrixMode(GL_MODELVIEW);
		ManagerTexture->ResetMultitexture();

	glPopMatrix();
UnGuard
}

void ExCHeightMap::RenderHeightMap()
{
Guard(void ExCHeightMap::RenderHeightMap())
	float xmul=1;
	float ymul=1.5;
	float zmul=1;
	//float xmul=1.4;
	//float ymul=1.5*1.5;
	//float zmul=3;
	int X = 0, Y = 0;						// Create some variables to walk the array with.
	int x, y, z;							// Create some variables for readability
	float fogY = 0;
	bool bSwitchSides = false;
	
glPushMatrix();
	glTranslatef(0,-10,0);
	glScalef(1.4,1.5,3);
		
	if(m_Fog){ManagerFog->EnableFog();}
	
	ManagerTexture->SetTexture(0,m_VecTexture.at(0));
	glDisableClientState(GL_TEXTURE_COORD_ARRAY);


	// If we want detail texturing on, let's render the second texture
	ManagerTexture->SetTexture(1,m_VecTexture.at(1));
	glDisableClientState(GL_TEXTURE_COORD_ARRAY);
	
	// Here we turn on the COMBINE properties and increase our RGB
	// gamma for the detail texture.  2 seems to work just right.
	glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE_ARB);
	glTexEnvi(GL_TEXTURE_ENV, GL_RGB_SCALE_ARB, 2);
	glMatrixMode(GL_TEXTURE);
		glPushMatrix();
		glLoadIdentity();
		glScalef((float)m_Texture2Repetition, (float)m_Texture2Repetition, 1);
	glMatrixMode(GL_MODELVIEW);
	
	
//	ManagerTexture->SetTexture(2,m_VecTexture.at(2));
//	glDisableClientState(GL_TEXTURE_COORD_ARRAY);
/*	glMatrixMode(GL_TEXTURE);
		glPushMatrix();
		glLoadIdentity();
		glTranslatef(0.0f,-0.00001*glutGet(GLUT_ELAPSED_TIME),0.0f);
	glMatrixMode(GL_MODELVIEW);*/

//	ManagerTexture->SetTexture(3,m_VecTexture.at(3));
//	glDisableClientState(GL_TEXTURE_COORD_ARRAY);

	glBegin( GL_TRIANGLE_STRIP );			
	// Go through all of the rows of the height map
	for ( X = 0; X <= m_MapResolution; X += m_MapDetailLevel )
	{
		// Check if we need to render the opposite way for this column
		if(bSwitchSides)
		{	
			//glColor3f(0.0f,0.0f,1.0f);
			// Render a column of the terrain, for this current X.
			// We start at m_MapResolution and render down to 0.
			for ( Y = m_MapResolution; Y >= 0; Y -= m_MapDetailLevel )
			{
				// Get the (X, Y, Z) value for the bottom left vertex		
				x = X;//X+=MapdetailLevel 							
				y = Height(X, Y );	
				z = Y;//Y = m_MapResolution ;Y-=MapdetailLevel							
				// Set the fog coordinate for this vertex, depending on it's height
				// and the current depth of the fog.
				if(m_Fog)
					y > m_FogDepthLevel ? ManagerFog->SetVolumetricFogCoord(0) : ManagerFog->SetVolumetricFogCoord( -((float)y - m_FogDepthLevel));			
				
				// Set the current texture coordinate and render the vertex
				SetTextureCoord((float)x / (float)m_MapResolution,-(float)z / (float)m_MapResolution);
				glVertex3i(x*xmul, y*ymul, z*zmul);		
				// Get the (X, Y, Z) value for the bottom right vertex		
				x = X + m_MapDetailLevel; 
				y = Height(X + m_MapDetailLevel, Y ); 
				z = Y;
				// Set the fog coordinate for this vertex
				if(m_Fog)
					y > m_FogDepthLevel ? ManagerFog->SetVolumetricFogCoord(0) : ManagerFog->SetVolumetricFogCoord( -((float)y - m_FogDepthLevel));
				// Set the current texture coordinate and render the vertex
				SetTextureCoord((float)x / (float)m_MapResolution,-(float)z / (float)m_MapResolution);
				glVertex3i(x*xmul, y*ymul, z*zmul);			
			}
		}
		else
		{	
			//glColor3f(1.0f,0.0f,0.0f);
			// Render a column of the terrain, for this current X.
			// We start at 0 and render down up to m_MapResolution.
			for ( Y = 0; Y <= m_MapResolution; Y += m_MapDetailLevel )
			{
				// Get the (X, Y, Z) value for the bottom right vertex		
				x = X + m_MapDetailLevel; 
				y = Height(X + m_MapDetailLevel, Y ); 
				z = Y;
				// Set the fog coordinate for this vertex
				if(m_Fog)
					y > m_FogDepthLevel ? ManagerFog->SetVolumetricFogCoord(0) : ManagerFog->SetVolumetricFogCoord( -((float)y - m_FogDepthLevel));
				// Set the current texture coordinate and render the vertex
				SetTextureCoord((float)x / (float)m_MapResolution,-(float)z / (float)m_MapResolution);
				glVertex3i(x, y*ymul, z);
				// Get the (X, Y, Z) value for the bottom left vertex		
				x = X;							
				y = Height(X, Y );	
				z = Y;
				if(m_Fog)
					y > m_FogDepthLevel ? ManagerFog->SetVolumetricFogCoord(0) : ManagerFog->SetVolumetricFogCoord( -((float)y - m_FogDepthLevel));

				// Set the current texture coordinate and render the vertex
				SetTextureCoord((float)x / (float)m_MapResolution,-(float)z / (float)m_MapResolution);
				glVertex3i(x*xmul, y*ymul, z*zmul);		
			}
		}
		// Switch the direction the column renders to allow the fluid tri strips
		bSwitchSides = !bSwitchSides;
	}
	glEnd();
	if(m_Fog)ManagerFog->DisableFog();
	glMatrixMode(GL_TEXTURE);
			ManagerTexture->SetTexture(1,m_VecTexture.at(1));
		glDisableClientState(GL_TEXTURE_COORD_ARRAY);
		glPopMatrix();
	glMatrixMode(GL_MODELVIEW);
	/*
	glMatrixMode(GL_TEXTURE);
			ManagerTexture->SetTexture(2,m_VecTexture.at(2));
		glDisableClientState(GL_TEXTURE_COORD_ARRAY);
		glPopMatrix();
	glMatrixMode(GL_MODELVIEW);*/
	ManagerTexture->ResetMultitexture();
glPopMatrix();
UnGuard
}

