/*********************************************************************//**
*	\brief Sprite element.
*	Zde je implementace objektu Sprite, ktery se stara o ovladani spritu, 
*	coz je jeho aktualizace v case, vykreslovani, zmena prehravani.
*
*
*	author: Michal Jirous
*	date: 22.1.2008
*	file: sprite.cpp
**********************************************************************/

#include "spriteslib.h"
#include <SDL/SDL_opengl.h>
void Sprite::reset()
{
	m_fCurrentFrame = 0;

}

void Sprite::unload()
{
	spritesLibrary.unloadSprite( *this );
}

/** @param frame Cislo snimku. */
void Sprite::setFrame(float frame)
{
	m_fCurrentFrame = frame;
}

/** @return Aktualni cislo snimku. */
float Sprite::getFrame()
{
	return m_fCurrentFrame;
}

void Sprite::play()
{
	m_bIsPlaying = true;
}

int Sprite::getRenderType()
{
	if( m_pCoreSprite )
		return m_pCoreSprite->m_iBlendType;
	return NORMAL;
}

float Sprite::getRadius()
{

	if( m_pCoreSprite )
	{
		float maximum = (float)(std::max( m_pCoreSprite->width, m_pCoreSprite->height ));
		return sqrt( 3*maximum * maximum );	//3 * a ^ 2
	}

	return 0.0f;
}

/** @param elapset_ms Pocet ubehnutych milisekund od minule aktualizace. */
void Sprite::update( int elapsed_ms )
{
	if( !m_bIsPlaying )
		return;

	if( m_pCoreSprite )
		m_fCurrentFrame += (elapsed_ms * m_pCoreSprite->m_uiFps) / 1000.0f;

	if( !m_bIsLooping )
	{
		if( m_fCurrentFrame > m_pCoreSprite->m_uiNumFrames )
		{
			m_fCurrentFrame -= m_pCoreSprite->m_uiNumFrames;
			m_bIsPlaying = false;
		}
		else if( m_fCurrentFrame < -((float)m_pCoreSprite->m_uiNumFrames) )
		{
			m_fCurrentFrame += m_pCoreSprite->m_uiNumFrames;
			m_bIsPlaying = false;
		}
	}
}


void Sprite::drawWithSpecificAxis( const alg::Vector &toCameraDirection )
{


}

void Sprite::drawPrologue()
{
	glPushAttrib( GL_ENABLE_BIT | GL_CURRENT_BIT );
		//glDisable( GL_CULL_FACE );
		glEnable( GL_TEXTURE_2D );
		glPushMatrix();
		
		//dle parametru vykreslovani se nastavi parametry opengl
		switch( m_pCoreSprite->m_iBlendType )
		{
			case ALPHA_TEST:
				glEnable( GL_ALPHA_TEST );
				break;
			case BLACK_BLEND:
				glEnable( GL_BLEND );
				glBlendFunc( GL_ONE, GL_ONE );
				break;
			case ALPHA_BLEND:
				glEnable( GL_BLEND );
				glBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA );
				break;
			case NORMAL:
			default:
				break;
		}
		
}

void Sprite::drawEpilogue()
{
	glPopAttrib();
	glPopMatrix();
}

/** @param cameraPosition Pozice kamery v prostoru.
*	@param axis Uzivateslky nastavena osa spritu.
*/
void Sprite::draw( const alg::Point &cameraPosition, const Axis &axis )
{
	if( m_pCoreSprite )
	{
		drawPrologue();
		glDisable( GL_CULL_FACE );
		float fSpriteHeight = (float)m_pCoreSprite->height;
		float fLength;
		Vector vecLength = axis.B - axis.A;
		Vector vecFromCamera = cameraPosition - axis.A;
		float fTexValue = 1.0f;

		switch( m_iAxisType )
		{
			case AXIS_TYPE_SPECIFICATED_TEXTURE_ORIGINAL:
				fLength = fSpriteHeight;
				vecLength.normalize();
				vecLength = vecLength * fLength;
				break;
			case AXIS_TYPE_SPECIFICATED_TEXTURE_COPY:
				fLength = vecLength.absolute();
				fTexValue = fLength / (float)m_pCoreSprite->height;
				break;
			case AXIS_TYPE_SPECIFICATED_TEXTURE_EXPAND:
				fLength = vecLength.absolute();
				break;
		}

		Vector side = Vector( vecFromCamera * vecLength, (float)m_pCoreSprite->width/2.0f );


		drawFrameSelect();
		
		
		glBegin( GL_QUADS );
			glTexCoord2f( 1, 0  );	//left top
			glVertex3f( axis.A.x + side.x, axis.A.y + side.y, axis.A.z + side.z );
			
			glTexCoord2f( 0,0 );
			glVertex3f( axis.A.x - side.x, axis.A.y - side.y, axis.A.z - side.z );

			glTexCoord2f( 0, fTexValue );
			glVertex3f( axis.A.x - side.x + vecLength.x, axis.A.y - side.y + vecLength.y, axis.A.z - side.z + vecLength.z );

			glTexCoord2f( 1,fTexValue );
			glVertex3f( axis.A.x + side.x + vecLength.x, axis.A.y + side.y + vecLength.y, axis.A.z + side.z + vecLength.z );

		glEnd();



		drawEpilogue();
	}
}



