#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 g_windowWidth = 1280.0;
uniform float g_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(g_windowWidth, g_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 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;
}

void main() {


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

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

    ivec2 tcs = ivec2(tc.x*g_windowWidth, tc.y*g_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;
    vec3 rayO = ray*dist;
    ray = normalize(ray);

    vec3 normal = getNormal3(dn.gb);
    //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);
    vec3 rayR = reflect(ray,-normal);
  //  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.1);// *dir*halver;
    float dist2=0.0;
    // float n = tex2D(smNoise, tc);
    float diff=0.0;
    float diffPrev=0.0;
    int inNow=0;

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

//    korv = 2.0;

    int loops = int(64.0*1.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.15;

    for (i=0; i<loops; i++) {
        //rayO += rayR*0.3;
        vec3 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;
        ivec2 tcs2 = ivec2(tc2.x*g_windowWidth, tc2.y*g_windowHeight);
        vec4 d2 = texelFetch(texDepth, tcs2, 0);
        dist2 = linearDepth(d2.r);
        diff = dist2-rayO.z;
        inNow=0;
        float absDiff = abs(diff);
        if (onceIn == 0) {
           if (diff<0.0 && i>=0 && absDiff<(0.45+float(i)/loops*1.450)) {
                tcHit = tc2;
                onceIn = 1;
                inNow=1;
                if (absDiff < kkx) {
                    break;
                }
            }
            fwdMul = (fwdMulBase+0.25*rayR.z*float(i)/loops);
            rayO+=rayR*(fwdMul*noiser/(1.0+loops*0.005));
            //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<1.50) {
                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(dist2-rayO.z);
    if (a>0.150) {
        onceIn = 0;
    }

    result *= g_bgBaseAmount;

    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);

  //  result.rgb = no;

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