/*********************************************************************//**
*	Passworder.
*	realizuje objekt, na kterem je traba zadat ciselny kod, aby 
*	doslo k aktivaci.
*	
*	author: Michal Jirous
*	date: 23.04.2009
*	file: ent_func_passworder.cpp
**********************************************************************/

///*********************************************************************
//*	Passworder
//*	SOURCE FILE
//*	Autor:	Michal Jirouš
//*	Datum: 23.9.2008
//*	Soubor: object_info.h
//*	Popis: Entita provozuje bezpecnosti zarizeni pro provadeni
//*			urcite operace pouze po zadani prislusneho hesla.
//**********************************************************************/
//
#include "game.h"
#include "ent_func_passworder.h"
#include "sys_config.h"
#include "ctrl_gamevars.h"
#include "ctrl_var_definition.h"
#include "HUD.h"
#include "HUD_crosshair.h"

Passworder::Passworder()
{
	m_sClassName = "func_passworder";
	m_fTotalWidth = 0;
	m_fTotalHeight = 0;
	m_pDigitsFont = NULL;
	m_fAlpha = 0.0f;
	m_uiFadeOutTime = 0;
	m_iStatus = LOCKED;
	m_iProperties = ENT_PARM_COLLIDING | ENT_PARM_INTERACTIVE | ENT_PARM_POINTING | ENT_PARM_RENDER | ENT_PARM_RUNNABLE | ENT_PARM_SHOOTABLE;

	m_fXAngle = 0.0f;
	m_fZAngle = 0.0f;
}

//
void Passworder::update(float seconds)
{
	CBaseFunction::update( seconds );
	if( m_uiResetTime + 10000 < game.getGameTime() && !m_sCurrentInsertedPassword.empty() )
	{
		m_sCurrentInsertedPassword.clear();
	
	}

	float distance = Vector(game.m_pPlayer->getRenderOrigin(), m_MyCenterPoint ).absolute();
	if( distance < 128.0f )
		m_uiFadeOutTime = game.getGameTime();


	if( m_uiFadeOutTime + 5000 < game.getGameTime() )
	{
		m_fAlpha -= getFromRange( 0.05f, 0.0f, m_fAlpha );
	
	}
	else if( distance < 128.0f )
		m_fAlpha += getFromRange( 0.2f, 0.0f, 1.0f-m_fAlpha);



	if( m_iStatus == WRONG && m_uiWrongResetTime + 2000 < game.getGameTime() )
	{
		m_iStatus = LOCKED;
		m_sCurrentInsertedPassword.clear();
	}
}
//
//
void Passworder::setParameters( parameters_t &parametersMap )
{
	CBaseFunction::setParameters( parametersMap );

	m_sButtonMap = getParameterValue( parametersMap, "buttonmap" ); 

	m_sTargetTexts  = getParameterValue( parametersMap, "targettexts" ); 

	std::string value =  getParameterValue( parametersMap, "width" );
	if( !value.empty() )
		m_fTotalWidth = (float)atof( value.c_str() );

	value =  getParameterValue( parametersMap, "height" );
	if( !value.empty() )
		m_fTotalHeight = (float)atof( value.c_str() );
	

	value =  getParameterValue( parametersMap, "passwordlen" );
	if( !value.empty() )
		m_iPassWordLength = getFromRange( (unsigned)atoi( value.c_str()), 1u, PASSWORD_MAX_LENGTH );



	value = getParameterValue( parametersMap, "lockedsound" );
	int key = 0;
	if( !value.empty() )
		key = atoi( value.c_str() );
	
	if( key != -1 )
	{
		generalSounds.setLockSound( key, m_LockedSound );
		m_LockedSound.setPosition( origin.x, origin.y, origin.z );
	}

	value = getParameterValue( parametersMap, "unlockedsound" );
	if( !value.empty() )
		key = atoi( value.c_str() );
	
	if( key != -1 )
	{
		generalSounds.setUnLockSound( key, m_UnlockSound );
		m_UnlockSound.setPosition( origin.x, origin.y, origin.z );
	}


	soundLibrary.loadSound( m_ButtonSound, BUTTONS_SOUND );
	m_ButtonSound.setPosition( origin.x, origin.y, origin.z );
}


