#version 120
//#extension GL_EXT_gpu_shader4 : require

#pragma optimize(on)

// http://www.gamedev.net/community/forums/topic.asp?topic_id=463075&PageSize=25&WhichPage=4
// by ArKano22

uniform sampler2D map_depth;
uniform sampler2D map_albedo;
uniform sampler2D map_aux;
uniform sampler2D map_normal;
uniform sampler2D map_noise;

uniform float contextWidth;
uniform float contextHeight;
uniform float contextX;
uniform float contextY;

uniform vec2 cameraClip;

varying vec4 positionView;

// unpack vector for linear depth
//const vec3 unpack = vec3( 255.0/256, 255.0/(256*256), 255.0/(256*256*256) );

#define numSamples 6

// For all settings: 1.0 = 100% 0.5=50% 1.5 = 150%
vec3 ContrastSaturationBrightness(vec3 color, float brt, float con, float sat) {
  // Increase or decrease theese values to adjust r, g and b color channels seperately
  const float AvgLumR = 0.0;
  const float AvgLumG = 0.0;
  const float AvgLumB = 0.0;

  const vec3 LumCoeff = vec3(0.2125, 0.7154, 0.0721);

  vec3 AvgLumin = vec3(AvgLumR, AvgLumG, AvgLumB);
  vec3 brtColor = color * brt;
  vec3 intensity = vec3(dot(brtColor, LumCoeff));
  vec3 satColor = mix(intensity, brtColor, sat);
  vec3 conColor = mix(AvgLumin, satColor, con);

  return conColor;
}

float GetEdge(vec2 pos) {
  vec2 offsets[9];
  offsets[0] = vec2(0.0, 0.0);
  offsets[1] = vec2(-1.0 / (contextWidth * 0.001), -1.0 / (contextHeight * 0.001)); // round the clock ;)
  offsets[2] = vec2(0.0, -1.0 / (contextHeight * 0.001));
  offsets[3] = vec2(1.0 / (contextWidth * 0.001), -1.0 / (contextHeight * 0.001));
  offsets[4] = vec2(1.0 / (contextWidth * 0.001), 0.0);
  offsets[5] = vec2(1.0 / (contextWidth * 0.001), 1.0 / (contextHeight * 0.001));
  offsets[6] = vec2(0.0, 1.0 / (contextHeight * 0.001));
  offsets[7] = vec2(-1.0 / (contextWidth * 0.001), 1.0 / (contextHeight * 0.001));
  offsets[8] = vec2(-1.0 / (contextWidth * 0.001), 0.0);

  /*
  for (int i = 0; i < 9; i++) {
    offsets[i].x /= contextWidth;
    offsets[i].y /= contextHeight;
  }
  */

  float depths[9];
  vec3 normals[9];
  for (int i = 0; i < 9; i++) {
    float depth = texture2D(map_depth, pos + offsets[i] * 0.001).x;
    depths[i] = -cameraClip.y / (depth - cameraClip.x);
    normals[i] = texture2D(map_normal, pos + offsets[i] * 0.001).xyz;
  }

  vec4 deltas1;
  vec4 deltas2;
  deltas1.x = depths[1];
  deltas1.y = depths[2];
  deltas1.z = depths[3];
  deltas1.w = depths[4];
  deltas2.x = depths[5];
  deltas2.y = depths[6];
  deltas2.z = depths[7];
  deltas2.w = depths[8];

  deltas1 = abs(deltas1 - depths[0]);
  deltas2 = abs(depths[0] - deltas2);

  vec4 minDeltas = max(min(deltas1, deltas2), 0.00001);
  vec4 maxDeltas = max(deltas1, deltas2);

  vec4 depthResults = step(minDeltas * 35.0, maxDeltas);

  deltas1.x = dot(normals[1], normals[0]);
  deltas1.y = dot(normals[2], normals[0]);
  deltas1.z = dot(normals[3], normals[0]);
  deltas1.w = dot(normals[4], normals[0]);
  deltas2.x = dot(normals[5], normals[0]);
  deltas2.y = dot(normals[6], normals[0]);
  deltas2.z = dot(normals[7], normals[0]);
  deltas2.w = dot(normals[8], normals[0]);
  deltas1 = abs(deltas1 - deltas2);

  vec4 normalResults = step(0.5, deltas1);
  vec4 results = max(normalResults, depthResults);

  return (results.x + results.y + results.z + results.w) * 0.25;
}

