/**
 * OpenGL RedBook Shader.
 * Examples 7.8, 7.9 and 7.10.
 */
#version 330 core

struct LightProperties {
    bool isEnabled;
    bool isLocal;
    bool isSpot;

    vec3 ambient;
    vec3 colour;
    vec3 position;

    vec3 halfVector;
    vec3 coneDirection;
    float spotCosCustoff;
    float spotExponent;
    float constantAttenuation;
    float linearAttenuation;
    float quadraticAttenuation;
};

// example 7.9
struct MaterialProperties {
    vec3 emission;
    vec3 ambient;
    vec3 diffuse;
    vec3 specular;
    float shininess;
};

const int MaxLights = 8;
uniform LightProperties lights[MaxLights];

const int MaxMaterials = 1;
uniform MaterialProperties materials[MaxMaterials];

uniform float Strength;
uniform vec3 EyeDirection;

in vec4 Position;
in vec3 Normal;
in vec4 Colour;
flat in int MatIndex;

out vec4 FragColour;

void main() {
    vec3 scatteredLight = vec3(0.0);
    vec3 reflectedLight = vec3(0.0);

    for(int light = 0; light < MaxLights; ++light) {
        if ( !lights[light].isEnabled ) continue;

        vec3 halfVector;
        vec3 lightDirection = lights[light].position;
        float attenuation = 1.0;

        float specular = 0;
        if ( lights[light].isLocal ) {
            lightDirection = lightDirection - vec3(Position);
            float lightDistance = length(lightDirection);
            lightDirection = lightDirection / lightDistance;

            attenuation = 1.0 /
                            lights[light].constantAttenuation +
                            lights[light].linearAttenuation * lightDistance +
                            lights[light].quadraticAttenuation * lightDistance * lightDistance;

            if ( lights[light].isSpot ) {
                float spotCos = dot(lightDirection, -lights[light].coneDirection);
                if ( spotCos < lights[light].spotCosCustoff )
                    attenuation = 0.0;
                else
                    attenuation *= pow(spotCos, lights[light].spotExponent);
            }

            halfVector = normalize(lightDirection + EyeDirection);
            specular = max(0.0, dot(Normal, halfVector));
        } else {
            //halfVector = lights[light].halfVector;
            halfVector = reflect(-lightDirection, Normal);
            specular = max(0.0, dot(EyeDirection, halfVector));
        }

        float diffuse = max(0.0, dot(Normal, lightDirection));
        if (diffuse == 0.0)
            specular = 0.0;
        else
            specular = pow(specular, materials[MatIndex].shininess) * Strength;

        scatteredLight += lights[light].ambient * materials[MatIndex].ambient * attenuation +
                          lights[light].colour * materials[MatIndex].diffuse * diffuse * attenuation;
        reflectedLight += lights[light].colour * materials[MatIndex].specular *
                            specular * attenuation;
    }

    vec3 rgb = min( materials[MatIndex].emission + Colour.rgb * scatteredLight + reflectedLight, vec3(1.0));
    FragColour = vec4(rgb, 1.0);
}