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

#include "giofx.h"

static float back_vArray[]={-1,-1,0, +1,-1,0, +1,+1,0,  -1,-1,0, +1,+1,0,  -1,+1,0};
static cVertex tVertex[360];

//---------------------------------------------------------------------------------
//
//---------------------------------------------------------------------------------
void cFlare::SetActive(bool activate)
{
	active = activate;
}
//---------------------------------------------------------------------------------
//
//---------------------------------------------------------------------------------
void cFlare::MakeLensFlare(float sx,float sy,float P,float S,float r,float g,float b,float a)
{	

	float increment		= DOUBLEPI/(float)shape;
	int vCount = 0;
	
	//questo sfuma il colore dal centro
	if (filled)
	{
		glColor4f(r,g,b,a*0.5f);  
		tVertex[vCount++].Set(sx*P,sy*P,0.0f);
		glColor4f(0,0,0,0);  
	}
	else
    {
		tVertex[vCount++].Set(sx*P,sy*P,0.0f); 
		glColor4f(r,g,b,a*0.5f);  
	}

	for (float t=0.0f; t<DOUBLEPI; t+=increment)
        tVertex[vCount++].Set(sx*P+(float)sin(t)*S*a,sy*P+(float)cos(t)*S*a,0.0f);
    
    glVertexPointer(3, GL_FLOAT, 0, &tVertex[0]);
	glDrawArrays(GL_TRIANGLE_FAN,0,vCount);  //TRIANGLES
}
//--------------------------------------------------------------------------------------------------
void cFlare::RenderLensFlare()
{

    float	sx,sy,sz;
	float   pMat[16];
	float	mMat[16];	

	glGetFloatv(GL_MODELVIEW_MATRIX, mMat);
	glGetFloatv(GL_PROJECTION_MATRIX,pMat);
	_gluProject(position.x,position.y,position.z,mMat,pMat,parent->viewport,&sx,&sy,&sz);
	
	glFrontFace(GL_CW);
	glDisable(GL_DEPTH_TEST);
	glDisable(GL_TEXTURE_2D);
	glDisableClientState(GL_TEXTURE_COORD_ARRAY);
	
	glPushMatrix();
	glLoadIdentity();
	glMatrixMode(GL_PROJECTION);
	glPushMatrix();
	glLoadIdentity();
	
	sx = (sx + parent->viewport[0])/(float)parent->viewport[2] *2.0f -1.0f;
	sy = (sy - parent->viewport[1])/(float)parent->viewport[3] *2.0f -1.0f;

	float br = (1.2f - (float)sqrt(sx*sx+sy*sy))*(color.a)*flareSize;

	if (brightLevel)
	{
		glColor4f(color.r,color.g,color.b,br*brightLevel);             
		glVertexPointer(3, GL_FLOAT, 0, &back_vArray[0]);
		glDrawArrays(GL_TRIANGLES,0,6); 
	}
	
	switch(flareSet){

		case 0:
		case 4:
			MakeLensFlare((float)sx,(float)sy, 0.7f,0.2f,color.r,color.g,color.b,br); 
			MakeLensFlare((float)sx,(float)sy, 0.4f,0.4f,color.r,color.g,color.b,br); 
			MakeLensFlare((float)sx,(float)sy, 0.2f,0.5f,color.r,color.g,color.b,br);
			MakeLensFlare((float)sx,(float)sy,-0.2f,0.3f,color.r,color.g,color.b,br);
			MakeLensFlare((float)sx,(float)sy,-0.6f,0.4f,color.r,color.g,color.b,br);
			MakeLensFlare((float)sx,(float)sy,-0.8f,0.6f,color.r,color.g,color.b,br);
			MakeLensFlare((float)sx,(float)sy,-1.2f,0.3f,color.r,color.g,color.b,br);
			if (flareSet==4)
				break;

		case 1:
			MakeLensFlare((float)sx,(float)sy, 0.6f,0.3f,1.0f,0.3f,0.3f,br); 			
			MakeLensFlare((float)sx,(float)sy, 0.3f,0.6f,0.8f,0.8f,0.3f,br);			
			MakeLensFlare((float)sx,(float)sy,-0.1f,0.4f,0.3f,1.0f,0.3f,br);			
			MakeLensFlare((float)sx,(float)sy,-0.4f,0.2f,0.3f,0.8f,0.8f,br);			
			MakeLensFlare((float)sx,(float)sy,-0.7f,0.5f,0.3f,0.3f,1.0f,br);			
			MakeLensFlare((float)sx,(float)sy,-0.9f,0.5f,0.8f,0.3f,0.8f,br);
			
			MakeLensFlare((float)sx,(float)sy,+1.4f,0.2f,0.3f,0.3f,0.8f,br);
			MakeLensFlare((float)sx,(float)sy,+1.8f,0.4f,0.3f,0.3f,0.8f,br);
			break;

		case 2:
			MakeLensFlare((float)sx,(float)sy, 0.5f,0.6f,0.2f,0.8f,1,br);			
			MakeLensFlare((float)sx,(float)sy,-0.1f,0.3f,0.2f,0.8f,1,br);			
			MakeLensFlare((float)sx,(float)sy,-0.5f,0.3f,0.2f,0.8f,1,br);			
			MakeLensFlare((float)sx,(float)sy,-0.9f,0.6f,0.2f,0.8f,1,br);
			MakeLensFlare((float)sx,(float)sy, 1.2f,0.3f,0.2f,0.8f,1,br);			
			MakeLensFlare((float)sx,(float)sy, 1.4f,0.6f,0.2f,0.8f,1,br);
			MakeLensFlare((float)sx,(float)sy, 0.7f,0.3f,1,1,0.5f,br);			
			MakeLensFlare((float)sx,(float)sy,-1.1f,0.3f,1,1,0.5f,br);
			MakeLensFlare((float)sx,(float)sy,-1.0f,1   ,1,1,0.4f,br);
			break;

		case 3:
			filled=0;
			MakeLensFlare((float)sx,(float)sy,-1.0f,0.6f,	color.r,color.g,color.b,br);
			MakeLensFlare((float)sx,(float)sy,-0.8f,0.4f,	color.r,color.g,color.b,br);
			filled=1;
			MakeLensFlare((float)sx,(float)sy,-0.6f,0.6f,	color.r,color.g,color.b,br);
			filled=0;
			MakeLensFlare((float)sx,(float)sy,+0.4f,0.6f,	color.r,color.g,color.b,br);
			MakeLensFlare((float)sx,(float)sy,+0.2f,0.8f,	color.r,color.g,color.b,br);
			filled=1;	
			MakeLensFlare((float)sx,(float)sy,+0.3f,0.6f,	color.r,color.g,color.b,br);
			filled=0;
			MakeLensFlare((float)sx,(float)sy,+1.2f,0.4f,	color.r,color.g,color.b,br);
			break;
		
		case 5:
			filled=0;
			MakeLensFlare((float)sx,(float)sy,-1,1,0.4f,0.4f,0.6f,br);//distanza/dimensione
			MakeLensFlare((float)sx,(float)sy,-0.6f,0.5f,0.3f,0.5f,0.2f,br);
			
			filled=1;
			MakeLensFlare((float)sx,(float)sy,-0.3f,0.4f,	0,0,1,br);
			filled=0;
			MakeLensFlare((float)sx,(float)sy,-0.25f,0.2f,0.3f,0.5f,0.2f,br);
			
			MakeLensFlare((float)sx,(float)sy,0,0.4f,	0.7f,0.5f,0.2f,br);
			filled=1;
			MakeLensFlare((float)sx,(float)sy,0.1f,0.6f,	0.7f,0.5f,0.2f,br);
			filled=0;
			MakeLensFlare((float)sx,(float)sy,+0.2f,0.1f,	0.7f,0.5f,0.2f,br);
			
			filled=0;
			MakeLensFlare((float)sx,(float)sy,+0.5f,0.2f,	0.7f,0.5f,0.2f,br);
			MakeLensFlare((float)sx,(float)sy,+0.7f,0.1f,	0.2f,0.5f,0.7f,br);
			
			MakeLensFlare((float)sx,(float)sy,+1.7f,0.3f,	0.4f,0.4f,0.6f,br);
			break;
		
	}

	glPopMatrix(); 
    glMatrixMode(GL_MODELVIEW);
    glPopMatrix();

	glEnable(GL_DEPTH_TEST);
	glEnable(GL_TEXTURE_2D);

	glEnableClientState(GL_TEXTURE_COORD_ARRAY);
	
	glBindTexture(GL_TEXTURE_2D,parent->pTexture[texture[curTex]]->memAddr);
}
//--------------------------------------------------------------------------------------------------
void cFlare::Draw(cVertex& x,cVertex& y)
{
	glPushMatrix();
	
		glTranslatef(position.x, position.y, position.z);
		glRotatef((deltaRot+=fixedDeltaRot),0,0,1);
		
		cVertex v0(0,0,0);
		cVertex v1 = (v0 -x +y);
		cVertex v2 = (v0 +x +y);
		cVertex v3 = (v0 +x -y);
		cVertex v4 = (v0 -x -y);
		
		static float fTArray[]={1,1, 0,1, 0,0,  1,1, 0,0, 1,0};
		static float fVArray[]={v4.x,v4.y,v4.z,  v3.x,v3.y,v3.z,   v2.x,v2.y,v2.z,  v4.x,v4.y,v4.z, v2.x,v2.y,v2.z,  v1.x,v1.y,v1.z};
		
		glVertexPointer(3, GL_FLOAT, 0, &fVArray[0]);
		glTexCoordPointer(2, GL_FLOAT, 0, &fTArray[0]);
		glDrawArrays(GL_TRIANGLES,0,6); 

	glPopMatrix();
}
//--------------------------------------------------------------------------------------------------

