#version 410

#define MAX_LIGHTS 3

#pragma include "p3d_uniform.glsl"

// Uniform inputs
uniform float u_MaxShininess;
uniform float u_RimLightIntensity;
uniform float u_ShadowSamples;
uniform float u_ShadowSpread;
uniform float u_SpecularBlendStrength;
uniform int u_NumberOfLights;
uniform float u_SpecularPower;

// Input from vertex shader
in vec4 vertInShadowSpaces[MAX_LIGHTS];
in vec4 vertColor;
in vec4 vertPosition;
//in vec4 domiPosition;
in vec2 vertMultiTexCoord0;
in vec3 vertNormal;
in vec3 binormal;
in vec3 tangent;
in float neg;
in float cloudSpecular;

// Output to the screen
out vec4 fragColor;

#define MAX_FRESNEL_POWER 5.0
#define MAX_SHININESS     127.75

void main() {

//    if (vertColor.a == 0.0) { discard; }
    if (vertColor.a <= 0.0) discard;


    vec4 diffuseColor = vertColor;

    float shininess = MAX_SHININESS * u_MaxShininess;
//    float shininess = MAX_SHININESS * 2.0;

    vec4 diffuse  = vec4(0.0, 0.0, 0.0, diffuseColor.a);
    vec4 specular = vec4(0.0, 0.0, 0.0, diffuseColor.a);
    vec4 rim      = vec4(0.0, 0.0, 0.0, diffuseColor.a);

    vec3 normal = normalize(vertNormal);

    vec3 eyeDirection = normalize(-vertPosition.xyz);

    for (int i = 0; i < u_NumberOfLights; ++i) {  // i = 0

        vec3 lightDirection = p3d_LightSource[i].position.xyz - vertPosition.xyz * p3d_LightSource[i].position.w;

//        normal = normalize(vertNormal);

        vec3 unitLightDirection = normalize(lightDirection);
        vec3 halfwayDirection   = normalize(unitLightDirection + eyeDirection);

        float lightDistance = length(lightDirection);

        float attenuation = 1 / (
            p3d_LightSource[i].constantAttenuation +
            p3d_LightSource[i].linearAttenuation * lightDistance +
            p3d_LightSource[i].quadraticAttenuation * (lightDistance * lightDistance)
        );

        if (attenuation <= 0.0) { continue; }

        // Diffuse
        float diffuseIntensity  = dot(normal, unitLightDirection);
        if (diffuseIntensity < 0.0) { continue; }
        vec4 diffuseTemp = vec4(
            clamp(diffuseColor.rgb * p3d_LightSource[i].diffuse.rgb * diffuseIntensity, 0.0 , 1.0), diffuseColor.a
        );


        // Specular
        float specularIntensity = clamp(dot(normal, halfwayDirection), 0.0, 1.0);

        vec4  lightSpecularColor = p3d_LightSource[i].specular;

        vec4 materialSpecularColor = vec4(vec3(1.0), diffuseColor.a);
        float fresnelFactor = dot(halfwayDirection, eyeDirection);
        fresnelFactor = max(fresnelFactor, 0.0);
        fresnelFactor = 1.0 - fresnelFactor;
        fresnelFactor = pow(fresnelFactor, MAX_FRESNEL_POWER);
        materialSpecularColor.rgb = mix(
            materialSpecularColor.rgb,
            vec3(u_SpecularBlendStrength),
//            fresnelFactor
            clamp(fresnelFactor, 0.0, 1.0)
        );

        vec4 specularTemp = vec4(vec3(0.0), diffuseColor.a);
        specularTemp.rgb  = lightSpecularColor.rgb * pow(specularIntensity, shininess);
        specularTemp.rgb *= materialSpecularColor.rgb;
        specularTemp.rgb  *= u_SpecularPower * cloudSpecular;

        // Spot
        float unitLightDirectionDelta = dot(normalize(p3d_LightSource[i].spotDirection), -unitLightDirection);
        if (unitLightDirectionDelta < p3d_LightSource[i].spotCosCutoff) { continue; }
        float spotExponent = p3d_LightSource[i].spotExponent;
        diffuseTemp.rgb *= (spotExponent <= 0.0 ? 1.0 : pow(unitLightDirectionDelta, spotExponent));

        // Shadow
        // Universal:
//        int u_ShadowSamples = int(u_ShadowSamples);
//        float shadow = 0.0;
//        int count = 0;
//        for (int si = -u_ShadowSamples; si <= u_ShadowSamples; ++si) {
//            for (int sj = -u_ShadowSamples; sj <= u_ShadowSamples; ++sj) {
//                shadow += textureProj(
//                    p3d_LightSource[i].shadowMap,
//                    vertInShadowSpaces[i] + vec4(vec2(si, sj) * u_ShadowSpread, vec2(0.0))
//                );
//                count += 1;
//            }
//        }
//        shadow /= count;

        // Fixed to 2:
        // --- Shadow mapping: fixed 5x5 kernel (ShadowSamples=2) ---
        float shadow = 0.0;
        for (int si = -2; si <= 2; ++si) {
            for (int sj = -2; sj <= 2; ++sj) {
                shadow += textureProj(
                    p3d_LightSource[i].shadowMap,
                    vertInShadowSpaces[i] + vec4(vec2(si, sj) * u_ShadowSpread, vec2(0.0))
                );
            }
        }
        shadow /= 25.0;

        diffuseTemp.rgb  *= shadow;
        specularTemp.rgb *= shadow;

        diffuseTemp.rgb  *= attenuation;
        specularTemp.rgb *= attenuation;

        diffuse  += diffuseTemp;
        specular += specularTemp;

        // Rim
        vec4 rimLight = vec4(0.0);
        rimLight.rgb = vec3(1.0 - max(0.0, dot(normalize(-vertPosition.xyz), normalize(normal))));
        rimLight.rgb = pow(rimLight.rgb, vec3(2.0)) * u_RimLightIntensity;
        rimLight.rgb *= diffuse.rgb;
        rim += rimLight;

    }

    vec4 ambient = p3d_Material.ambient * p3d_LightModel.ambient * diffuseColor;
    vec3 color = (diffuse.rgb + rim.rgb + specular.rgb * 50. + ambient.rgb + p3d_Material.emission.rgb);
//    color = rim.rgb;
    fragColor = vec4(color, diffuseColor.a);

}