#version 430 core

in VertexData {
	vec3 position_world;
	vec3 normal_world;
	vec2 uv;
    vec4 fragPosLightSpace;
	mat3 TBN;
} vert;

out vec4 color;

uniform vec3 camera_world;

uniform vec3 materialCoefficients; // x = ambient, y = diffuse, z = specular 
uniform float specularAlpha;
uniform sampler2D diffuseTexture;
uniform float ambientStrength;
uniform bool drawWithNormalMap;

uniform sampler2D normalTex;
uniform sampler2D depthMap;
uniform sampler2D specularTexture;

uniform struct DirectionalLight {
	vec3 color;
	vec3 direction;
} dirL;

uniform struct PointLight {
	vec3 color;
	vec3 position;
	vec3 attenuation;
} pointL;

vec3 phong(vec3 texColor, vec3 n, vec3 lightDir, vec3 viewDir, vec3 lightColor, float diffuseM, float specularM, float alpha, bool attenuate, vec3 attenuation) {
    

    float d = length(lightDir);
    lightDir = normalize(lightDir); 
    vec3 diffuse = diffuseM * max(dot(n, lightDir), 0.0)* lightColor;

    vec3 reflectDir = reflect(-lightDir, n); 
    float spec = pow(max(dot(viewDir, reflectDir), 0.0), alpha);
    vec3 specular = specularM * spec * lightColor * texture(specularTexture, vert.uv).rgb; 
    
	float att = 1.0;	
    if(attenuate){
        att = 1.0f / (attenuation.x + d * attenuation.y + d * d * attenuation.z);
    }

    return diffuse + specular;
}

float calcShadow(vec4 fragPosLightSpace, vec3 normal, vec3 lightDirection)
{
    float bias = 0.005; //max(0.01 * (1.0 - dot(normal, lightDirection)), 0.001);  
    //transform the light-space fragment position in clip-space to normalized device coordinates
    vec3 projCoords = fragPosLightSpace.xyz / fragPosLightSpace.w;//only important for perspective projection
    projCoords = projCoords * 0.5 + 0.5; // in range 0..1
    float currentDepth = projCoords.z;
    if(projCoords.z > 1){
        return 0.0;
    }
    float shadow = 0.0;
    vec2 texelSize = 1.0 / textureSize(depthMap, 0);

    for(int x = -1; x <= 1; ++x){
        for(int y = -1; y <= 1; ++y){
            float pcfDepth = texture(depthMap, projCoords.xy + (vec2(x, y)*texelSize)).r;
            if(currentDepth-bias > pcfDepth){
                shadow++;
            }
        }
    }
    shadow /= 9.0;
    return shadow;
    /* simple shadow mapping without PCF and alias:
    float closestDepth = texture(depthMap, projCoords.xy).r;   
    float shadow;
    if(currentDepth > closestDepth){
        shadow = 1.0;
    } else{
        shadow = 0.0;
    }
    return shadow;
    */
}

void main() {
	vec3 n = normalize(vert.normal_world);
    if(drawWithNormalMap){
        vec3 mappedNormal = (texture(normalTex, vert.uv).rgb);
	    mappedNormal = normalize(mappedNormal * 2.0 - 1.0);
        n = normalize(vert.TBN * mappedNormal); 
    }

	vec3 texColor = texture(diffuseTexture, vert.uv).rgb;
	vec3 ambient = vec3(texColor * materialCoefficients.x*ambientStrength); // ambient
	vec3 v = normalize(camera_world - vert.position_world);
    float shadow = calcShadow(vert.fragPosLightSpace, n, dirL.direction);   
    vec3 light = vec3(0.0);
    
    //shadow nur für directional Light
    light += (1-shadow)* phong(texColor, n, -dirL.direction, v, dirL.color, materialCoefficients.y, materialCoefficients.z, specularAlpha, true, vec3(0));
	
    light += phong(texColor, n, pointL.position - vert.position_world, v, pointL.color, materialCoefficients.y, materialCoefficients.z, specularAlpha, true, pointL.attenuation);

    color = vec4(light*texColor + ambient, 1);
}
