#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;

// 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; }

//    vec2 u_TexShift = vec2(0.0, 0.);
//    vec2 u_TexShift = vec2(0/3., 0/3.);

    // Normal combined
//    vec4 diffuseColor = texture(p3d_Texture0, vertMultiTexCoord0);
//    vec4 diffuseColor = texture(p3d_Texture0, vertMultiTexCoord0);
//    diffuseColor.rgb *= 1.0 - vertColor.rgb;
//    diffuseColor.rgb = 1.0 - diffuseColor.rgb;

    vec4 diffuseColor;
    vec4 roughnessMap;
    vec4 normalTex;
//    if (domiPosition.z < 0.2) {
//        diffuseColor = vec4(1.0) * vertColor;
//        roughnessMap = vec4(0.0);
//        normalTex = vec4(1.0);
//    } else {
        diffuseColor = texture(p3d_Texture0, vertMultiTexCoord0) * vertColor;
        vec3 negColor = 1.0 - diffuseColor.rgb;
        diffuseColor.rgb = mix(diffuseColor.rgb, negColor, neg);
        roughnessMap = texture(p3d_Texture1, vertMultiTexCoord0);
        normalTex = texture(p3d_Texture2, vertMultiTexCoord0);
//    }


//    vec4 diffuseColor = texture(p3d_Texture0, vertMultiTexCoord0) * vertColor;
//    vec4 diffuseColor = vertColor;

//    vec4 roughnessMap = texture(p3d_Texture1, vertMultiTexCoord0 + u_TexShift);
//    vec4 roughnessMap = vec4(0.0);
//    vec4 roughnessMap = vec4(0.0);

    float roughness = roughnessMap.r;
    float shininess = (1.0 - roughness) * MAX_SHININESS * u_MaxShininess;

    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 materialSpecularColor = p3d_Material.specular;

//    vec4 normalTex = texture(p3d_Texture2, vertMultiTexCoord0 + u_TexShift);
//    vec4 normalTex = vec4(0.5, 0.5, 1.0, 1.0);

    vec3 normal;

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

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

        vec3 normalRaw = normalize(normalTex.rgb * 2.0 - 1.0);
        normal = normalize(mat3(tangent, binormal, vertNormal) * normalRaw);

        vec3 unitLightDirection = normalize(lightDirection);
        vec3 eyeDirection       = normalize(-vertPosition.xyz);
        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
        );
        diffuseTemp = clamp(diffuseTemp, vec4(0), diffuseColor);

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

        vec4  lightSpecularColor = p3d_LightSource[i].specular;

        vec4 materialSpecularColor = vec4(vec3(1.0 - roughness), 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  = clamp(specularTemp.rgb, 0.0, 1.0);  // Comment to have more specular
        specularTemp.rbg *= u_SpecularPower;

        // 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
        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;

        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 + ambient.rgb + p3d_Material.emission.rgb);
    fragColor = vec4(color, diffuseColor.a);

}
