#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;

uniform sampler2D normalMap;
uniform sampler2D depthMap;
uniform sampler2D materialInfoMap;
uniform sampler2D diffuseMap;
uniform sampler2D specularMap;

uniform vec2 planes;

smooth in vec2 VS_TexCoord;
smooth in vec3 VS_ViewVector;

layout(location = 0) out vec4 colorOut;

vec3 spheremapDec(vec2 enc)
{
  vec3 ret = vec3(enc * 2.0f, 0.0f) + vec3(-1.0f, -1.0f, 1.0f);
  float l = dot(ret.xy, -ret.xy) + 1.0f;
  ret.z = l;
  ret.xy *= sqrt(l);
  return ret.xyz * 2.0f + vec3(0.0f, 0.0f, -1.0f);
}

void main()
{

	vec4 materialInfo = texture(materialInfoMap, VS_TexCoord);

	switch(int(materialInfo.x*10.1))
	{
	case 1:
		discard;	//don't render reflective geometry
	case 2:
	{
		float depth = texture(depthMap, VS_TexCoord).x;
		vec3 viewVector = normalize(VS_ViewVector);

		//position
		vec3 pos;
		pos.z = -planes.y/(planes.x+depth);
		pos.xy = viewVector.xy/viewVector.z*pos.z;

		//normal
		vec3 N = spheremapDec(texture(normalMap, VS_TexCoord).xy);

		float len = length(N);
		if(len > 0.1)
			N = N / len;
		else
			N = vec3(0.0,0.0,0.0);
	
		//diffuse and specular colors 
		vec4 diffuse  = texture(diffuseMap, VS_TexCoord);
		vec4 specular = texture(specularMap, VS_TexCoord);


		//compute lighting from lights
		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] - pos);		//light vector
				float dist = length(lights.position[i] - pos);  //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] - pos);

				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] - pos);  //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(-pos);                       //viewpoint
			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 * NdotL;
			vec4 Is = specular * pow(RdotE, materialInfo.y*255);

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

		//add ambient color
		colorOut += ambientLightIntensity * diffuse;  //multiply diffuse material by ambient light
		
		//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 - pos);
			vec3 E = normalize(-pos);  
			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 * NdotL;
			vec4 Is = specular * pow(RdotE, materialInfo.y*255);
			colorOut += (light.intensity / ref) * vec4(light.diffuse, 0.0) * (Id + Is);			
		}
		break;
	}
	default:
		discard;
	}
}	