void main(void) {
  // todo: maybe we can skip this. ambient is rendered on a quad with set texcoords already
  vec2 texCoord = gl_FragCoord.xy;
  texCoord.x -= contextX;
  texCoord.y -= contextY;
  texCoord.x /= contextWidth;
  texCoord.y /= contextHeight;
  
  vec3 base = texture2D(map_albedo, texCoord).xyz;
  /* blur
  base = base + texture2D(map_albedo, texCoord + vec2(0, 1 / contextHeight)).xyz;
  base = base + texture2D(map_albedo, texCoord + vec2( 1 / contextHeight, 0)).xyz;
  base = base + texture2D(map_albedo, texCoord + vec2(0, -1 / contextHeight)).xyz;
  base = base + texture2D(map_albedo, texCoord + vec2(-1 / contextHeight, 0)).xyz;
  base = base / 5.0;
  */
  
  float brightness = 0.4;

  // blueish tint + brightness
//  base.r *= 1.0 * brightness;
//  base.g *= 1.08 * brightness;
//  base.b *= 1.3 * brightness;
  base.r *= 0.8 * brightness;
  base.g *= 0.9 * brightness;
  base.b *= 1.6 * brightness;

  
  // screen space ambient occlusion
  // crease method, (c) megan fox

  float depth = texture2D(map_depth, texCoord).x;
  float linearDepth = -cameraClip.y / (depth - cameraClip.x);
  vec3 normal = texture2D(map_normal, texCoord).xyz;
  vec4 aux = texture2D(map_aux, texCoord.st);

  vec3 centerPosition = vec3(aux.x, aux.y, aux.z);
  
  //vec3 position = vec3(positionView.xy * linearDepth / positionView.z, linearDepth);
  // calculate fragments location in view-space
  //vec2 positionClip = (positionView * gl_ProjectionMatrix).xy / (positionView * gl_ProjectionMatrix).z;
  //vec3 centerPosition = vec3(positionClip, linearDepth);
  //vec3 centerPosition = vec3(texCoord.xy, depth);
  

  float totalSSAO = 0;
  int samples = 0;
  
  float depthCorrection = 1.0 - depth;// (1 / cameraClip.x);// * 0.5 + 0.5;
  depthCorrection = 0.15 * 0.6 + depthCorrection * 0.4; // don't use full depth correction. incorrect, but looks nicer this way
  //depthCorrection = 0.15;
  
  /*
  int randomBlah = int(depth * 10000.0 + linearDepth * 10.0);
  float randomQ = (randomBlah % 100) * 0.02 - 1.0; // -1 .. 1
  randomQ = randomQ * 0.05;
  */

  for (float sample = 0; sample < numSamples; sample++) {
    float x = sin(sample / numSamples * 2.0 * 3.1415926535);
    float y = cos(sample / numSamples * 2.0 * 3.1415926535);
  //for (float x = -2; x < 3; x += 1) {
    //for (float y = -2; y < 3; y += 1) {
      //if (!(x == 0 && y == 0)) {

      /*
        vec2 noiseCoord = texCoord.st;
        noiseCoord.x *= contextWidth / 32.0;
        noiseCoord.x += randomQ + sample * 7.135;
        noiseCoord.y *= contextHeight / 32.0;
        noiseCoord.y += randomQ + sample * 9.827;
        vec2 random = texture2D(map_noise, noiseCoord).xy * 2.0 - vec2(1.0);
        random.x /= contextWidth;
        random.y /= contextHeight;
      */

        //vec2 offset = vec2(x * 0.12, y * 0.12);
        //vec2 offset = vec2(x * 0.02, y * 0.02);
        //vec2 offset = vec2(x * 0.006, y * 0.018); // todo: correct aspect ratio for resolution
        vec2 offset = vec2(x * 0.01 / (contextWidth * 0.001), y * 0.01 / (contextHeight * 0.001)); // todo: correct aspect ratio for resolution
        
    //float sampleDepth = texture2D(map_depth, texCoord + offset * depthCorrection).x;
    //sampleDepth = -cameraClip.y / (sampleDepth - cameraClip.x);

        //vec3 samplePosition = vec3(texCoord.xy + offset * depthCorrection, sampleDepth);

        //float depth2 = texture2D(map_depth, texCoord).x;
        //float depthCorrection2 = 1.0 - depth;// (1 / cameraClip.x);// * 0.5 + 0.5;
        //depthCorrection2 = 0.15 * 0.2 + depthCorrection2 * 0.8; // don't use full depth correction. incorrect, but looks nicer this way

        vec2 totalOffset = offset * depthCorrection;

        //if (fabs(totalOffset.s) * contextWidth < 1) totalOffset.s = 1.0 / contextWidth;
        //if (fabs(totalOffset.t) * contextHeight < 1) totalOffset.t = 1.0 / contextHeight;
        if (abs(totalOffset.s) * contextWidth >= 1.0 || abs(totalOffset.t) * contextHeight >= 1.0) {

          vec2 sampleCoord = texCoord + totalOffset;

          vec3 samplePosition = texture2D(map_aux, sampleCoord).xyz;
        
          vec3 toCenter = samplePosition - centerPosition;
          float distance = length(toCenter);
          //toCenter.z *= 0.01;

          toCenter = normalize(toCenter);

          //vec3 sampleNormal = texture2D(map_normal, sampleCoord).xyz;

          float contrib = clamp(dot(toCenter, normal) * 1.7 - 0.7, 0.0, 1.0);
          //contrib *= 2;
          //contrib *= 1 - min(1.0, pow(distance * 5.0, 2.0));
          contrib -= distance * 2.5;
          contrib = clamp(contrib, 0.0, 1.0);

          totalSSAO += contrib;
          
          samples++;
        }
      //}
    //}
  }
  float SSAO = 0.0;
  if (samples > 0) SSAO = (totalSSAO * 1) / samples;
  SSAO = clamp(SSAO, 0.0, 1.0);


  // self-illumination
  float self_illumination = aux.w;

  vec3 fragColor = vec3(clamp(base * (1.0 + self_illumination), 0.0, 1.0));



  /*
  float brightness2 = 1.0;// * (1.5 - brightness);
  float contrast = 1.0;//1.35;
  float saturation = 0.4;//0.95;//1.06;
  fragColor = ContrastSaturationBrightness(fragColor, brightness2, contrast, saturation);
  */
  
  
  
  //fragColor = vec4(vec3(normal.z * 0.5 + 0.5), 1.0);

  gl_FragData[0] = vec4(fragColor, 1.0);
  //gl_FragData[1].r = 0;

  gl_FragData[1].r = GetEdge(texCoord); // AA
  gl_FragData[1].g = SSAO;
  
//  gl_FragData[0] = vec4(vec3(SSAO), 0);
}
