/**
 * Shader implementing: Clipping, Texturing (RGB and YUV), Lighting, Fog
 **/

//version defined first, at shader compilation
#ifdef GF_GL_IS_ExternalOES
#extension GL_OES_EGL_image_external : require
#endif
#if defined(GL_ES)
#if defined(GL_FRAGMENT_PRECISION_HIGH)
precision highp float;	//ES2.0 supporting highp
#else
precision mediump float;	//Default
#endif
#endif

//#pragma STDGL invariant(all)	//removed due to incompatibility with the emulator

//LIGHTS_MAX and CLIP_MAX defined at shader compilation


#ifdef GF_GL_HAS_LIGHT

#define FOG_TYPE_LINEAR 0
#define FOG_TYPE_EXP    1
#define FOG_TYPE_EXP2   2

#define L_DIRECTIONAL	0
#define L_SPOT		    1
#define L_POINT			2

//Light Structure
struct gfLight{
#if defined(GL_ES)
	lowp int type;
#else
	int type;
#endif
	vec4 position;
	vec4 direction;
	vec3 attenuation;
	vec4 color;
	float ambientIntensity;	//it is not used - we calculate it inside the shader
	float intensity;
	float beamWidth;	//it is not used - we calculate it inside the shader
	float cutOffAngle;
};

//Generic (Scene) Uniforms
#if defined(GL_ES)
uniform lowp int gfNumLights;
#else
uniform int gfNumLights;
#endif
uniform bool gfLightTwoSide;
uniform gfLight lights[LIGHTS_MAX];

//Material and Lighting Properties
uniform vec4 gfAmbientColor;
uniform vec4 gfDiffuseColor;
uniform vec4 gfSpecularColor;
uniform float gfShininess;	//a.k.a. specular exponent
uniform vec4 gfLightDiffuse;
uniform vec4 gfLightAmbient;
uniform vec4 gfLightSpecular;

//Fog
uniform bool gfFogEnabled;
uniform vec3 gfFogColor;

#endif

uniform bool hasMaterial2D;
uniform vec4 gfEmissionColor;

#ifdef GF_GL_HAS_COLOR
uniform bool hasMeshColor;
#endif

#ifdef GF_GL_HAS_CLIP
#if defined(GL_ES)
uniform lowp int gfNumClippers;
#else
uniform int gfNumClippers;
#endif
#endif

//Color Matrix
uniform mat4 gfColorMatrix;
uniform bool hasColorMatrix;
uniform vec4 gfTranslationVector;
	
//Color Key
uniform vec3 gfKeyColor;
uniform float gfKeyAlpha;
uniform float gfKeyLow;
uniform float gfKeyHigh;
uniform bool hasColorKey;

#ifdef GF_GL_HAS_TEXTURE

uniform int gfNumTextures;

//Texture samplers
#if defined(GF_GL_IS_YUV)
uniform int yuvPixelFormat;
uniform sampler2D y_plane;
uniform sampler2D u_plane;
uniform sampler2D v_plane;
#elif defined(GF_GL_IS_ExternalOES)
uniform samplerExternalOES imgOES;
#else
uniform sampler2D img;
#endif

//Texture other
uniform float alpha;
const vec3 offset = vec3(-0.0625, -0.5, -0.5);
const vec3 R_mul = vec3(1.164,  0.000,  1.596);
const vec3 G_mul = vec3(1.164, -0.391, -0.813);
const vec3 B_mul = vec3(1.164,  2.018,  0.000);

#endif


//Varyings
#ifdef GF_GL_HAS_LIGHT
varying vec3 m_normal;
varying vec4 gfEye;
varying vec3 lightVector[LIGHTS_MAX];
varying float gfFogFactor;
#endif

#ifdef GF_GL_HAS_TEXTURE
varying vec2 TexCoord;
#endif

#ifdef GF_GL_HAS_COLOR
varying vec4 m_color;
#endif

#ifdef GF_GL_HAS_CLIP
varying float clipDistance[CLIPS_MAX];
#endif

//constants
const float zero_float = 0.0;
const float one_float = 1.0;


