#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;
uniform mat4 projectionMatrix;
uniform mat4 projectionInvMatrix;

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;


vec4 CalcEyeFromWindow(in vec3 windowSpace) {
    vec3 ndcPos;
    vec4 viewport = vec4(0.0, 0.0, 1280.0, 720.0);
    ndcPos.xy = ((2.0 * windowSpace.xy) - (2.0 * viewport.xy)) / (viewport.zw) - 1;
//    ndcPos.z = (2.0 * windowSpace.z - zNear - zFar) / (zFar - zNear);
    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, tcs, 0);
    // float dist = getPointDist(d.r);

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

    vec4 dn = texelFetch(texNormal, tcs, 0);

  //  float dist = linearDepth(d.r);
  //  vec2 tc2 = tc*2.0-1.0;
  //  vec3 ray = vec3(tc2, 1.0);
  //  ray.y *= 9.0/16.0;
    vec4 ray;
    vec4 rayO;// = ray*dist;
   // ray = normalize(ray);



    vec4 efe = CalcEyeFromWindow(vec3(gl_FragCoord.xy, d.r));
    vec4 seze = (viewInvMatrix*efe);

    ray = seze;
    vec4 camPos = vec4(viewInvMatrix[3][0], viewInvMatrix[3][1], viewInvMatrix[3][2], viewInvMatrix[3][3]);
    //vec3 camPos = -viewMatrix[3].xyz * mat3(viewMatrix);

   // ray.xyz -= camPos;
   // ray.xyz -= vec3(-5.6, 2.62, -5.36);
    rayO = ray;


    vec4 pkz = viewMatrix*seze;
    pkz = vec4(CalcScrFromEye(pkz), 0.0);
    pkz.b = 0.0;


    vec3 normal = getNormal3(dn.gb)+vec3(0.0, 0.0, 0.0);
    //normal.x *= 1.333;
    //normal.y *= 1.333;
    //vec4 normal4 = (viewMatrix * vec4(normal, 0.0));
    vec4 normal4 = (vec4(normal, 0.0));
    normal = normal4.xyz; ///normal4.w;

    vec3 no = normal;


   // vec3 upV = vec3(viewMatrix[0][0], viewMatrix[0][1], viewMatrix[0][2]);
   // upV = normalize(upV);

    //normal.x = -normal4.x*(1.0+0.33*16.0/9.0*upV.y);
    //normal.y = -normal4.y*(1.0+0.33*upV.x);
   // normal = normalize(normal);


    vec4 rayR = reflect(ray-vec4(camPos),vec4(normal,0.0));
  //  rayR = normalize(rayR);
  //  rayR.xy *= 16.0/9.0;
    vec2 tcHit = tc;
    int onceIn = 0;
    int inTimes = 0;
    float dir = 1.0;
    float halver = 1.0;
    rayO+=rayR*(0.001);// *dir*halver;
    float dist2=0.0;
    float diff=0.0;
    float diffPrev=0.0;
    int inNow=0;

    float korv = g_maxDist*1.0;//g_maxDist*(abs(rayR.z)+2.0);


    //efe = normalize(efe);
   // no = efe.rgb*10.0;
    //no = rayR;

//    korv = 2.0;

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

    float noise = g_noise;
    float noiser = rand(tc.xy*0.1+0.20*vec2(0.3, 1.732)*g_time);
    noiser = ((1.0-noise)+noise*noiser);
    int i=0;

    float kkx = 0.005;
    vec4 pers;
    vec2 tc2;

    for (i=0; i<loops; i++) {
        vec4 ro = rayO;
        //ro.y *= 16.0/9.0*1.0;
       // tc2 = (ro.xy/(ro.z-0.0)+1.0)*0.5;
       // rayO.y *= 9.0/16.0;

        ro = viewMatrix*(rayO);
        //ro.xyz/=ro.w;
        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));

       // break;

