uniform sampler2D Normal;
uniform sampler2D Shadow;
uniform sampler2D Falloff;
uniform sampler2D Occlusion;

uniform vec3 ViewUp;

uniform float ScreenWidth;
uniform float ScreenHeight;

uniform vec3 LightAbove;
uniform vec3 LightBelow;
uniform vec3 LightHue;
uniform float LightAmbientScale;

varying vec3 eyeToScreen;

vec4 shadowCoords;

float varianceShadowFunction( float distance )
{
  vec4 moments = texture2D( Shadow, shadowCoords.xy );
 //
  distance = exp( 80.0 * distance );
  // fully lit if fragment before occluder
  if( distance <= moments.x )
    return 1.0;
 
  // compute amount of shaodw
  float variance = moments.y - ( moments.x * moments.x );
  variance = max( variance, 0.00002 );

  float d = (distance - moments.x);
  d = max( d, 0.003 );// 0.3 );//
  float p_max = variance / ( variance + (d*d) );

  return p_max;
   
/*
  float temp = exp(30.0 * distance) * exp(-30.0 * moments.x);
  return clamp(temp, 0.0, 1.0);*/
}

void main()
{
  vec4 buff = texture2D( Normal, gl_TexCoord[0].st);

  vec3 norm = 2.0 * buff.xyz - 1.0;

  // get positive ndotl
  float NDotL = max(dot(norm, ViewUp), 0.0);//- 0.7, 0.0) * 3.0;// max( norm.y, 0.0 );// * 0.8 + 0.2;// * 0.5 + 0.5;max(norm.y - 0.2, 0.0);//
  float NDotL2= max(dot(norm, -ViewUp), 0.0) * 0.8 + 0.2;//0.5 + 0.5;//max( -norm.y, 0.0 );// * 0.8 + 0.2;// * 0.5 + 0.5;

  // recover position from gbuffer
  float depth = buff.w;

  // position in view space
  vec4 oldPos;
  oldPos.xyz = depth * normalize( eyeToScreen );
  oldPos.z = -oldPos.z;
  oldPos.w   = 1.0;

  // bias * light projection * light view * view inverse * position in view space
  vec4 shadowViewCoords = gl_TextureMatrix[1] * oldPos;
  // project
  shadowCoords = gl_TextureMatrix[0] * shadowViewCoords;
  shadowCoords.xyz /= shadowCoords.w;

  float shadow = 1.0;
  float distance = length( shadowViewCoords.xyz ) / 6000.0;

  // Falloff at edge of shadow
  shadowCoords.xy = clamp( shadowCoords.xy, 0.0, 1.0 );
  float attenuation = texture2D( Falloff, shadowCoords.xy ).r;

  // Early out or shadows
  if( attenuation > 0.001 )
  {
    shadow = varianceShadowFunction(distance);//attenuation * varianceShadowFunction( distance );
    shadow = shadow - 0.25;
    shadow = shadow * shadow;//2.0;
    shadow += 0.25;
    //shadow = max( shadow, 0.1 );/**/
    shadow = 1.0 - attenuation * (1.0 - shadow);// ( ( 1.0 - shadow ) );
  }

  // Up and down directional lights
  vec3 Light   = (NDotL * shadow) * (LightAbove);// + vec3(0.1, 0.1, 0.12));//vec3(shadow, shadow, shadow);//( shadow */**/ NDotL ) * (LightAbove + vec3(0.1, 0.1, 0.12));
  vec3 Light2  = (NDotL2        ) * (LightBelow);

  // distance quequeing
  distance = buff.a / 6000.0 - 0.3;
  distance = max( distance, 0.0 );
  vec3 Hue = distance * LightHue;

  Light += Light2;
  Light *= (texture2D(Occlusion, gl_TexCoord[0].st).r);
  Light  = mix(Light, LightHue, 0.8 * smoothstep(400.0, 800.0, buff.a));
  Light += Hue;

  gl_FragColor = vec4(Light, 0.0);
}