/** @param cameraPosition Pozice kamery v prostoru.
*	@param targetPosition Stredovy bod spritu (v pripade uzivatelsky zadane funkce na tomto parametru nezalezi a je treba nastavovat osu pred vykreslenim).
*/
void Sprite::draw( const alg::Vector &cameraPosition, const alg::Vector &targetPosition )
{
	if( m_pCoreSprite && m_iAxisType < AXIS_TYPE_SPECIFICATED_TEXTURE_ORIGINAL)
	{
		drawPrologue();

		//pomocne vektory pro vypocet orientace
		Vector vecTargetVector = ( cameraPosition - targetPosition );

		//nejprve se posuneme na cilovou pozici
		glTranslatef( targetPosition.x, targetPosition.y, targetPosition.z);

		//natoceni vzdy k hraci a nebo pouze otoceni podle osy Z
		if( m_iAxisType == AXIS_TYPE_NO_AXIS || m_iAxisType == AXIS_TYPE_Z_AXIS )
		{
			Vector vecHorizontal = vecTargetVector;
			vecHorizontal.z = 0.0f;
			
			//otoceni dle osy Z
			float angle1 = vecHorizontal.scalarMultiply( Vector( 0,-1,0) );	//testujeme uhel s osou Y

			//potrebujeme svislou osu, ale musime zjistit jestli vektor smeruje nahoru nebo dolu
			Vector axis;
			if( vecHorizontal.x < 0 )	//po vektorovem soucinu s 0,-1,0 bude smerovat vysledek dolu
				axis.z = -1.0f;
			else						//v opacnem pripade nahoru
				axis.z = 1.0f;

			if( m_iAxisType == AXIS_TYPE_NO_AXIS )	//bez osy se musi sprite jeste natocit podle horizontalni osy
			{
				//otoceni dle horizontalni osy
				//float angle = get360VectorsAngle( vecHorizontal, vecTargetVector );
				//if( compareFloats( vecTargetVector.z, vecHorizontal.z, FLOAT_COMPARE_ACCURACY_PRECISION ) ) //osetreni pripadu, kdy neni treba nic otacet (mala pravdepodobnost)
				//{
					float angle = vecHorizontal.scalarMultiply( vecTargetVector );	//zjistime uhel otoceni dle horizontalni osy
					vecHorizontal = vecHorizontal * vecTargetVector;		//spocitame osu otoceni
					glRotatef( angle, vecHorizontal.x, vecHorizontal.y, vecHorizontal.z );	//otocime souradny system
				//}
			}
			
			glRotatef( angle1, 0,0,axis.z );	//nakonec otocime v ose z
		}
		//otoceni a drzeni se osy X
		else if( m_iAxisType == AXIS_TYPE_X_AXIS )	
		{
			Vector vecTmp = vecTargetVector;
			vecTmp.x = 0.0f;

			//float angle = get360VectorsAngle( vecTmp, Vector( 0,0,1) );
			float angle = vecTmp.scalarMultiply( Vector( 0,0,1) );
			
			if( vecTmp.y < 0 )
				vecTmp.x = 1.0f;
			else
				vecTmp.x = -1.0f;

			//vecTmp = Vector( 0,0,1) * vecTmp;
			glRotatef( angle, vecTmp.x, 0,0 );
			glRotatef( -90.0f, 0,1,0 );
			glRotatef( 90.0f, 0,0,1 );
		}
		//otoceni a drzeni se osy Y
		else if( m_iAxisType == AXIS_TYPE_Y_AXIS )
		{
			Vector vecTmp = vecTargetVector;
			vecTmp.y = 0.0f;

			//float angle = get360VectorsAngle( vecTmp, Vector( 0,0,1) );
			float angle = vecTmp.scalarMultiply( Vector( 0,0,1) );

			if( vecTmp.x < 0 )
				vecTmp.y = -1.0f;
			else 
				vecTmp.y = 1.0f;

			glRotatef( angle, 0, vecTmp.y,0 );
			glRotatef( -90.0f, 1,0,0 );
		}


		drawFrameSelect();
		glCallList( m_pCoreSprite->m_uiDListIndex );	
	

		
		drawEpilogue();
	}
}


void Sprite::drawFrameSelect()
{
	int iFrame = (int)m_fCurrentFrame % m_pCoreSprite->m_uiNumFrames;
	if( iFrame < 0 )
		iFrame += m_pCoreSprite->m_uiNumFrames;

	glBindTexture( GL_TEXTURE_2D, m_pCoreSprite->m_Textures[ iFrame ] );
}