void cFlare::SetRotation(float tAngle)
{	
	fixedDeltaRot = tAngle;
}

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

cFlare::cFlare(cScene* ptr)
{

	parent		= ptr;

	totEmitters	= 0;
	totTextures	= 0;

	filled		= 0;
	shape		= 6;

	flareSet	= 0;
	active		= 0;
	flareSize	= 0;
	fsize		= 0;
	brightLevel = 1;
	curTex		= 0;
	object		= -1;
	fixedDeltaRot = 0;
	deltaRot	  = 0;
	
	color.Set(1,1,1,1);
	
	changeSpeed = BASE_TIMER;
	fadeSpeed	= BASE_TIMER;

	alfaStep	= 255 / fadeSpeed;
	changeTimer = parent->globalTimer + changeSpeed;
	fadeTimer	= parent->globalTimer + fadeSpeed;
	isVisible	= 0;
	depthTest	= 0;

	for (int t=0;t<MAX_ENTITIES;t++)
		texture[t] = 0;

	for (int t=0;t<MAX_ENTITIES;t++)
    {
		
		emitter[t][0] =
		emitter[t][1] = 0;

		offset[t][0]  =
		offset[t][1]  =
		offset[t][2]  = 0;
		
		alfa[t]       = 0;
	}

}
//--------------------------------------------------------------------------------------------------
cFlare::~cFlare()
{

}
//--------------------------------------------------------------------------------------------------