#ifdef GF_GL_HAS_LIGHT
vec4 doLighting(int i){

	vec4 lightColor = vec4(zero_float, zero_float, zero_float, zero_float);
	float att = zero_float;
	gfLight tempLight;	//we use a temp gfLight, because array of straucts in fragment are not supported in android
	vec3 lightVnorm; //normalized lightVector
	vec3 lightV; //temp lightVector
	vec3 halfVnorm; //temp lightVector

	//FIXME - couldn't we use a light[i]. something here ?
	if(i==0) {	//ES2 does not support switch() statements
		tempLight = lights[0];
		lightV = lightVector[0];
		halfVnorm = normalize( lightVector[0] + gfEye.xyz );
	} else if (i==1) {
		tempLight = lights[1];
		lightV = lightVector[1];
		halfVnorm = normalize( lightVector[1] + gfEye.xyz );
	} else if(i==2) {
		tempLight = lights[2];
		lightV = lightVector[2];
		halfVnorm = normalize( lightVector[2] + gfEye.xyz );
	}
	
	lightVnorm = normalize(lightV);
	vec3 normal = normalize(m_normal);

	if (gfLightTwoSide && (!gl_FrontFacing)){//light back face
		//originally: normal *=-1; -> Not compliant with Shading Language v1.0
		normal *= vec3(-1.0, -1.0, -1.0);
	}
	
	float light_cos = max(zero_float, dot(normal, lightVnorm));	//ndotl
	float half_cos = dot(normal, halfVnorm);

	if (tempLight.type == L_POINT) {	//we have a point
		float distance = length(lightV);	
		att = 1.0 / (tempLight.attenuation.x + tempLight.attenuation.y * distance + tempLight.attenuation.z * distance * distance);

		if (att <= 0.0)
			return lightColor;
			
		lightColor += light_cos * tempLight.color * gfDiffuseColor;
		
		if (light_cos > 0.0) {
			float dotNormHalf = max(dot(normal, halfVnorm),0.0);	//ndoth
			lightColor += (pow(dotNormHalf, gfShininess) * gfSpecularColor * tempLight.color);
			lightColor *= att;
		}
		lightColor.a = gfDiffuseColor.a;
		return lightColor;
		
	} else if (tempLight.type == L_SPOT) {	//we have a spot
		if (light_cos > 0.0) {
			float spot = dot(normalize(tempLight.direction.xyz), lightVnorm);	//it should be -direction, but we invert it before parsing
			if (spot > tempLight.cutOffAngle) {
				float distance = length(lightV);	
				float dotNormHalf = max(dot(normal, halfVnorm),0.0);	//ndoth
				spot = pow(spot, tempLight.intensity);
				att = spot / (tempLight.attenuation.x + tempLight.attenuation.y * distance + tempLight.attenuation.z * distance * distance);
				lightColor += att * (light_cos * tempLight.color * gfDiffuseColor);
				lightColor += att * (pow(dotNormHalf, gfShininess) * gfSpecularColor * tempLight.color);
			}
		}
		return lightColor;

	} else if(tempLight.position.w == zero_float || tempLight.type == L_DIRECTIONAL) { //we have a direction
		vec3 lightDirection = vec3(tempLight.position);
		lightColor = (gfDiffuseColor * gfLightDiffuse) * light_cos; 
		if (half_cos > zero_float) {
			lightColor += (gfSpecularColor * gfLightSpecular) * pow(half_cos, gfShininess);
		}
		lightColor.a = gfDiffuseColor.a;
		return lightColor;
	}

	return vec4(zero_float);
}
#endif //GF_GL_HAS_LIGHT

