/*********************************************************************
*	Game Environment
*	SOURCE FILE
*	Autor:	Michal Jirouš
*	Datum: 16.9.2008
*	Soubor: game_environment.cpp
*	Popis: Trida vytvari okolni prostredi, coz je Sky box a Highmap, 
*			ktera generuje relief krajiny podle alfa slozky obrazku.
*			Textura obrazku je pak na tento povrch mapovana bud 1:1 nebo
*			je opakovana.
**********************************************************************/


#include "game_environment.h"
//#include "collision.h"
#include "textureslib.h"
#include "sys_console.h"

#include "ctrl_var_definition.h"
using namespace systemConsole;

CEnvironment environment;
CEnvironment::CEnvironment()
{
	m_fGroundHeight = ENVIRONMENT_DEFAULT_GROUND;
	m_fElemSize = ENVIRONMENT_DEFAULT_ELEM_SIZE;
	m_iTextureID = 0;
	setSkyDistance( ENVIRONMENT_DEFAUL_SKY_DIST );
	pCurrentPicture = NULL;
	m_fHeightPerAlphaUnit = ENVIRONMENT_MIN_ALPHA_UNIT;
	m_fSizePerPixel = ENVIRONMENT_SIZE_PER_PIXEL_DEFAULT;
	m_iTextureRepeat = ENVIRONMENT_DEFAULT_TEXTURE_REPEAT;
}

CEnvironment::~CEnvironment()
{
	decompile();
}

void CEnvironment::decompile()
{
	glDeleteTextures( 1, &m_iTextureID );
	for( list<EnvElem>::iterator iter = m_ElementsList.begin(); iter != m_ElementsList.end(); iter++ )
		glDeleteLists( (*iter).m_uiDListIndex, 1 );
	m_ElementsList.clear();

	for( list<EnvElem>::iterator iter = m_SkyBoxWalls.begin(); iter != m_SkyBoxWalls.end(); iter++ )
	{
		glDeleteLists( (*iter).m_uiDListIndex, 1 );
		glDeleteTextures( 1, &(*iter).m_uiTextureIndex );
	}
	m_SkyBoxWalls.clear();


	m_iTextureID = 0;
}

void CEnvironment::createHighMap()
{
	//alg::BoundingBox globalBounds = ObjectInfo::object_info.getMapObjectBounds();
	//	//collision.getCollisionOctreeBounds();

	//m_HighMapImage.setFilename("textures/highmaps/" + m_HighMapImage.getFilename() );
	//pCurrentPicture = textureLibrary.loadPixels( m_HighMapImage );

	//if( !pCurrentPicture )
	//{
	//	console << "Error while loading highmap -> file '" << m_HighMapImage.getFilename() << "' not found" << endline;
	//	return;
	//}

	//if( m_HighMapImage.bpp != 32 )	//potrebujeme alfa kanal
	//{
	//	console << "Error while creating highmap -> bpp is not 32" << endline;
	//	return;
	//}

	//TextureElement texture;
	//texture.image.setFilename( m_HighMapImage.getFilename() );


	//if( !textureLibrary.applyTexture( &texture ) )
	//	return;

	//m_iTextureID = texture.texture_id;

	//alg::Point center = globalBounds.getCenterPoint();

	//m_W = (m_HighMapImage.width-1) * m_fSizePerPixel;
	//m_H = (m_HighMapImage.height-1) * m_fSizePerPixel;

	//m_X = center.x - m_W / 2.0f;
	//m_Y = center.y - m_H / 2.0f;

	//float curr_x = m_X;
	//float curr_y = m_Y;
	//float curr_w = globalBounds.m_fBounds[ MIN_X ] - curr_x;
	//float curr_h = globalBounds.m_fBounds[ MIN_Y ] - curr_y;

	////now create basic 8 elements
	//makeElements( curr_x, curr_y, curr_w, curr_h );	//vlevo dole

	//curr_y = curr_y + curr_h;
	//curr_h = globalBounds.m_fBounds[ MAX_Y ] - globalBounds.m_fBounds[ MIN_Y ];

	//makeElements( curr_x, curr_y, curr_w, curr_h );	//vlevo uprostred

	//curr_y = curr_y + curr_h;
	//curr_h = m_Y + m_H - globalBounds.m_fBounds[ MAX_Y ];

	//makeElements( curr_x, curr_y, curr_w, curr_h );	//vlevo nahore

	//curr_x = curr_x + curr_w;
	//curr_w = globalBounds.m_fBounds[ MAX_X ] - globalBounds.m_fBounds[ MIN_X ];

	//makeElements( curr_x, curr_y, curr_w, curr_h );	//uprostred nahore

	//curr_x = curr_x + curr_w;
	//curr_w = m_X + m_W - globalBounds.m_fBounds[ MAX_X ] ;

	//makeElements( curr_x, curr_y, curr_w, curr_h );	//vpravo nahore

	//curr_y = m_Y;
	//curr_h = globalBounds.m_fBounds[ MIN_Y ] - curr_y;

	//makeElements( curr_x, curr_y, curr_w, curr_h );	//vpravo dole

	//curr_y = curr_y + curr_h;
	//curr_h = globalBounds.m_fBounds[ MAX_Y ] - globalBounds.m_fBounds[ MIN_Y ];

	//makeElements( curr_x, curr_y, curr_w, curr_h );	//vpravo dole

	//curr_x = m_X + globalBounds.m_fBounds[ MIN_X ] - m_X;
	//curr_w = globalBounds.m_fBounds[ MAX_X ] - globalBounds.m_fBounds[ MIN_X ];
	//curr_y = m_Y;
	//curr_h = globalBounds.m_fBounds[ MIN_Y ] - m_Y;

	//makeElements( curr_x, curr_y, curr_w, curr_h );	//uprostred dole

	//delete [] pCurrentPicture;
	//pCurrentPicture = NULL;
}


