#version 400

struct EnvLight
{
	vec3  diffuse;
	float intensity;
	vec3 position;
};
 
layout(std140) uniform EnvironmentLightBlock
{
	int ub_size;
	int ub_lightRef;
	EnvLight ub_lightSources[1024];
};

//lights
struct Light
{
	int   count;
	vec4  diffuse[10];
	float intensity[10];
	float angle[10];
	float exponent[10];
	vec3  attenuation[10];
	vec3  position[10];
	vec3  direction[10];
};
uniform Light lights;
uniform float ambientLightIntensity;

//texturing
uniform int textureCoordGenMode; // 0 - no generating, 1 - object space, 2 - eye space, 3 - sphere
uniform int textureMode;		 // 0 - no texture, 1 - replace, 2 - decal, 3 - blend, 4 - add, 5 - interpolate, 6 - modulate
uniform int alphaTest = 0;		 // alpha testing
uniform bool cubeMap;			 // environment map enabled
uniform sampler2D texture0;
uniform samplerCube environmentMap;

//material
struct Material
{
	float type; 
	vec4  diffuse;
	vec4  specular;
	float shininess;
};
uniform Material material;

smooth in vec3 VS_Vertex;
smooth in vec3 VS_Normal;
smooth in vec3 VS_View;
smooth in vec2 VS_TexCoord;
smooth in vec4 VS_TexCoord4;


layout(location = 0) out vec4 colorOut;


void main()
{
	//normal
	vec3 N = normalize(VS_Normal);
	vec3 E = normalize(-VS_Vertex);                 //view vector
	
	//diffuse color 
	vec4 color;
	if(textureCoordGenMode != 2 && !cubeMap)
		color = texture(texture0, VS_TexCoord);
	else if(cubeMap)
		color = texture(environmentMap, reflect(normalize(VS_View), N));
	else
		color = textureProj(texture0, gl_FragCoord);
	
	vec4 diffuse_mat;
	switch(textureMode)
	{
		case 0:
			diffuse_mat = material.diffuse;
			break;
		case 1:
			diffuse_mat = color;	
			break;
		case 2:
		{
			diffuse_mat = vec4(color.rgb * color.a, 1.0);
			diffuse_mat += material.diffuse * (1 - color.a);
			diffuse_mat.a = material.diffuse.a;				
			break;	
		}
		case 3:
		{
			diffuse_mat = material.diffuse * (1 - color) + color;
			diffuse_mat.a = material.diffuse.a * color.a;
			break;	
		}
		case 4:
		{
			diffuse_mat = clamp(material.diffuse +  color, 0.0, 1.0);
			diffuse_mat.a = material.diffuse.a * color.a;
			break;
		}
		case 5:
			diffuse_mat = mix(material.diffuse, color, color.a);
			break;	
		default:
			diffuse_mat = material.diffuse * color;	
	}
	
	if(alphaTest == 1 && diffuse_mat.a < 0.5)
		discard;

	//compute lighting
	colorOut = vec4(0.0, 0.0, 0.0, 1.0);
	for(int i = 0; i < lights.count; i++)
	{	
		vec3 L;
		float attenuation;

		if(length(lights.direction[i]) == 0.0)
		{
			//PointLight

			L = normalize(lights.position[i] - VS_Vertex);		  //light vector
			float dist = length(lights.position[i] - VS_Vertex);  //distance from vertex to light
			attenuation = 1.0 / (lights.attenuation[i].x + lights.attenuation[i].y * dist + lights.attenuation[i].z * dist * dist);	
		}
		else if(lights.angle[i] == 0.0)
		{
			//DirectionalLight
			L = normalize(-lights.direction[i]);
			attenuation = 1.0;
		}
		else
		{
			//SpotLight
			attenuation = 0.0;
			L = normalize(lights.position[i] - VS_Vertex);

			float spotEffect = dot(normalize(lights.direction[i]), -L);
			if (spotEffect > cos(lights.angle[i]))
			{	
				spotEffect = pow(spotEffect, lights.exponent[i]);

				//attentuation
				float dist = length(lights.position[i] - VS_Vertex);  //distance from vertex to light
				attenuation = spotEffect / (lights.attenuation[i].x + lights.attenuation[i].y * dist + lights.attenuation[i].z * dist * dist);
			}
		}	

		//vec3 E = normalize(-VS_Vertex);                 //view vector
		vec3 R = normalize(-reflect(L,N));              //reflected light ray

		float NdotL = max(dot(N,L), 0.0);
		float RdotE = max(dot(R,E), 0.0);

		vec4 Id = diffuse_mat * NdotL;
		vec4 Is = material.specular * pow(RdotE, material.shininess);

		//set fragment color
		colorOut += attenuation * lights.intensity[i] * lights.diffuse[i] * (Id + Is);	
	}

	//add ambient color
	colorOut += ambientLightIntensity * diffuse_mat;
	
	//add environment lights
	int s = ub_size; //just to make sure it's used only once
	float ref = ub_lightRef <= 0 ? 100.f : float(ub_lightRef); //tmp, fixme
	EnvLight light;
	for(int i = 0; i < s; i++) {
		light = ub_lightSources[i];
		
		vec3 L = normalize(light.position - VS_Vertex);
		//vec3 E = normalize(-VS_Vertex);  
		vec3 R = normalize(-reflect(L,N));
		
		float NdotL = max(dot(N,L), 0.0);
		float RdotE = max(dot(R,E), 0.0);
		
		vec4 Id = diffuse_mat * NdotL;
		vec4 Is = material.specular * pow(RdotE, material.shininess);
		colorOut += (light.intensity / ref) * vec4(light.diffuse, 0.0) * (Id + Is);
	}
		
	//set correct alfa
	colorOut.a = diffuse_mat.a;
}	
