#version 430

layout(binding=0) uniform sampler2D texScreen;
layout(binding=1) uniform sampler2D texNormal;
layout(binding=2) uniform sampler2D texDepth;
layout(binding=3) uniform sampler2D texPrev;

in vec2 uv;

layout(location = 0) out vec4 frag;
layout(location = 1) out vec4 frag2;

uniform float g_time;

float rand(vec2 co){
    return fract(sin(dot(co.xy ,vec2(12.9898,78.233))) * 43758.5453);
}
/*
vec3 tonemapUC2(vec3 x) {
    float A = 0.15;
    float B = 0.50;
    float C = 0.10;
    float D = 0.20;
    float E = 0.02;
    float F = 0.30;
    return ((x*(A*x+C*B)+D*E)/(x*(A*x+B)+D*F))-E/F;
}
*/


uniform float flip=1.0;

uniform float g_posX = 0.0;
uniform float g_posY = 0.0;

uniform float g_origAmount = 1.0;

uniform float windowWidth = 1280.0;
uniform float windowHeight = 720.0;

uniform float g_reflectAmount = 1.0;
uniform float g_addBlend = 0.0;
uniform float g_distFade = 0.50;
uniform float g_distBase = 1.0;
uniform float g_fakeAO = 0.25;
uniform float g_prevRefl = 0.5;
uniform float g_bgBaseAmount = 1.0;
uniform float g_noise = 0.5;
uniform float g_maxDist = 3.0;

// from Iq: http://www.iquilezles.org/www/articles/texture/texture.htm
//vec4 getTexel( sampler2D s, vec2 p ) {
//    //return texture2D(s, p);

//    highp vec2 texRes = vec2(windowWidth, windowHeight);
//    p = p*texRes+0.5;

//    highp vec2 i = floor(p);
//    highp vec2 f = p - i;
//    f = f*f*f*(f*(f*6.0-15.0)+10.0);
//    p = i + f;

//    p = (p - 0.5)/texRes;
//    return texture2D(s, p);
//}

const float zFar = 1000.0;
const float zNear = 0.10;

float getPointDist(float z) {
    float clipA = zFar / (zFar - zNear);
    float clipB = zFar*zNear / (zNear - zFar);
    return clipB/(z-clipA);
}
float getPointZ(float d) {
    float clipA = zFar / (zFar - zNear);
    float clipB = zFar*zNear / (zNear - zFar);
    return (clipB + d*clipA)/d;
}


vec3 getNormal3(vec2 n) {
    vec3 n3;
    n3.xy = n.xy;
    n3.z = sqrt(1.0-dot(n,n));
    return n3;
}

uniform mat4 viewMatrix;
uniform mat4 viewInvMatrix;

float linearDepth(float z) {
//    z = 2.0*z-1.0;
//    float zLinear = 2.0*zNear*zFar/(zFar+zNear-z*(zFar-zNear));
//    return zLinear;

    float clipA = zFar / (zFar - zNear);
    float clipB = zFar*zNear / (zNear - zFar);
    return clipB/(z-clipA)*1.0;
//    return z;
}

float zn = 0.0;
float zf = 1.0;
uniform mat4 projectionMatrix;
uniform mat4 projectionInvMatrix;
vec4 CalcEyeFromWindow(in vec3 windowSpace) {
    vec3 ndcPos;
    vec4 viewport = vec4(0.0, 0.0, windowWidth, windowHeight);
    ndcPos.xy = ((2.0 * windowSpace.xy) - (2.0 * viewport.xy)) / (viewport.zw) - 1;
    ndcPos.z = (2.0 * windowSpace.z - zn - zf) / (zf - zn);
    vec4 clipPos;
    clipPos.w = projectionMatrix[3][2]/(ndcPos.z-(projectionMatrix[2][2]/projectionMatrix[2][3]));
    clipPos.xyz = ndcPos * clipPos.w;
    return projectionInvMatrix * clipPos;
}

vec3 CalcScrFromEye(vec4 eye) {
    vec4 result = projectionMatrix*vec4(eye);
    result.rgb /= result.w;
    result.rg += vec2(1.0);
    result.rg *= 0.5;
    return result.rgb;
}


