#version 330 core

#define NR_POINT_LIGHTS 4

struct Material {
	sampler2D diffuseMap;
	sampler2D specularMap;
	float shininess;
};
struct SpotLight {
	vec3 position;
	vec3 direction;
	float cutOffAngle;
	float outerCutOffAngle;
  
	vec3 ambientIntensity;
	vec3 diffuseIntensity;
	vec3 specularIntensity;

	float constant;
	float linear;
	float quadratic;
};

struct DirectionalLight {
	vec3 direction;

	vec3 ambient;
	vec3 diffuse;
	vec3 specular;
};
struct PointLight {
	vec3 position;

	float constant;
	float linear;
	float quadratic;

	vec3 ambient;
	vec3 diffuse;
	vec3 specular;
};

out vec4 FragColor;

uniform vec3 viewPosition;
uniform Material material;
uniform DirectionalLight dirLight;
uniform PointLight pointLights[NR_POINT_LIGHTS];
uniform SpotLight spotLight;

in vec2 TexCoords;
in vec3 FragPos;
in vec3 Normal;

vec3 CalculateDirectionalLight(DirectionalLight light, vec3 normal, vec3 viewDir)
{
	vec3 lightDir = normalize(-light.direction);

	float diff = max(dot(normal, lightDir), 0);

	vec3 reflectionDir = reflect(-lightDir, normal);
	float spec = pow(max(dot(viewDir, reflectionDir), 0), material.shininess);

	vec3 ambient = light.ambient * vec3(texture(material.diffuseMap, TexCoords));
	vec3 diffuse = light.diffuse * diff * vec3(texture(material.diffuseMap, TexCoords));
	vec3 specular = light.specular * spec * vec3(texture(material.specularMap, TexCoords));
	
	return ambient + diffuse + specular;
}

vec3 CalculatePointLight(PointLight light, vec3 normal, vec3 fragPos, vec3 viewDir)
{
	vec3 lightDir = normalize(light.position - fragPos);

	float diff = max(dot(normal, lightDir), 0.0);

	vec3 reflectionDir = reflect(-lightDir, normal);
	float spec = pow(max(dot(viewDir, reflectionDir), 0.0), material.shininess);

	float distance = length(light.position - fragPos);
	float attenuation = 1.0 / (light.constant + light.linear * distance + light.quadratic * (distance * distance));

	vec3 ambientLight = light.ambient * texture(material.diffuseMap, TexCoords).rgb * attenuation;
	vec3 diffuseLight = light.diffuse * diff * texture(material.diffuseMap, TexCoords).rgb * attenuation;
	vec3 specularLight = light.specular * spec * texture(material.specularMap, TexCoords).rgb * attenuation;

	return ambientLight + diffuseLight + specularLight;
}

vec3 CalculateSpotLight(SpotLight light, vec3 normal, vec3 fragPos, vec3 viewDir)
{
	vec3 lightDir = normalize(light.position - fragPos);

	float theta = dot(lightDir, normalize(-light.direction));
	float epsilon = light.cutOffAngle - light.outerCutOffAngle;
	float intensity = clamp((theta - light.outerCutOffAngle) / epsilon, 0.0, 1.0);

	float diff = max(dot(normal, lightDir), 0.0);

	vec3 reflectDir = reflect(-lightDir, normal);
	float spec = pow(max(dot(viewDir, reflectDir), 0.0), material.shininess);

	float distance = length(light.position - fragPos);
	float attenuation = 1.0 / (light.constant + light.linear * distance + light.quadratic * (distance * distance));

	vec3 ambientLight = light.ambientIntensity * texture(material.diffuseMap, TexCoords).rgb * attenuation;
	vec3 diffuseLight = light.diffuseIntensity * diff * texture(material.diffuseMap, TexCoords).rgb * attenuation * intensity;
	vec3 specularLight = light.specularIntensity * spec * texture(material.specularMap, TexCoords).rgb * attenuation * intensity;

	return ambientLight + diffuseLight + specularLight;
}


void main()
{
	vec3 normal = normalize(Normal);
	vec3 viewDir = normalize(viewPosition - FragPos);

	//vec3 result = CalculateDirectionalLight(dirLight, normal, viewDir);
	
	//for(int i = 0; i < NR_POINT_LIGHTS; i++)
	//	result += CalculatePointLight(pointLights[i], normal, FragPos, viewDir);

	vec3 result = CalculateSpotLight(spotLight, normal, FragPos, viewDir);

	FragColor = vec4(result, 1.0);
} 