void Passworder::generatePassWord()
{
	//smazeme aktualni heslo
	m_sPassword.clear();
	
	//vygenerujeme nove
	for( int i = 0; i < m_iPassWordLength; ++i )
		 m_sPassword+= '0' + rand()%10;

	//nastavime heslo nejakemu textu
	for( std::list<EnvText*>::iterator iter = m_TargetTexts.begin(); iter != m_TargetTexts.end(); ++iter )
		(*iter)->setText( m_sPassword );
}


#include "level_loader.h"
bool is_that_pass_face( Face &face, TextureElement *texture )
{
	if( face.pTexture->m_pTextureElement == texture )
		return true;
	return false;
}

void Passworder::compile()
{
	

	CBaseFunction::compile();
	TextureElement *tmpPassTex = textureLibrary.getTexture( "~~PASS~~" );
	m_pFace = levelLoader.forEachFaceDo( m_SolidModel, (bool(*)(Face&,void*))is_that_pass_face, tmpPassTex );
	
	if( !m_pFace )	//neexistuje
		return;

	m_MyCenterPoint = m_pFace->m_Bounds.getCenterPoint();
	Plane plane = levelLoader.m_LevelData.planes[m_pFace->planenum];
	if( m_pFace->side )
		plane.m_vecNormal = -plane.m_vecNormal;


	

	Vector horizontal( plane.m_vecNormal );
	horizontal.z = 0.0f;


	Vector Z_axis = Vector( 0,-1,0 ) * horizontal;
	m_fZAngle = Vector( 0,-1,0 ).scalarMultiply( horizontal );
	if( compareFloats( m_fZAngle, 0, FLOAT_COMPARE_ACCURACY_HIGH ) == 0 ||
		compareFloats( m_fZAngle, 180, FLOAT_COMPARE_ACCURACY_HIGH ) == 0 )
		Z_axis.z = 1;

	Vector X_axis = horizontal * plane.m_vecNormal;
	m_fXAngle = horizontal.scalarMultiply( plane.m_vecNormal );

	
		



	createControlPanel();	//vytvori se panely z pixelu obrazku
	if( m_Elements.empty() )
		return;

	m_fLabelX = (m_Elements.back().x - m_fTotalWidth  / 2.0f) * m_fXAdapt;
	m_fLabelY = (m_Elements.back().y - m_fTotalHeight  / 2.0f) * m_fYAdapt;
	m_fLabelW = (m_Elements.back().w) * m_fXAdapt;
	m_fLabelH = (m_Elements.back().h) * m_fYAdapt;

	m_pDigitsFont = fontLibrary::getFont( "HEALTH" ); 
	if( m_pDigitsFont )
	{
		float fFontHeight = (float)m_pDigitsFont->m_iLetterHeight;
		m_fLabelScale = getFromRange( (m_fLabelH-2) / fFontHeight, 0.0f ,1.0f);

		m_fLabelX = (m_fLabelX + 1.0f)/m_fLabelScale;
		m_fLabelY = (m_fLabelY+1+m_fLabelH/2.0f - ((float)m_pDigitsFont->m_iLetterHeight /2.0f)*m_fLabelScale ) / m_fLabelScale;
	}

	m_Elements.pop_back();	//posledni je label a ten nedetekujeme

	createPickingDList();
	createDigits();





	entityList_t *pList = levelLoader.m_Entities.getEntitiesByName( m_sTargetTexts );
	if( pList )
	{
		for( entityList_t::iterator iter = pList->begin(); iter != pList->end(); ++iter )
		{
			if( (*iter)->getClassName() == "env_text" )
				m_TargetTexts.push_back( reinterpret_cast<EnvText*>( (*iter) ) );
		}
	
	}



	generatePassWord();



	m_transformationDList = glGenLists( 1 );

	glNewList( m_transformationDList, GL_COMPILE );
		//glRotatef( m_fXAngle, X_axis.x, X_axis.y, X_axis.z );
		glRotatef( m_fZAngle, Z_axis.x, Z_axis.y, Z_axis.z );
	glEndList();

}