void CEnvironment::compile()
{
	createHighMap();
	createSkyBox();
}

unsigned char *CEnvironment::getPictureValues( int x_index, int y_index )
{
	
	return &pCurrentPicture[ (x_index*4)  + y_index * m_HighMapImage.width*4];
}

float CEnvironment::makeVertex( float x, float y )
{
	int x_index = (int)((x - m_X) / m_fSizePerPixel);
	int y_index = (int)((y - m_Y) / m_fSizePerPixel);

	float z = (float)(  getPictureValues( x_index, y_index )[3] - 128) * m_fHeightPerAlphaUnit + m_fGroundHeight;


	float coord_x = 0.0f, coord_y = 0.0f;
	if( m_iTextureRepeat )
	{
		coord_x = (x - m_X) / (float)m_HighMapImage.width;
		coord_y = (y - m_Y) / (float)m_HighMapImage.height;
	}
	else
	{
		coord_x = (float)x_index / (float)m_HighMapImage.width;
		coord_y = (float)y_index / (float)m_HighMapImage.height;
	}

	glTexCoord2f( coord_x, coord_y );
	
	
	
	glVertex3f( x, y, z );
	return z;
}
void CEnvironment::makeElements( float x, float y, float w, float h )
{
	if( (w < m_fElemSize || compareFloats(w,m_fElemSize) == 0) && (h < m_fElemSize || compareFloats(h,m_fElemSize) == 0) )
	{
		EnvElem element;
		element.m_uiDListIndex = glGenLists( 1 );
		
		//element.m_Bounds.updateData( alg::Point( x+w, y+h, 0) );	//TODO - potrebujeme i z slozku

		float curr_x = x;
		float curr_y = y;
		float curr_z = 0;

		glNewList( element.m_uiDListIndex, GL_COMPILE);
			glBindTexture( GL_TEXTURE_2D, m_iTextureID );
			glBegin( GL_TRIANGLE_STRIP );
				glColor4f( 1,1,1,1);

				curr_z = makeVertex( curr_x, curr_y );
				element.m_Bounds = alg::BoundingBox( alg::Point( curr_x, curr_y, curr_z) );

				curr_y = y + h;
				curr_z = makeVertex( curr_x, curr_y );
				element.m_Bounds.updateData( alg::Point( curr_x, curr_y, curr_z) );

				curr_x = x + w;
				curr_y = y;
				curr_z = makeVertex( curr_x, curr_y );
				element.m_Bounds.updateData( alg::Point( curr_x, curr_y, curr_z) );

				curr_x = x + w;
				curr_y = y + h;
				curr_z = makeVertex( curr_x, curr_y );
				element.m_Bounds.updateData( alg::Point( curr_x, curr_y, curr_z) );
			glEnd();
		glEndList();
		m_ElementsList.push_back( element );
	}
	else	//je treba jeste delit
	{
		if( w > h )
		{
			float newW = w / 2.0f;
			makeElements( x, y, newW, h );
			makeElements( x + newW, y, newW, h );
		}
		else
		{
			float newH = h / 2.0f;
			makeElements( x, y, w, newH );
			makeElements( x, y + newH, w, newH );
		}
	}
}