void main()
{
#if defined(GF_GL_HAS_CLIP) && defined(GL_ES)
	bool do_clip=false;
#endif
	int i;
	vec2 texc;
	vec3 yuv, rgb;
	vec4 rgba, fragColor;

#ifdef GF_GL_HAS_CLIP
	//clipping
	for (int i=0; i<CLIPS_MAX; i++) {
		if (i==gfNumClippers) break;
		if (clipDistance[i]<0.0) {
			//do not discard on GLES too slow on most devices
#if defined(GL_ES)
			do_clip=true;
			break;
#else
			discard;
#endif
		}
	}
#endif
	
#if defined(GF_GL_HAS_CLIP) && defined(GL_ES)
	if (do_clip) {
		gl_FragColor = vec4(zero_float);
	} else {
#endif
		
		
#ifdef GF_GL_HAS_COLOR
	fragColor = m_color;
#else
	fragColor = vec4(zero_float);
#endif

	if (hasMaterial2D) {
		fragColor = gfEmissionColor;
	}

#if defined (GF_GL_HAS_LIGHT)
	if (gfNumLights>0) {
#ifdef GF_GL_HAS_COLOR
		fragColor += (gfAmbientColor * gfLightAmbient);
#else
		fragColor = gfEmissionColor + (gfAmbientColor * gfLightAmbient);
#endif
	}
#endif

#ifdef GF_GL_HAS_LIGHT
	if (gfNumLights > 0) {
		for (int i=0; i<LIGHTS_MAX; i++) {
			if (i==gfNumLights) break;
			fragColor += doLighting(i);
		}
		fragColor.a = gfDiffuseColor.a;
	}
#endif
	
	fragColor = clamp(fragColor, zero_float, one_float);

#ifdef GF_GL_HAS_TEXTURE
	
	//currently supporting 1 texture
	if (gfNumTextures>0) {
#if defined(GF_GL_IS_YUV)
		texc = TexCoord.st;
		yuv.x = texture2D(y_plane, texc).r;
		if (yuvPixelFormat==2) {
			yuv.y = texture2D(u_plane, texc).r;
			yuv.z = texture2D(u_plane, texc).a;
		}
		else if (yuvPixelFormat==1) {
			yuv.y = texture2D(u_plane, texc).a;
			yuv.z = texture2D(u_plane, texc).r;
		}
		else {
			yuv.y = texture2D(u_plane, texc).r;
			yuv.z = texture2D(v_plane, texc).r;
		}
		yuv += offset;
		rgb.r = dot(yuv, R_mul);
		rgb.g = dot(yuv, G_mul);
		rgb.b = dot(yuv, B_mul);

		rgba = vec4(rgb, alpha);
		
#elif defined(GF_GL_IS_ExternalOES)
		rgba = texture2D(imgOES, TexCoord);
#else
		rgba = texture2D(img, TexCoord);
#endif

#ifdef GF_GL_HAS_LIGHT
		if (gfNumLights>0) {	//RGB texture
			fragColor *= rgba;
		}
		//RGB texture with material 2D [TODO: check]
		else if(gfNumLights==0)
#endif
		{
			fragColor = rgba;
		}

		//we have mat 2D + texture
#ifndef GF_GL_IS_ExternalOES
		if (hasMaterial2D) {
			if(gfEmissionColor.a > 0.0 && gfEmissionColor.a <1.0) {
				fragColor *= gfEmissionColor;
			} else if(fragColor.rgb == vec3(0.0, 0.0, 0.0)){
				fragColor.rgb = gfEmissionColor.rgb;
			}
		}
#endif // GF_GL_IS_ExternalOES
	}
	
#endif // GF_GL_HAS_TEXTURE
	
	
	if (hasColorMatrix) {
		fragColor = gfColorMatrix * fragColor;
		fragColor += gfTranslationVector;
		fragColor = clamp(fragColor, zero_float, one_float);
	}
	
	if (hasColorKey) {
		vec3 tempColour = vec3(0.0, 0.0, 0.0);
		float mean = 0.0;
		
		tempColour.r = abs(gfKeyColor.r-fragColor.r);
		tempColour.g = abs(gfKeyColor.g-fragColor.g);
		tempColour.b = abs(gfKeyColor.b-fragColor.b);
		mean = (tempColour.r + tempColour.g + tempColour.b)/3.0;
		
		if (mean<gfKeyLow) {
			fragColor.a =0.0;
		} else if(mean<=gfKeyHigh) {
			fragColor.a = (mean-gfKeyLow) * gfKeyAlpha / (gfKeyHigh - gfKeyLow);
		}
	}
	
#ifdef GF_GL_HAS_LIGHT
	if (gfFogEnabled)
		fragColor = fragColor * gfFogFactor + vec4(gfFogColor, zero_float) * (one_float - gfFogFactor);
#endif
	gl_FragColor = fragColor;

#if defined(GF_GL_HAS_CLIP) && defined(GL_ES)
	}
#endif
}
