#version 330

const int MAX_POINT_LIGHTS = 2;
const int MAX_SPOT_LIGHTS = 3;

in vec4 vsColor;
in vec2 vsTextureCoord; 
in vec3 vsNormal; 
in vec3 vsWorldPosition;

out vec4 fsFragmentColor;

struct BaseLight
{
	vec3 color;
	float ambientIntensity;
	float diffuseIntensity; 
};

struct DirectionalLight
{
	BaseLight base;
	vec3 direction;
};

struct Attenuation
{
	float constant;
	float linear;
	float exponential;
};

struct PointLight
{
	BaseLight base;
	vec3 position;
	Attenuation attenuation;
};

struct SpotLight
{
	PointLight pointLight;
	vec3 direction;
	float cutoff;
};

uniform int uPointLightsNumber;
uniform int uSpotLightsNumber;
uniform DirectionalLight uDirectionalLight;
uniform PointLight uPointLights[MAX_POINT_LIGHTS]; 
uniform SpotLight uSpotLights[MAX_SPOT_LIGHTS]; 
uniform sampler2D uTextureSampler; 
uniform vec3 uCameraPosition;
uniform float uMaterialSpecularIntensity;
uniform float uMaterialSpecularPower; 


vec4 CalcLightInternal(BaseLight light, vec3 _lightDirection, vec3 _normal)
{
	vec4 diffuseColor = vec4(0, 0, 0, 0);
	vec4 specularColor = vec4(0, 0, 0, 0);
	
	vec3 lightDirectionOpposite = normalize(-_lightDirection);
	vec3 normal = normalize(_normal);		 

	float diffuseFactor = dot(normal, lightDirectionOpposite);
	
	if (diffuseFactor > 0 )
	{
		diffuseColor = vec4(light.color* light.diffuseIntensity * diffuseFactor, 1.0f);

		vec3 vertexToEyeDirection = normalize(uCameraPosition - vsWorldPosition); 
		
		vec3 lightDirection = normalize(_lightDirection);
		vec3 lightReflectionDirection = normalize(reflect(lightDirection, normal));

		float specularFactor = dot(vertexToEyeDirection, lightReflectionDirection);
		if (specularFactor > 0) 
		{
			specularFactor = pow(specularFactor, uMaterialSpecularPower);
			specularColor = vec4(light.color * uMaterialSpecularIntensity * specularFactor, 1.0f);
		}
	} 

	vec4 ambientColor = vec4(light.color* light.ambientIntensity, 1.0f);
	return ( ambientColor +  diffuseColor + specularColor );
}

vec4 CalcDirectionalLight(vec3 normal)
{
	return CalcLightInternal(uDirectionalLight.base, uDirectionalLight.direction, normal);
}

vec4 CalcPointLight(PointLight pointLight, vec3 normal) 
{
	vec3 lightDirection = vsWorldPosition - pointLight.position; 
	float distance = length(lightDirection);
	lightDirection = normalize(lightDirection); 

		
	vec4 color = CalcLightInternal(pointLight.base, lightDirection, normal); 
	float attenuation =  pointLight.attenuation.constant + 
						 pointLight.attenuation.linear * distance + 
						 pointLight.attenuation.exponential * distance * distance;
		
	return color / attenuation; 
}

vec4 CalcSpotLight(SpotLight l, vec3 normal)
{
	vec3 lightToPixel = normalize(vsWorldPosition - l.pointLight.position);
	float spotFactor = dot(lightToPixel, l.direction);
		
	if (spotFactor > l.cutoff) 
	{
		vec4 color = CalcPointLight(l.pointLight, normal);
		return color * (1.0 - (1.0 - spotFactor) * 1.0/(1.0 - l.cutoff)); 
	}
	else 
	{
		return vec4(0,0,0,0);
	}
}


void main()
{
	vec3 normal = normalize(vsNormal);
	
	vec4 totalLight = CalcDirectionalLight(normal);

	for (int i = 0 ; i < uPointLightsNumber ; i++) 
	{ 
		totalLight += CalcPointLight(uPointLights[i], normal);
	} 
	
	for (int i = 0 ; i < uSpotLightsNumber ; i++)
	{
		totalLight += CalcSpotLight(uSpotLights[i], normal);
	}

	vec4 color = texture2D(uTextureSampler, vsTextureCoord);
	if(vsColor != vec4(.0f, .0f, .0f, 1.0f) && color == vec4( 1.0f, 1.0f, 1.0f, 1.0f))
	{
		color = vsColor;
	}
	
	fsFragmentColor = color * totalLight;
}