void CEnvironment::drawHighmap()
{
	glPushAttrib( GL_ENABLE_BIT );
	glEnable( GL_TEXTURE_2D );

	for( list<EnvElem>::iterator iter = m_ElementsList.begin(); iter != m_ElementsList.end(); iter++ )
		glCallList( (*iter).m_uiDListIndex );
	glPopAttrib();
}

#include "ctrl_gamevars.h"
void CEnvironment::createSkyBox()
{
	//zname uhlopricku a potrebujeme znat sirku
	//hodnotu pak zaokrouhlime
	float fSkyDistance = gamevarsLibrary::getfData( vardef::MAX_DISTANCE_NAME );
	float size = (float)(int)( sqrt( pow(fSkyDistance, 2.0f) / 3.0f ) ) + 1.0f;
	std::string sFinalTextureName;

	if( m_sSkyName.empty() )
		m_sSkyName = ENVIRONMENT_DEFAULT_SKY;

	sFinalTextureName = m_sSkyName + "rt"; //pravo

	createSkyBoxWall( alg::Point( size, size, -size ),
					  alg::Point( size, -size, -size ),
					  alg::Point( size, -size, size ),
					  alg::Point( size, size, size ),
					  sFinalTextureName );
	
	sFinalTextureName = m_sSkyName + "bk";	//vpravo

	createSkyBoxWall( alg::Point( -size, size, -size ),
					  alg::Point( size, size, -size ),
					  alg::Point( size, size, size ),
					  alg::Point( -size, size, size ),
					  sFinalTextureName );


	sFinalTextureName = m_sSkyName + "lf";	//vlevo
	
	createSkyBoxWall( alg::Point( -size, -size, -size ),
					  alg::Point( -size, size, -size ),
					  alg::Point( -size, size, size ),
					  alg::Point( -size, -size, size ),
					  sFinalTextureName );

	sFinalTextureName = m_sSkyName + "ft";

	createSkyBoxWall( alg::Point( size, -size, -size ),
					  alg::Point( -size, -size, -size ),
					  alg::Point( -size, -size, size ),
					  alg::Point( size, -size, size ),
					  sFinalTextureName );

	sFinalTextureName = m_sSkyName + "up";

	createSkyBoxWall(  alg::Point( size, size, size ),
					   alg::Point( size, -size, size ),
					   alg::Point( -size, -size, size ),
					   alg::Point( -size, size, size ),
					  sFinalTextureName );

	sFinalTextureName = m_sSkyName + "dn";
	
	createSkyBoxWall(  alg::Point( -size, size, -size ),alg::Point( -size, -size, -size ),
					   alg::Point( size, -size, -size ),
					   alg::Point( size, size, -size ),
					   
					  sFinalTextureName );
}


void CEnvironment::createSkyBoxWall( alg::Point &A, alg::Point &B, alg::Point &C, alg::Point &D, std::string sFilename )
{
	EnvElem element;
	element.m_uiDListIndex = glGenLists( 1 );
	element.m_Bounds = alg::BoundingBox( A );
	element.m_Bounds.updateData( C );

	sFilename += ".tga";
	TextureElement texture;
	texture.image.setFilename( "textures/env/" + sFilename );
	textureLibrary.setTextureWraps( &texture, GL_CLAMP_TO_EDGE, GL_CLAMP_TO_EDGE  );
	
	if( !textureLibrary.applyTexture( &texture ) )
		return;

	element.m_uiTextureIndex = texture.texture_id;

	glNewList( element.m_uiDListIndex, GL_COMPILE );
		glColor4f( 1,1,1,1);
		glBindTexture( GL_TEXTURE_2D, element.m_uiTextureIndex );
		glBegin( GL_TRIANGLE_STRIP );
			glTexCoord2f( 0,0 );	glVertex3f( A.x, A.y, A.z );
			glTexCoord2f( 0,1 );	glVertex3f( D.x, D.y, D.z );
			glTexCoord2f( 1,0 );	glVertex3f( B.x, B.y, B.z );
			glTexCoord2f( 1,1 );	glVertex3f( C.x, C.y, C.z );
		glEnd();
	glEndList();


	m_SkyBoxWalls.push_back( element );
}

//vykresli vsech 6 sten sky boxu
void CEnvironment::drawSkyBox()
{
	glPushAttrib( GL_ENABLE_BIT );
	glEnable( GL_TEXTURE_2D );
	
	for( list<EnvElem>::iterator iter = m_SkyBoxWalls.begin(); iter != m_SkyBoxWalls.end(); iter++ )
		glCallList( (*iter).m_uiDListIndex );

	glPopAttrib();
}