void Passworder::createControlPanel()
{
	ImageInfo image;
	
	image.setFilename( "textures/" + m_sButtonMap );
	unsigned char * pCurrentImage = textureLibrary.loadPixels( image );
	textureLibrary.swapRows( pCurrentImage, image );
	if( !pCurrentImage || image.bpp != 32 )	//potrebujeme alpha kanal
		return;


	m_pFace->pTexture->unload();	//texturu, ktera je na facu se nebude pouzivat..

	//podivame se, jestli je textura v databazi
	TextureElement *pTexture = textureLibrary.getTexture( m_sButtonMap );
	if( pTexture )
	{
		m_pFace->pTexture = m_TexturesPool.Pool_applyTexture( m_sButtonMap );
	}
	else
	{
		pTexture = textureLibrary.addEntry( m_sButtonMap );
		if( !pTexture )
			return;
		pTexture->image.setFilename( image.getFilename() );

		m_pFace->pTexture = m_TexturesPool.Pool_applyTexture( m_sButtonMap );
	
	}

	if( !m_pFace->pTexture )
		return;

	
	m_fXAdapt = m_fTotalWidth / image.width;
	m_fYAdapt = m_fTotalHeight / image.height;

	int iElemID = 1;
	int iRowID = 3;
	int iColor = 1;
	for( unsigned int i = 0; i < image.height*image.width; i++ )
	{
		unsigned char alpha = pCurrentImage[i*4+3];
		if( alpha == 255 )	//zajimaji nas pouze nepruhledne hodnoty
		{
			int x = i % image.width;
			int y = i / image.height;
			bool bFounded = false;
			for( list<PickElem>::iterator iter = m_Elements.begin(); iter != m_Elements.end(); iter++ )
			{
				if( (*iter).x - 0.1f < x && (*iter).x + (*iter).w + 1 +0.1f > x &&
					(*iter).y-0.1f < y && (*iter).y + (*iter).h + 1 +0.1f > y )
				{
					(*iter).w = max( (*iter).w, x - (*iter).x );
					
					(*iter).h = max( (*iter).h, y - (*iter).y );

					bFounded = true;
					break;
				}
			
			}
		
			if( !bFounded )
			{
				PickElem newElem;
				m_Elements.push_back( newElem );
				m_Elements.back().x = (float)x;
				m_Elements.back().y = (float)y;
				m_Elements.back().w = 0;
				m_Elements.back().h = 0;
				m_Elements.back().m_iCommand = iRowID * 3 + iElemID++;
				m_Elements.back().m_Color[2] = m_Elements.back().m_iCommand;
				
				if( iElemID == 4 )
				{	
					iRowID--;
					iElemID = 1;
				}
			}
		}
	}

	delete [] pCurrentImage;
}


/* Tato funkce vytvori display list, ktery bude obsahovat pole prislusici jednotlivym tlacitkum a
tato pole budou mit jedinecnou barvu dle ktere se pak identifikuji pri pickingu. */
void Passworder::createPickingDList()
{
	m_uiPickingDList = glGenLists( 1 );
	glNewList( m_uiPickingDList, GL_COMPILE );
		
		for( list<PickElem>::iterator iter = m_Elements.begin(); iter != m_Elements.end(); iter++ )
		{
			(*iter).x = (*iter).x*m_fXAdapt -m_fTotalWidth/2.0f;
			(*iter).w *= m_fXAdapt;
			(*iter).y = (*iter).y*m_fYAdapt -m_fTotalWidth/2.0f;
			(*iter).h *= m_fYAdapt;

			glBegin( GL_QUADS );
				glColor3ubv( (*iter).m_Color );
				glVertex3f( (*iter).x, -0.1f, (*iter).y );
				glVertex3f( (*iter).x, -0.1f, (*iter).y + (*iter).h );
				glVertex3f( (*iter).x + (*iter).w, -0.1f, (*iter).y + (*iter).h );
				glVertex3f( (*iter).x + (*iter).w, -0.1f, (*iter).y );
			glEnd();
		}

	glEndList();
}




