#version 450
layout(binding = 0, std140) uniform Camera {
    mat4 view;
    mat4 projection;
    vec4 position;
} camera;

layout(binding = 1, std140) uniform Material {
    vec4 ambient_color;
    vec4 diffuse_color;
    vec4 specular_color;
} material;

struct Light {
	vec4 position;
	vec4 ambient_color;
	vec4 diffuse_color;
	vec4 specular_color;
    vec4 cone_direction;
};

layout(binding = 2, std430) buffer Lights {
	Light lights[];
};

layout(binding = 3) uniform samplerCube skybox_texture;
layout(binding = 4) uniform sampler2D albedo_texture;

layout(location = 4) uniform bool has_texture = false;

layout(location = 0) in vec3 frag_position;
layout(location = 1) in vec3 frag_normal;
layout(location = 2) in vec2 frag_texture_coordinate;

layout(location = 0) out vec4 final_color;

void main()
{
    vec3 color_sum = vec3(0.0);
    
    for (int i = 0; i < lights.length(); ++i)
    {
        Light light = lights[i];
        vec3 light_vector = light.position.xyz - frag_position * light.position.w;

        // Blinn-Phong components
        vec3 to_light = normalize(light_vector);
        vec3 normal = normalize(frag_normal);
        vec3 to_eye = normalize(camera.position.xyz - frag_position);
        vec3 halfway = normalize(to_light + to_eye);

        float NdotL = max(dot(normal, to_light), 0.0);
        float NdotH = max(dot(normal, halfway), 0.0001);

        vec3 ambient = material.ambient_color.rgb * light.ambient_color.rgb;
        vec3 diffuse = material.diffuse_color.rgb * light.diffuse_color.rgb
                       * (has_texture ? texture(albedo_texture, frag_texture_coordinate).rgb : vec3(1.0));
        vec3 specular = material.specular_color.rgb * light.specular_color.rgb;

        // Environment Mapping
        if (material.ambient_color.w > 0.0)
            specular = specular * texture(skybox_texture, halfway).rgb;

        vec3 color = ambient + NdotL * diffuse + pow(NdotH, material.specular_color.w) * specular;

        // Attenuation or Cone
        if (light.position.w == 1.0) 
        {
            float attenuation = dot(light_vector, light_vector);
            
            if (length(light.cone_direction) > 0.0)
            {
                vec3 d = normalize(light.cone_direction.xyz);
                vec3 v = -to_light;
                color *= pow(max(dot(d, v), 0.0), attenuation);
            }
            else
            {
                color /= attenuation;
            }
        }

        // color = color / (color + 1.0);       // tone mapping
        // color = pow(color, vec3(1.0 / 2.2)); // gamma correction
        color_sum += color;
    }

    final_color = vec4(color_sum, material.diffuse_color.w);
}
