#include <OpenGL/gl.h>
#include <math.h>
#include "vectors.h"
#include "environment.h"
#include "entities.h"
#include "textures.h"
#include "renderframe.h"
#include "roads.h"
#include "collision.h"
#include "config.h"
#include "stencil.h"

#define kLightOffset 0.6
#define kLightReflectionOffset 15

void RenderEntityLights(tGameEntity *entity,int numLights,int lightFlags,float reflectionDim,tLightDefinition *lights)
{
	glPushAttrib(GL_DEPTH_BUFFER_BIT+GL_CURRENT_BIT+GL_LIGHTING_BIT+GL_COLOR_BUFFER_BIT+GL_FOG_BIT);
	glDepthMask(false);
	glDisable(GL_LIGHTING);
	glEnable(GL_BLEND);									
	glBlendFunc(GL_SRC_ALPHA,GL_ONE);			
	glDisable(GL_FOG);		
	TexturesSelectTex(FileGetReference("Particle.pct"));
		
	for(int i=0;i<numLights;i++)
		//is the light on?
		if(lightFlags&lights[i].onFlags&&!(lightFlags&lights[i].offFlags))
			//is this a dot light? (a light which only needs a particle texture to be drawn)
			if(lights[i].type!=kLightTypeSpot)
			{
				float visibility=-lights[i].dir*entity->dir**MatrixGetZVector(gCameraEntity->dir);
				//is the light pointing towards the camera?
				
				if(lights[i].type==kLightTypeDirectionlessDot||lights[i].type==kLightTypeDirectionlessDotReflective)
					visibility=1;
				else if(lights[i].type==kLightTypeSpecularDot)
					visibility=sign(visibility)*powf(visibility*!((lights[i].pos*entity->dir+entity->pos)-gCameraEntity->pos)**MatrixGetZVector(gCameraEntity->dir),100);
					
				if(visibility>0){
					if(visibility>1)visibility=1;
					
					float size=lights[i].size;
										
					//draw particle texture
					
					tVector3 lightPos=lights[i].pos*entity->dir+entity->pos;
					float camDist=~(lightPos-gCameraEntity->pos);
					tVector3 drawlightPos=lightPos-(lightPos-gCameraEntity->pos)*kLightOffset/camDist;
					
					size*=(camDist-kLightOffset)/camDist;
					
					SetupTranslation(drawlightPos,gCameraEntity->dir);
					glColor4f(lights[i].rgb.x,lights[i].rgb.y,lights[i].rgb.z,visibility);
					
					glBegin(GL_TRIANGLE_STRIP);								
					glTexCoord2d(1,1); glVertex2f(size,size);		
					glTexCoord2d(1,0); glVertex2f(size,-size); 	
					glTexCoord2d(0,1); glVertex2f(-size,size); 	
					glTexCoord2d(0,0); glVertex2f(-size,-size); 	
					glEnd();
					
					//does the light reflect on the ground?
					if(lights[i].type==kLightTypeDotRefelective||lights[i].type==kLightTypeDirectionlessDotReflective)
					{
						int lastRoadIndex=entity->lastRoadIndex;
						int surface=-1;
						lightPos=lightPos-Vector(0,GetGroundOffset(lightPos,&lastRoadIndex,NULL,&surface),0);
						float camDist=~(lightPos-gCameraEntity->pos);
						float size=lights[i].size;
						if(camDist>2*kLightReflectionOffset)
						{
							drawlightPos=lightPos-(lightPos-gCameraEntity->pos)*kLightReflectionOffset/camDist;
							size*=(camDist-kLightReflectionOffset)/camDist;
						}
						else
							drawlightPos=lightPos;
						//is the ground reflective
						if(surface!=-1)
							if(gSurfaceTypes->types[surface].reflectionEnable)
							{
							//draw reflection texture
								SetupTranslation(drawlightPos,gCameraEntity->dir);
								if(camDist<=2*kLightReflectionOffset)		
								glDisable(GL_DEPTH_TEST);
								
								visibility*=1-reflectionDim*0.6;
								glColor4f(lights[i].rgb.x,lights[i].rgb.y,lights[i].rgb.z,visibility);
								
								TexturesSelectTex(FileGetReference("lightreflection.pct"));
								glBegin(GL_TRIANGLE_STRIP);								
								glTexCoord2d(1,1); glVertex2f(size,size);		
								glTexCoord2d(1,0); glVertex2f(size,-5*size); 	
								glTexCoord2d(0,1); glVertex2f(-size,size); 	
								glTexCoord2d(0,0); glVertex2f(-size,-5*size); 	
								glEnd();
								TexturesSelectTex(FileGetReference("Particle.pct"));
								#ifdef __POLYCOUNT
								gPolyCount+=2;
								#endif
								glEnable(GL_DEPTH_TEST);
							}
					}
					#ifdef __POLYCOUNT
					gPolyCount+=2;
					#endif
				}
			}															
	glPopAttrib();
}

#define kConeSections 16
#define kConeLength 6.5