//        vec3 pk1 = (vec4(rayO.xyz, 1.0)).xyz;
//        vec4 pk2a = (viewInvMatrix*pers);
//        vec3 pk2 = pk2a.xyz; // /pk2a.w;

       // dist2 = linearDepth(d2.r);
       // diff = (pers.z-rayO.z);
       // diff = sqrt(dot(pk2-pk1, pk2-pk1));
       // diff = pk2.z-pk1.z;

        diff = (pers.z-ro.z);


//        diff = ((pp.z+1.0)*0.5-d2.z);
        inNow=0;
        float absDiff = abs(diff);
        if (onceIn == 0) {
           if (diff>0.0 && i>1 && absDiff<(0.5+float(i)/loops*1.450)) {
          //  if (absDiff<0.020 && i>=0) {
                tcHit = tc2;
                onceIn = 1;
                inNow=1;
                if (absDiff < kkx) {
                //    break;
                }
            }
            fwdMul = 1.0*(fwdMulBase*0.010+0.0010*abs(rayR.z)*float(i)/loops+0.00010*float(i));
           // rayO+=rayR*(fwdMul*noiser/(1.0+0.0*loops*0.005));
            rayO+=rayR*fwdMul;
            //rayO+=rayR*(fwdMul+(0.0+1.0*rand(tc.xy+vec2(0.3, 1.732)*g_time)))*fwdMul;
            td += fwdMul;
        } else {
        //  break;
            halver*=0.75;
            if (diff > 0.0 && absDiff<(0.40+float(i)/loops*0.450)) {
                tcHit = tc2;
                inNow=1;
                if (absDiff < kkx) {
                    break;
                }
            }
            if (inNow>0) {
                //rayO-=rayR*(fwdMul+0.0*rand(tc.xy)*fwdMul)*halver;
                rayO-=rayR*(fwdMul)*halver;
                td -= fwdMul*halver;
            } else {
                //rayO+=rayR*(fwdMul+0.0*rand(tc.xy)*fwdMul)*halver;
                rayO+=rayR*(fwdMul)*halver;
                td += fwdMul*halver;
            }
        }
    }

    float im = float(i)/loops;

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

    result *= g_bgBaseAmount;

    //result.rgb = no.rgb;


    float reflectAmount = g_reflectAmount/(td*0.0*g_distFade+im*im*im*g_distFade*80.0+g_distBase);
    // float fakeAO = clamp(1.0-td-d.r*0.50, 0.0, 1.0);
    float fakeAO = 0.50/(td*1.0+0.5);
    fakeAO = clamp((1.0-fakeAO), 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;
    }
    result *= g_addBlend + (1.0-g_addBlend)*clamp((1.0-reflectAmount), 0.0, 1.0);


    vec4 rc = texture2D(texPrev, tcHit)*g_prevRefl+texture2D(texScreen, tcHit)*(1.0-g_prevRefl);
    rc *= rc;
    result += reflectAmount*rc;
    result *= fakeAO;

    result = clamp(result, 0.0, 10000.0);

    result = pow(result, vec4(0.5));
    result.a = reflectAmount;

    //result.rgb = vec3(dist*0.05);
  //  result.rgb = vec3(dist2*0.05);

//    vec4 test = vec4(efe);
// //   test = viewMatrix*test;
//    test = projectionMatrix*test;
//    test.rgb /= test.w;
//    test.rg += vec2(1.0);
//    test.rg *= 0.5;

//   // test.rg = gl_FragCoord.xy*vec2(1.0/1280.0, 1.0/720.0);

//    result.rg = test.rg;
//    result.b = 0.0;

//    result.rg = CalcScrFromEye(ray+rayR*0.02);
//    result.b = 0.0;


 //  result.rgb = (rayR.rgb)*1.0;

  //  result.rg = tc2;
  //  result.b = 0.0;

    frag = result;
    // frag2 = vec4(0.0, 0.0, 0.0, 0.0);
}