void cFlare::SetSet(int fSet)
{
	flareSet = (short)fSet;
}

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

void cFlare::SetDepthTest(int what)
{
	depthTest= what; //0=TEST_NONE, 1=TEST_DEPTH  2=TEST_PIXEL
}
//--------------------------------------------------------------------------------------------------
void cFlare::SetPosition(float mx,float my,float mz)
{
	position.Set(mx,my,mz);
}
//--------------------------------------------------------------------------------------------------
void cFlare::SetTexture(char* texName)
{
	texture[0] = parent->LoadTexture(texName);
}
//--------------------------------------------------------------------------------------------------
void cFlare::SetTexture(int numTex){
	
	if (totTextures>=MAX_ENTITIES)
		return;

	texture[totTextures] = numTex;
	curTex=0;
	totTextures++;
}
//--------------------------------------------------------------------------------------------------
void cFlare::SetChangeSpeed(int texChangeSpeed)
{	
	changeSpeed = texChangeSpeed;
}
//--------------------------------------------------------------------------------------------------
int cFlare::AddEmitter(int numObj,int numSubObj,int numVertex,float offsetX,float offsetY,float offsetZ)
{
	if (totEmitters >= MAX_ENTITIES)
		return 0;

	object = numObj;

	emitter[totEmitters][0] = numSubObj;
	emitter[totEmitters][1] = numVertex;

	offset[totEmitters][0] = offsetX;
	offset[totEmitters][1] = offsetY;
	offset[totEmitters][2] = offsetZ;
					
	++totEmitters;
	return totEmitters;
}
//--------------------------------------------------------------------------------------------------
int cFlare::SetOffset(int emitter,short offsetX,short offsetY,short offsetZ)
{

	if (emitter<0 || emitter >= MAX_ENTITIES)
		return 0;

	offset[emitter][0] = offsetX;
	offset[emitter][1] = offsetY;
	offset[emitter][2] = offsetZ;
					
	return 1;
}
//--------------------------------------------------------------------------------------------------
int cFlare::AddTexChanger(short texChangeSpeed,char* texName)
{
	if (totTextures>MAX_ENTITIES)
		return 0;

	changeSpeed = texChangeSpeed;
	texture[totTextures]  = parent->LoadTexture(texName);
	totTextures++;
	return 1;
}
//--------------------------------------------------------------------------------------------------
void cFlare::SetParms(float r,float g,float b,float a,float tSize,int depthTestType,short faderSpeed)
{

	color.r		= r;
	color.g		= g;
	color.b		= b;
	color.a		= a;
	fsize		= tSize;
	depthTest	= depthTestType;
	fadeSpeed   = faderSpeed;
}
//--------------------------------------------------------------------------------------------------
void cFlare::SetSpeed(short faderSpeed)
{

	fadeSpeed   = faderSpeed;
	alfaStep	= 255 / fadeSpeed;
}
//--------------------------------------------------------------------------------------------------
void cFlare::SetColor(cColor tColor){

	color = tColor;
}
//--------------------------------------------------------------------------------------------------
void cFlare::SetColor(float r,float g,float b,float a){

	color.Set(r,g,b,a);
}
//--------------------------------------------------------------------------------------------------
void cFlare::SetSize(float tSize)
{
	fsize = tSize;
}
//--------------------------------------------------------------------------------------------------
void cFlare::SetReflect(float reflSize,float reflBright,short reflShape,bool reflFilled){

	flareSize	= reflSize;
	filled		= reflFilled;
	shape		= reflShape;
	brightLevel = reflBright;
}
//--------------------------------------------------------------------------------------------------
int cFlare::Render()
{
	
	if (!active)
		return 0;

	//if (blendMode == 1)
	//	glBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA );
	//else
		glBlendFunc(GL_SRC_ALPHA, GL_ONE);

	if (object==-1)
		if (depthTest == 2)
			//if (!parent->FOV_PointTest(position))
				return 0;

	//la sparizione della flare non e' legata al globalTimer
	long elapsed = parent->globalTimer;

	if (changeSpeed)
		if (parent->globalTimer > changeTimer)
		{
			if (++curTex >= totTextures) curTex=0;
			changeTimer = parent->globalTimer + changeSpeed;
		}

	if (depthTest == 1)
	{
		glEnable(GL_DEPTH_TEST);
		glEnable(GL_ALPHA_TEST);
		glAlphaFunc(GL_GEQUAL,0.1);
	}
	else
		glDisable(GL_DEPTH_TEST);
	
	glBindTexture(GL_TEXTURE_2D,parent->pTexture[texture[curTex]]->memAddr);
	
	float	mMat[16];
	float	tColor;
	
	if (object==-1)
	{
		glGetFloatv(GL_MODELVIEW_MATRIX, mMat);
		
		if (depthTest==2)
		{
			isVisible = parent->RayToPixel(position);

			if (elapsed > fadeTimer)
			{
				fadeTimer = elapsed + fadeSpeed;
				if (isVisible && alfa[0]<255)
					alfa[0] += alfaStep;
				else if (!isVisible && alfa[0]>0)
					alfa[0] -= alfaStep;
			}	

			tColor = (float)alfa[0]/255.0f;
		
		}else{
			
			tColor = 1;
		}

		if (tColor)
		{
			cVertex bx(mMat[0]*fsize, mMat[4]*fsize, mMat[8]*fsize);
			cVertex by(mMat[1]*fsize, mMat[5]*fsize, mMat[9]*fsize);
			color.Set(color.r,color.g,color.b,tColor);
			glColor4f(color.r,color.g,color.b,color.a);	
			Draw(bx,by);
            if (flareSize)
				RenderLensFlare();
		}
		
	}
	else if (parent->pObject[object]->active)
	{
			
			cVertex p;
			int		set=0;

			parent->pObject[object]->PushPosition();
			
			glGetFloatv(GL_MODELVIEW_MATRIX, mMat);
			cVertex bx(mMat[0]*fsize,mMat[4]*fsize,mMat[8]*fsize);
			cVertex by(mMat[1]*fsize,mMat[5]*fsize,mMat[9]*fsize);
			
			for (int e=0;e<totEmitters;e++)
				{	
					
					position.Set(
						parent->pObject[object]->subObj[emitter[e][0]].vertex[emitter[e][1]].x + offset[e][0],
						parent->pObject[object]->subObj[emitter[e][0]].vertex[emitter[e][1]].y + offset[e][1],
						parent->pObject[object]->subObj[emitter[e][0]].vertex[emitter[e][1]].z + offset[e][2]);

					if (depthTest==2)
					{
						isVisible = parent->RayToPixel(position);

						if (elapsed > fadeTimer)
						{
							fadeTimer = elapsed + fadeSpeed;
							set=1;
						}
						
						if (set)
						{
							if (isVisible && alfa[e] < 255)
								alfa[e] += alfaStep;
							else if (!isVisible && alfa[e] > 0)
								alfa[e] -= alfaStep;
						}
						
						tColor = (float)alfa[e]/255.0f;
					
					}else{

						tColor = 1;
					}
					
					if (tColor)
					{	
						color.Set(color.r,color.g,color.b,tColor);
						glColor4f(color.r,color.g,color.b,color.a);	
						Draw(bx,by);
						if (flareSize)
							RenderLensFlare();
					}
				}

			parent->pObject[object]->PopPosition();
	}
	glDisable(GL_ALPHA_TEST);
	glEnable(GL_DEPTH_TEST);
	return 1;
}
//--------------------------------------------------------------------------------------------------