void main() {


    vec4 result = vec4(0.0);
    vec2 tc = uv;

    result = texture2D(texScreen, tc);
    result *= result;

  //  ivec2 tcs = ivec2(tc.x*windowWidth, tc.y*windowHeight);

    vec4 d = texelFetch(texDepth, ivec2(gl_FragCoord.xy), 0);

    if (d.r<0.001 || d.r>0.99999) {
        discard;
    }

    vec4 dn = texelFetch(texNormal, ivec2(gl_FragCoord.xy), 0);
   // vec4 dn = texture2D(texNormal, tcs);

    vec4 rayStartView = CalcEyeFromWindow(vec3(gl_FragCoord.xy, d.r));
    vec4 ray = viewInvMatrix*rayStartView;
    vec4 camPos = vec4(viewInvMatrix[3][0], viewInvMatrix[3][1], viewInvMatrix[3][2], viewInvMatrix[3][3]);
    vec4 rayO = ray;

    vec3 normal = -dn.rgb; // getNormal3(dn.gb);

    vec3 no = normal;

    vec3 normalO = normal;
    float noiser = rand(tc.xy*0.1+0.20*vec2(0.3, 1.732)*g_time);
    normal = normalO; // + 0.0*vec3(noiser, rand(tc.xy*0.582+0.08*vec2(0.12, 0.88)*g_time), 0.0);
    normal = normalize(normal);
    vec4 rayR = reflect(ray-vec4(camPos),vec4(normal,0.0));
    //rayR = vec4(-normal,0.0);

    vec4 viewRayR = viewMatrix*rayR;

    rayR = normalize(rayR)*3.0;


    viewRayR = normalize(viewRayR);


    vec2 tcHit = tc;
    int onceIn = 0;
    int inTimes = 0;
    float dir = 1.0;
    float halver = 1.0;
   // rayO+=rayR*(0.001*(1.0+rayStartView.z*rayStartView.z*0.04));// *dir*halver;

    float dist2=0.0;
    float diff=0.0;
    float diffPrev=0.0;
    int inNow=0;

    float korv = g_maxDist*1.0;

    int loops = int(64.0);
    loops = min(loops, 256);
    float fwdMulBase = korv/loops;
    float fwdMul = fwdMulBase;
    float td = 0.0;

    float noise = g_noise;


    int i=0;

    float kkx = 0.005;
    vec4 pers;
    vec2 tc2;

    float diffStart;

    result *= g_bgBaseAmount;

    vec4 hitColor = vec4(0.0);
    float reflectAmount = 0.0;
    float rayDist = 0.0;

    int bloops = 1;

    float a;

    int ii;
    for (ii=0; ii<bloops; ii++) {
    inNow = 0;
    onceIn = 0;

//    noiser = rand(tc.xy*1.81+0.20*vec2(0.3, 1.732)*(g_time+float(bloops)*0.3703));
//    normal = normalO+0.15*vec3(noiser, rand(tc.xy*0.582+0.08*vec2(0.12, 0.88)*(g_time+bloops)), 0.0);
//    normal = normalize(normal);
//    rayR = reflect(ray-vec4(camPos),vec4(normal,0.0));
//    rayR = normalize(rayR)*5.0;

    noiser = ((1.0-noise)+noise*noiser);
    rayO=ray+rayR*(0.01);// *dir*halver;
    td = 0.0;
    for (i=0; i<loops; i++) {
        vec4 ro = rayO;

        ro = viewMatrix*(rayO);
        vec3 pp = CalcScrFromEye(ro);
        tc2 = pp.rg;

        ivec2 tcs2 = ivec2(tc2.x*windowWidth, tc2.y*windowHeight);
        vec4 d2 = texelFetch(texDepth, tcs2, 0);
        pers = CalcEyeFromWindow(vec3(tcs2, d2.r));
        diff = (pers.z-ro.z+0.0);
        //diffStart = abs(pers.z-rayStartView.z);

        inNow=0;
        float absDiff = abs(diff);
        if (onceIn == 0) {
          // if (((diff<-0.250 && viewRayR.z>0.0) || (diff>0.0 && viewRayR.z<=0.0)) && i>0 && absDiff<3.0) {
            if (diff>=-0.0 && i>0 && absDiff<1.0) {
          //  if (absDiff<0.020 && i>=0) {
                tcHit = tc2;
                onceIn = 1;
                inNow=1;
                if (absDiff < kkx) {
                    break;
                }
            }
            fwdMul = korv*0.01*(abs(viewRayR.z)*8.0+1.0)*(1.0/loops+1.0*float(i)/loops);
            //fwdMul = 1.0*(fwdMulBase*0.0+korv*0.01*abs(rayR.z)*float(i)/loops+0.0000*float(i));
            rayO+=rayR*(fwdMul*noiser/(1.0+0.0*loops*0.005));
            td += fwdMul;
        } else {
       //  break;
            halver*=0.75;
            if (diff > 0.0 && absDiff<(2.0)) {
                tcHit = tc2;
                inNow=1;
                if (absDiff < kkx) {
                    break;
                }
            }
            if (inNow>0) {
                rayO-=rayR*(fwdMul)*halver;
                td -= fwdMul*halver;
            } else {
                rayO+=rayR*(fwdMul)*halver;
                td += fwdMul*halver;
            }
        }
    }

    float im = float(i)/loops;

    a = abs(diff);
    if (a>0.8) {
        onceIn = 0;
    }

    //result.rgb = no.rgb;

//    vec4 surf = texture2D(texScreen, tc);
//    float surfB = dot(vec3(0.2126, 0.7152, 0.0722), surf.rgb);
//    surfB = 1.0; // 1.0*clamp((0.5-surfB*2.0)*3.0, 0.0, 1.0);

   // float reflectAmount = g_reflectAmount/(td*0.0*g_distFade+im*im*im*g_distFade*80.0+g_distBase);
    rayO-=ray;
    rayDist += dot(rayO,rayO);
    reflectAmount += g_reflectAmount/(rayDist*g_distFade+g_distBase);
    vec4 hc = texture2D(texScreen, tcHit);
    hitColor += hc*hc;

    }


    hitColor = pow(hitColor, vec4(0.5));

    rayDist /= float(bloops);
    reflectAmount /= float(bloops);
    hitColor /= float(bloops);

    float normalZ = (viewRayR.z);
    float viewRayFadeLimit = 0.1;
    float viewRayFade = 1.0;
    if (normalZ > 1.0-viewRayFadeLimit) {
        viewRayFade = (1.0-normalZ)/viewRayFadeLimit;
        viewRayFade *= viewRayFade;
    }

    normalZ = -(viewRayR.z);
    float viewRayFadeLimitAO = 0.1;
    float viewRayFadeAO = 1.0;
    if (normalZ > 1.0-viewRayFadeLimitAO) {
      viewRayFadeAO = (1.0-normalZ)/viewRayFadeLimitAO;
      viewRayFadeAO *= viewRayFadeAO;
    }

//    float blops = clamp(1.0-((a-0.9)/0.4), 0.0, 1.0);

//    blops = pow(blops, 64.0);

//    viewRayFade *= blops;



    // float fakeAO = clamp(1.0-td-d.r*0.50, 0.0, 1.0);
    float fakeAO = 0.50/(td*1.0+1.0);
    fakeAO = fakeAO/(rayDist*g_distFade*1.0+g_distBase);
    fakeAO *= viewRayFadeAO;
    fakeAO = clamp((1.0-fakeAO)+0.1, 0.0, 1.0)*g_fakeAO+(1.0-g_fakeAO);
    if (tcHit.x < 0.0 || tcHit.x > 1.0 || tcHit.y < 0.0 || tcHit.y > 1.0 || onceIn==0) {
        tcHit = tc;
        reflectAmount = 0.0;
        fakeAO = 1.0;
    }
    reflectAmount *= viewRayFade;
    result *= g_addBlend + (1.0-g_addBlend)*clamp((1.0-reflectAmount), 0.0, 1.0);
    vec4 rc = texture2D(texPrev, tcHit)*g_prevRefl+hitColor*(1.0-g_prevRefl);
    rc *= rc;
    result += reflectAmount*rc;
    result *= (fakeAO+0.0);

    result = clamp(result, 0.0, 10000.0);

    result = pow(result, vec4(0.5));

//    float dd = texture2D(texDepth, tc).r;
//   // dd = texelFetch(texDepth, ivec2(gl_FragCoord.xy), 0).r;
//    vec4 rayStartView2 = CalcEyeFromWindow(vec3(gl_FragCoord.xy, dd));
//    vec4 ray2 = viewInvMatrix*rayStartView2;
//// //   d = texelFetch(texDepth, ivec2(gl_FragCoord.xy), 0);
//    dd = pow(ray2.z,1.0)*0.10;
//    result.rgb = 0.5*vec3(dd);

//    float rayAlign = dot(rayR, vec4(0.0, 0.0, 1.0, 0.0)) / 3.0;
//    if (rayAlign > 0.30 && rayAlign < 0.70) {
//        result.r = 1.0;
//      //  discard;
//    }

//    if (viewRayR.z < 0.0) {
//        result.r = 1.0;
//        result.gb = vec2(0.0);
//    }


    result.a = reflectAmount;

   // result.rgb = (ray-vec4(camPos)).rgb*0.1;
   // result.rgb = reflect(ray,vec4(normal,0.0)).rgb*0.1;
   // result.rgb = reflect(ray.rgb/ray.w,normal.rgb)*0.1;
  //  result.rgb = pow(normal.rgb, vec3(1.0));
   // result.rgb = (rayR).rgb*0.1;

//    dn = texelFetch(texDepth, ivec2(gl_FragCoord.xy), 0);
//    dn = texelFetch(texNormal, ivec2(gl_FragCoord.xy), 0);
//    result.rgb = vec3(pow(1.0-dn.r, 0.2));
//    result.a = 1.0;



    frag = result;
}