void Passworder::createDigits()
{	
	if( m_pDigitsFont )
	{
		float fMinHeight = (float)m_pDigitsFont->m_iLetterHeight;
		for( list<PickElem>::iterator iter = m_Elements.begin(); iter != m_Elements.end(); ++iter )
		{
			fMinHeight = min( fMinHeight, (*iter).h );
		}

		float fScale = fMinHeight / (float)m_pDigitsFont->m_iLetterHeight;
		m_uiDigitsDList = glGenLists( 1 );
		glNewList( m_uiDigitsDList, GL_COMPILE );
			glPushMatrix();
			glScalef( fScale, fScale, 0.0f );
				for( list<PickElem>::iterator iter = m_Elements.begin(); iter != m_Elements.end(); ++iter )
				{
					std::string sText;
					if( (*iter).m_iCommand > 0 && (*iter).m_iCommand < COMMAND_X )
						sText += (char)(*iter).m_iCommand + '0';
					else if( (*iter).m_iCommand == COMMAND_X )
						sText = "X";
					else if( (*iter).m_iCommand == NUMBER_0 )
						sText = "0";
					else if( (*iter).m_iCommand == COMMAND_OK )
					{
						//TextureElement *tmpTex = NULL;
						//if( (tmpTex = textureLibrary.addEntry( "~~PASS-A~~" )) )
						//{
						//	tmpTex->image.setFilename( "textures/accept.tga" );
						//}
						//
						//tmpTex = textureLibrary.applyTexture( "~~PASS-A~~" );
						//if( tmpTex )
						//{
						//	glEnable( GL_BLEND );
						//	glBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA );
						//	glBindTexture( GL_TEXTURE_2D, tmpTex->texture_id );
						//	glBegin( GL_QUADS );
						//		glTexCoord2i(0,0); glVertex3f( (*iter).x/ fScale, (*iter).y/ fScale, -2 );
						//		glTexCoord2i(0,1); glVertex3f( (*iter).x/ fScale, ((*iter).y + (*iter).h)/ fScale, -2 );
						//		glTexCoord2i(1,1); glVertex3f( ((*iter).x + (*iter).w)/ fScale, ((*iter).y + (*iter).h)/ fScale, -2 );
						//		glTexCoord2i(1,0); glVertex3f( ((*iter).x + (*iter).w)/ fScale, ((*iter).y)/ fScale, -2 );
						//	glEnd();
						//}
						////hm...ze by nejaka textura?
						continue;
					}
					
					float width = m_pDigitsFont->getCharacterWidth( sText.at(0) ) * fScale;

					fontLibrary::simpleDrawText( sText, ((*iter).x + (*iter).w/2.0f -width /2.0f) / fScale, ((*iter).y+1)/fScale, m_pDigitsFont);


				}
			glPopMatrix();
		glEndList();
	}
}
//
//
//
//
void Passworder::decompile()
{
	CBaseFunction::decompile();
	glDeleteLists( m_uiDigitsDList, 1 );
	glDeleteLists( m_uiPickingDList, 1 );
	soundLibrary.unloadSound( m_UnlockSound );
	soundLibrary.unloadSound( m_LockedSound );
	//textureLibrary.unloadTexture( "~~PASS-A~~" );
	glDeleteLists( m_transformationDList, 1 );
}
//
void Passworder::drawMyFace()
{
	if( m_fAlpha < 0.0001f )
		return;

	glPushAttrib( GL_ENABLE_BIT | GL_CURRENT_BIT );
	////glDisable( GL_TEXTURE_2D );
	glPushMatrix();

	
	glTranslatef( m_MyCenterPoint.x, m_MyCenterPoint.y, m_MyCenterPoint.z );
	
	
	glCallList( m_transformationDList );
	
	
	
	glRotatef(90.0f, 1.0f, 0.0f, 0.0f);	//zmena os..zamena Y za Z protoze text se vykresluje x, y a ja potrebuju x, z
	
	
	glTranslatef( 0.0f, 0.0f, 0.2f );	//aby to bylo trochu od polygonu	
	glScalef( m_fLabelScale, m_fLabelScale, 0.0f);

	
	switch( m_iStatus )
	{
		case LOCKED:
			glColor4f(1.0f,0.8f,0.2f,m_fAlpha);
			break;
		case WRONG:
			glColor4f(1.0f,0.0f,0.0f,m_fAlpha);
			break;
		case UNLOCKED:
			glColor4f(0.2f,1.0f,0.0f,m_fAlpha);
			break;
		
	
	}

	
	glDisable( GL_CULL_FACE );
	
	if( m_pDigitsFont )
	//glTranslatef( -m_fTotalWidth/2.0f, 0.0f, -m_fTotalHeight/2.0f );
		fontLibrary::simpleDrawText( m_sCurrentInsertedPassword, m_fLabelX, m_fLabelY, m_pDigitsFont);

	glScalef( 1.0f/m_fLabelScale, 1.0f/m_fLabelScale, 0.0f);
	
	if( m_uiDigitsDList )
		glCallList( m_uiDigitsDList );

	glPopMatrix();
	glPopAttrib();
}
//

