#version 440

#define NR_POINT_LIGHTS 2
#define NR_DIR_LIGHTS 1
#define NR_SPOT_LIGHTS 1

layout (location = 0) out vec4 FragColor;
layout (location = 1) out vec4 BrightColor;

struct Material
{
    vec3 ambient;
    vec3 diffuse;
    vec3 specular;

    sampler2D diffuseTex;
    sampler2D specularTex;
};

struct PointLight
{
    vec3 position;
    vec3 color;
    float intensity;

    float a;
    float b;
    
    float ambientLight;
    float specularLight;
};

struct DirLight {
    vec3 direction;
    vec3 color;
    float intensity;

    float ambientLight;
    float specularLight;
};

struct SpotLight {
    vec3 position;
    vec3 direction;
    float intensity;
    vec3 color;
    float cutOff;
    float outerCutOff;
  
    float ambientLight;
    float specularLight;       
};

in vec3 vs_position;
in vec3 vs_color;
in vec2 vs_texcoord;
in vec3 vs_normal;

uniform Material material;
uniform DirLight dirLights[NR_DIR_LIGHTS];
uniform PointLight pointLights[NR_POINT_LIGHTS];
uniform SpotLight spotLights[NR_POINT_LIGHTS];
uniform SpotLight flashLight;

uniform vec3 cameraPos;

vec3 CalcDirLight(DirLight light, vec3 Normal, vec3 crntPos)
{
    float ambient = light.ambientLight;

    vec3 normal = normalize(Normal);
    vec3 lightDirection = normalize(light.direction);
    float diffuse = max(dot(normal, lightDirection), 0.0f);

    float specular = 0.0f;
    if (diffuse != 0.0f)
    {
        float specularLight = light.specularLight;
        vec3 viewDirection = normalize(cameraPos - crntPos);
        vec3 halfwayVec = normalize(viewDirection + lightDirection);
        float specAmount = pow(max(dot(normal, halfwayVec), 0.0f), 16);
        specular = specAmount * specularLight;
    };

    vec3 ambientRes = ambient * vec3(texture(material.diffuseTex, vs_texcoord));
    vec3 diffuseRes = diffuse * vec3(texture(material.diffuseTex, vs_texcoord));
    vec3 specularRes = specular * vec3(texture(material.specularTex, vs_texcoord).r);

    if (texture(material.diffuseTex, vs_texcoord).a < 0.1)
        discard;

    return (ambientRes + diffuseRes + specularRes) * light.color * light.intensity;
}

vec3 CalcPointLight(PointLight light, vec3 Normal, vec3 crntPos, vec3 viewDir)
{
    vec3 lightVec = light.position - crntPos;

    float distance = length(lightVec);
    float inten = 1.0f / (light.a * distance * distance + light.b * distance + 1.0f);

    float ambient = light.ambientLight;

    vec3 normal = normalize(Normal);
    vec3 lightDirection = normalize(lightVec);
    float diffuse = max(dot(normal, lightDirection), 0.0f);

    float specular = 0.0f;
    if (diffuse != 0.0f)
    {
        float specularLight = light.specularLight;
        vec3 viewDirection = normalize(cameraPos - crntPos);
        vec3 halfwayVec = normalize(viewDirection + lightDirection);
        float specAmount = pow(max(dot(normal, halfwayVec), 0.0f), 16);
        specular = specAmount * specularLight;
    };
        
    vec3 ambientRes = ambient * vec3(texture(material.diffuseTex, vs_texcoord));
    vec3 diffuseRes = inten * diffuse * vec3(texture(material.diffuseTex, vs_texcoord));
    vec3 specularRes = inten * specular * vec3(texture(material.specularTex, vs_texcoord).r);
    
    if (texture(material.diffuseTex, vs_texcoord).a < 0.1)
        discard;

    return (ambientRes + diffuseRes + specularRes) * light.color * light.intensity;
}

vec3 CalcSpotLight(SpotLight light, vec3 Normal, vec3 crntPos, vec3 viewDir)
{
    float outerCone = light.outerCutOff;
    float innerCone = light.cutOff;
    
    float ambient = light.ambientLight;

    vec3 normal = normalize(Normal);
    vec3 lightDirection = normalize(light.position - crntPos);
    float diffuse = max(dot(normal, lightDirection), 0.0f);

    float specular = 0.0f;
    if (diffuse != 0.0f)
    {
        float specularLight = light.specularLight;
        vec3 viewDirection = normalize(cameraPos - crntPos);
        vec3 halfwayVec = normalize(viewDirection + lightDirection);
        float specAmount = pow(max(dot(normal, halfwayVec), 0.0f), 16);
        specular = specAmount * specularLight;
    };

    // Apply attenuation based on distance
    float distance = length(light.position - crntPos);
    float attenuation = 1.0 / (distance * distance);

    // calculates the intensity of the crntPos based on its angle to the center of the light cone
	float angle = dot(lightDirection, normalize(-light.direction));
	float inten = clamp((angle - outerCone) / (innerCone - outerCone), 0.0f, 1.0f);
   
    vec3 ambientRes = ambient * vec3(texture(material.diffuseTex, vs_texcoord));
    vec3 diffuseRes = diffuse * attenuation * inten * vec3(texture(material.diffuseTex, vs_texcoord));
    vec3 specularRes = specular * attenuation * inten * vec3(texture(material.specularTex, vs_texcoord).r);
    
    if (texture(material.diffuseTex, vs_texcoord).a < 0.1)
        discard;

    return (ambientRes + diffuseRes + specularRes) * light.color * light.intensity;
}

void main()
{
    vec3 norm = normalize(vs_normal);
    if (!gl_FrontFacing) {
        norm = -norm;  // Flip the normal for backfaces
    }
    vec3 viewDir = normalize(cameraPos - vs_position);
    vec3 result = vec3(0.f);

    for(int i = 0; i < NR_DIR_LIGHTS; i++)
        result += CalcDirLight(dirLights[i], norm, viewDir);
    
    for(int i = 0; i < NR_POINT_LIGHTS; i++)
        result += CalcPointLight(pointLights[i], norm, vs_position, viewDir);
    
    result += CalcSpotLight(flashLight, norm, vs_position, viewDir);
    for(int i = 0; i < NR_SPOT_LIGHTS; i++)
        result += CalcSpotLight(spotLights[i], norm, vs_position, viewDir);

    // fog parameters
    vec3 fogColor = vec3(0.f, 0.f, 0.f);
    float fogStart = 10.0f;
    float fogEnd = 30.0f;
    // Fog calculation
    float distance = length(cameraPos - vs_position);
    float fogFactor = clamp((fogEnd - distance) / (fogEnd - fogStart), 0.0, 1.0);
    result = mix(fogColor, result, fogFactor);

    FragColor  = vec4(result, texture(material.diffuseTex, vs_texcoord).a);
    
    BrightColor = vec4(0.0, 0.0, 0.0, 1.0);
}