void DrawLightCone(float scale)
{
	tVector3 c[kConeSections];
	for(int i=0;i<kConeSections;i++)
		c[i]=Vector(cos((2*PI*i)/kConeSections)*gStencilZoom,sin((2*PI*i)/kConeSections)*gStencilZoom,4)*scale*gStencilZoom;
	for(int i=0;i<kConeSections;i++)
	{
		glBegin(GL_TRIANGLE_STRIP);
		glVertex3f(0,0,(1-gStencilZoom)*10);
		glVertex3fv(&c[i].x);
		glVertex3fv(&c[(i+1)%kConeSections].x);
		glVertex3f(0,0,kConeLength*scale);		
		glEnd();
	}
	#ifdef __POLYCOUNT
	gPolyCount+=2*kConeSections;
	#endif
}

void DrawLightConeClippedFar(float scale,float clipPlanePos,tVector3 clipPlaneNormal)
{
	tVector3 c[kConeSections];
	tVector3 clipPos=Vector(0,0,kConeLength*scale*clipPlanePos);	
	float baseClipDist=-clipPos*clipPlaneNormal;
	float cClipDist;
	for(int i=0;i<kConeSections;i++)
		c[i]=Vector(cos((2*PI*i)/kConeSections)*gStencilZoom,sin((2*PI*i)/kConeSections)*gStencilZoom,4)*scale*gStencilZoom;
	for(int i=0;i<kConeSections;i++)
		if((cClipDist=((c[i]-clipPos)*clipPlaneNormal))>0)
		{
			float cClipPos=(1-(cClipDist/(cClipDist-baseClipDist)));
			if(cClipPos<=0)return;
			c[i]=c[i]*cClipPos;
		}
	for(int i=0;i<kConeSections;i++)
	{
		glBegin(GL_TRIANGLE_STRIP);
		glVertex3f(0,0,(1-gStencilZoom)*10);
		glVertex3fv(&c[i].x);
		glVertex3fv(&c[(i+1)%kConeSections].x);
		glVertex3f(0,0,clipPos.z);		
		glEnd();
	}
	#ifdef __POLYCOUNT
	gPolyCount+=2*kConeSections;
	#endif
}

void DrawLightConeClippedNear(float scale,tVector3 clipPlanePos,tVector3 clipPlaneNormal)
{
}

//draws a car's light cones (usually headlights)
void RenderEntityLightCones(tGameEntity *entity,int numLights,int lightFlags,tLightDefinition *lights)
{
	glPushAttrib(GL_STENCIL_BUFFER_BIT+GL_COLOR_BUFFER_BIT+GL_POLYGON_BIT+GL_DEPTH_BUFFER_BIT+GL_ENABLE_BIT);
	glDisable(GL_LIGHTING);
	glDisable(GL_FOG);

	for(int i=0;i<numLights;i++)
		//is the light on?
		if(lightFlags&lights[i].onFlags&&!(lightFlags&lights[i].offFlags))
			//is it a spot light
			if(lights[i].type==kLightTypeSpot)
				//is stencil buffering enabled, and are spotlights enabled for this environment?
				if(gConfig->stencil&&gEnvironment->spotLightEnable)
				{
					//translate to draw the light cone
					tVector3 lightPos=lights[i].pos*entity->dir+entity->pos;
					tMatrix3 m;
					MatrixIdentity(m);
					*MatrixGetZVector(m)=lights[i].dir;
					*MatrixGetXVector(m)=!(lights[i].dir%Vector(0,1,0));
					*MatrixGetYVector(m)=(lights[i].dir%*MatrixGetXVector(m));		
					MatrixMult(m,entity->dir,m);
					SetupTranslation(lightPos,m);
					
					//setup stencil buffer
					//drawing light cones works just like stencil buffer shadow volumes		
					glDepthMask(GL_FALSE);
					glEnable(GL_STENCIL_TEST);
					glColorMask(0, 0, 0, 0);
					glStencilFunc(GL_ALWAYS, 1, 0xffffffff);
					
					tVector3 camDir=*MatrixGetZVector(gCameraEntity->dir);
					float clip1=(lightPos-gCameraEntity->pos)*camDir;
					float clip2=((lightPos+(lights[i].dir*kConeLength*lights[i].size)*entity->dir)-gCameraEntity->pos)*camDir;
					//test if the light cone volume intersects the clipping volume
					
					if(clip1>ClipDistance()&&clip2>ClipDistance())
					{//clip near and far (not implemented)
					}
					else if(clip1>ClipDistance())
					{//clip near (not implemented)
					}
					else if(clip2>ClipDistance()-20)
					{//clip far 
						tMatrix3 mInv;
						MatrixTranspose(m,mInv);
						tVector3 clipPlaneDir=camDir*mInv;
						glFrontFace(GL_CW);
						glStencilOp(GL_KEEP, GL_INCR, GL_KEEP);
						DrawLightConeClippedFar(lights[i].size*gStencilZoom,(ClipDistance()-20-clip1)/(clip2-clip1),clipPlaneDir);
						
						glFrontFace(GL_CCW);
						glStencilOp(GL_KEEP, GL_DECR, GL_KEEP);
						DrawLightConeClippedFar(lights[i].size*gStencilZoom,(ClipDistance()-20-clip1)/(clip2-clip1),clipPlaneDir);
					}
					else
					{//no clip
						glFrontFace(GL_CW);
						glStencilOp(GL_KEEP, GL_INCR, GL_KEEP);
						DrawLightCone(lights[i].size*gStencilZoom);
						
						glFrontFace(GL_CCW);
						glStencilOp(GL_KEEP, GL_DECR, GL_KEEP);
						DrawLightCone(lights[i].size*gStencilZoom);		
					}
				}															
	glPopAttrib();
}