//
//
//

#include "level_render.h"
void Passworder::onUsage( const RayTraceData &rayData )
{
	glPushAttrib( GL_ENABLE_BIT | GL_CURRENT_BIT );

	glDisable(GL_TEXTURE_2D);
	glDisable(GL_FOG);
	glDisable(GL_LIGHTING);
	glDisable(GL_DEPTH_TEST );

	glColor4f( 0.0f, 0.0f, 0.0f, 1.0f );	//pozadi pro jistotu
	

	game.setBasicDrawSettings();
	game.setBasicDrawMatrixes();
	game.setCurrentTranslateMatrix();
	renderer.renderFace( *m_pFace );
	glTranslatef( m_MyCenterPoint.x, m_MyCenterPoint.y-0.1f, m_MyCenterPoint.z );
	
	
	glCallList( m_transformationDList );

	//a nyni picking objekty
	glCallList( m_uiPickingDList );

	unsigned char pixel[3];
	GLint viewport[4];
    glGetIntegerv(GL_VIEWPORT, viewport);

	glReadPixels(viewport[2]/2, viewport[3]/2, 1, 1, GL_RGB, GL_UNSIGNED_BYTE, pixel);
	command( pixel[2] );
	glPopAttrib();
}
//
void Passworder::addPasswordChar( char c )
{
	if( m_sCurrentInsertedPassword.length() < PASSWORD_MAX_LENGTH )
	{
		m_sCurrentInsertedPassword += c;
		m_ButtonSound.play();
	}
	m_uiResetTime = game.getGameTime();

}


void Passworder::restart()
{
	CBaseFunction::restart();
	m_sCurrentInsertedPassword.clear();
	m_iStatus = LOCKED;
	m_uiFadeOutTime = 0;
	m_uiWrongResetTime = 0;
	m_uiResetTime = 0;
	generatePassWord();
}
//
void Passworder::command( int id )
{
	if( m_iStatus == UNLOCKED )
		return;
	

	if( id > 0 && id < COMMAND_X )	//cisla
	{
		addPasswordChar( (char)id + '0' );
	}
	else
	{
		switch( id )
		{
			case COMMAND_X:
				m_ButtonSound.play();
				m_sCurrentInsertedPassword.clear();
				break;
			case NUMBER_0:
				addPasswordChar( '0' );
				break;
		}
	}

	if( m_sCurrentInsertedPassword.length() == m_sPassword.length() )
	{
		if( m_sCurrentInsertedPassword == m_sPassword )
		{
			m_iStatus = UNLOCKED;
			trigger();
		}
		else
		{
			m_uiWrongResetTime = game.getGameTime();
			m_iStatus = WRONG;
			m_LockedSound.playIfNotPlaying();
		}
	
	}
}


void Passworder::render( RenderData &renderData )
{
	setRenderMode();
	basicSolidRender( renderData );

	drawMyFace();

	unsetRenderMode();

}


void Passworder::trigger()
{
	if( m_TargetList )
		for( entityList_t::iterator iter = m_TargetList->begin(); iter != m_TargetList->end(); ++iter )
			(*iter)->onTarget( 0 );

	m_BackTimeData.push_front( BackTimeData() );
	m_BackTimeData.front().event_time = game.m_uiGameTime;
	m_BackTimeData.front().type = EVENT_TRIGGER;

}


void Passworder::passStoredEvent( BackTimeData &backData )
{
	CBaseFunction::passStoredEvent( backData );
	if( backData.type == EVENT_TRIGGER )
	{
		trigger();
		m_iStatus = UNLOCKED;
	}

}

void Passworder::goBackInTime()
{
	CBaseFunction::goBackInTime();
	
	for( std::list<BackTimeData>::reverse_iterator riter = m_BackTimeData.rbegin(); riter != m_BackTimeData.rend(); ++riter )
	{
		//jakmile nalezneme border, tak koncime pruchod
		if( (*riter).type == EVENT_BORDER )
			return;

		if( (*riter).type == EVENT_TRIGGER )
		{
			m_iStatus = LOCKED;
			break;
		}
	}
}
