#version 430

in vec2 uv;

layout(location = 0) out vec4 frag;

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



layout(binding=0) uniform sampler2D tex;
layout(binding=1) uniform sampler2D texDepth;
layout(binding=2) uniform sampler2D texNormal;
layout(binding=3) uniform sampler2D texDepthLight;
layout(binding=4) uniform sampler2D texProjector;
layout(binding=5) uniform sampler2D texPardexMask;


float zn = 0.0;
float zf = 1.0;
uniform mat4 projectionMatrix;
uniform mat4 projectionInvMatrix;
uniform mat4 viewMatrix;
uniform mat4 viewInvMatrix;
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;
}

uniform mat4 projSecond;
uniform mat4 projInvSecond;
uniform mat4 viewSecond;
uniform mat4 viewInvSecond;
vec4 CalcEyeFromLight(in vec3 windowSpace) {
    vec3 ndcPos;
    vec2 texSize = textureSize(texDepthLight, 0).xy;
    vec4 viewport = vec4(0.0, 0.0, texSize.x, texSize.y);
    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 = projSecond[3][2]/(ndcPos.z-(projSecond[2][2]/projSecond[2][3]));
    clipPos.xyz = ndcPos * clipPos.w;
    return projInvSecond * clipPos;
}


uniform vec4 pos = vec4(0.0, 0.0, 0.0, 0.0);
uniform vec4 color = vec4(1.0, 1.0, 1.0, 1.0);

uniform float amp = 1.0;
uniform float _exp = 1.0;

uniform float ambient = 0.50;

uniform float distAmp = 1.0;
uniform float distExp = 1.0;

uniform float projectorAmp = 0.0;
uniform float projectorUvScale = 1.0;

void getWpLightOnly(inout vec4 wpLight, vec2 tcLight, ivec2 ofsex) {
  vec2 tcLightScreen = tcLight*textureSize(texDepthLight, 0).xy;
  vec3 dLight = texelFetch(texDepthLight, ivec2(tcLightScreen)+ofsex, 0).xyz;

//  vec3 dLight = texture2D(texDepthLight, tcLight).xyz;
  vec4 eyeLight = CalcEyeFromLight(vec3(tcLightScreen, dLight.r));
  wpLight = viewInvSecond*eyeLight;

}

void getLightShadows(vec4 wp, inout vec4 wpLight, inout vec2 tcLight, ivec2 ofsex) {
  vec4 projLight = projSecond*viewSecond*wp;
  tcLight = (projLight.xy/projLight.w*0.5+vec2(0.5));
  vec2 tcLightScreen = tcLight*textureSize(texDepthLight, 0).xy;
  vec3 dLight = texelFetch(texDepthLight, ivec2(tcLightScreen)+ofsex, 0).xyz;

//  vec3 dLight = texture2D(texDepthLight, tcLight).xyz;
  vec4 eyeLight = CalcEyeFromLight(vec3(tcLightScreen, dLight.r));
  wpLight = viewInvSecond*eyeLight;
}


// shadows from pardex
uniform float shadowZOfs = -150.0;
uniform float shadowZScale = -5.0;

uniform float transmitEnabled = 1.0;
uniform float transmitPow = 4.0;


uniform mat4 projSecond2;
uniform mat4 viewSecond2;


layout(binding=0, r32f) uniform image2D pardexTransmittance;

uniform float baseDim = 16.0;

float getTransmit(vec4 pw) {
  float transmit = 1.0;
  if (transmitEnabled > 0.5) {
    int baseDimi = int(baseDim);
    int baseDimi2 = baseDimi*baseDimi;
    vec4 pkv = viewSecond2*pw;
    vec3 pkvLi = pkv.xyz;
    pkv = projSecond2*pkv;
    pkv /= pkv.w;
    vec2 p2ds = (pkv.xy+vec2(1.0))*0.50*(baseDim*baseDim-1.0); // 0 to 255 range xy
    ivec2 p2dso = ivec2(p2ds);
    p2ds = clamp(p2ds, 0.0, baseDim*baseDim-1.0);
    // int zLevel = int(-480.0-pv.z*10.0);
    int zLevel = int(shadowZOfs+pkvLi.z*shadowZScale);
    zLevel = clamp(zLevel-1, 0, baseDimi2-1);
    int zLevelY = zLevel/baseDimi;
    int zLevelX = zLevel-zLevelY*baseDimi;
    ivec2 zLevel2 = ivec2(zLevelX, zLevelY)*baseDimi2;

    float km = 1.0;
    if (p2dso.x < 0) {
      km = 0.0;
    }
    if (p2dso.x > baseDim*baseDim-1.0) {
      km = 0.0;
    }
    if (p2dso.y < 0) {
      km = 0.0;
    }
    if (p2dso.y > baseDim*baseDim-1.0) {
      km = 0.0;
    }

    ivec2 p2dsm = ivec2(p2ds);
    float t1 = float(imageLoad(pardexTransmittance, p2dsm+zLevel2).x);
    float t2 = float(imageLoad(pardexTransmittance, ivec2(p2ds)+ivec2(1,0)+zLevel2).x);
    float t3 = float(imageLoad(pardexTransmittance, ivec2(p2ds)+ivec2(0,1)+zLevel2).x);
    float t4 = float(imageLoad(pardexTransmittance, ivec2(p2ds)+ivec2(1,1)+zLevel2).x);

    float bix = fract(p2ds.x);
    float biy = fract(p2ds.y);

    float t12 = mix(t1, t2, bix);
    float t34 = mix(t3, t4, bix);

    transmit = mix(t12, t34, biy)*km+1.0*(1.0-km);
   // transmit = mix(t12, t34, biy);

   // transmit = t1;

    transmit = clamp(transmit, 0.0, 1.0);

  }
  return transmit;
}


void main() {

    vec2 uvS = uv;
    uvS.y = 1.0-uvS.y;

    vec2 tc = gl_FragCoord.xy;

    vec4 screen = texelFetch(tex, ivec2(tc), 0);
    vec3 n = texelFetch(texNormal, ivec2(tc), 0).xyz;

    vec4 pMask = texelFetch(texPardexMask, ivec2(tc), 0);

    vec4 wp;



    vec4 camPos = vec4(viewInvMatrix[3][0], viewInvMatrix[3][1], viewInvMatrix[3][2], viewInvMatrix[3][3]);
    vec4 camLightPos = vec4(viewInvSecond[3][0], viewInvSecond[3][1], viewInvSecond[3][2], viewInvSecond[3][3]);

   // vec4 camDir = vec4(viewMatrix[0][2], viewMatrix[1][2], viewMatrix[2][2], 0.0);
    vec4 camDir = vec4(viewMatrix[2][0], viewMatrix[2][1], viewMatrix[2][2], 0.0);

    vec4 lightDir = vec4(viewSecond[2][0], viewSecond[2][1], viewSecond[2][2], 0.0);

  //  camDir /= camDir.w;
  //  lightDir /= lightDir.w;



    vec4 wpLight = vec4(0.0);
    vec2 tcLight = vec2(0.0);

    vec3 wpLightToLight;


    wp = viewInvMatrix*CalcEyeFromWindow(vec3(tc, texelFetch(texDepth, ivec2(tc), 0).r));
    getLightShadows(wp, wpLight, tcLight, ivec2(0));
    wpLightToLight = (wpLight.xyz-camLightPos.xyz);
    float lightCamDist = sqrt(dot(wpLightToLight, wpLightToLight));

    vec3 wpToLight = (wp.xyz-camLightPos.xyz);
    float eyeCamDist = sqrt(dot(wpToLight, wpToLight));


    vec3 projCol = vec3(1.0);

    if (abs(projectorAmp) > 0.0001) {
      vec2 projUV = (tcLight-vec2(0.5))*projectorUvScale;
      projUV.xy += vec2(0.5);
      projCol = texture2D(texProjector, projUV).rgb;
      projCol = pow(projCol.rgb, vec3(0.5))*projectorAmp;

    }

    screen.rgb = pow(screen.rgb, vec3(0.5));

    vec3 p2v = wp.xyz-pos.xyz;

    float len = sqrt(dot(p2v, p2v));
    p2v /= (len+0.0001);

    float dv = dot(p2v, -n);

    float dm = 1.0/(distAmp*pow(len,distExp)+0.0001);

    float valo = amp*pow(clamp(dv, 0.0, 1.0), _exp);


    int kbz = clamp(int((eyeCamDist-lightCamDist)*10.0), 0, 10);

    float shadowFactor = 0.0;

    int km = 3;
    float smw = 0.0;
    for (int sy=-km; sy<=km; sy++) {
      for (int sx=-km; sx<=km; sx++) {
        ivec2 vd = ivec2(sx,sy)*1;
      //  wp = viewInvMatrix*CalcEyeFromWindow(vec3(tc+vd, texelFetch(texDepth, ivec2(tc+vd), 0).r));
       // wp = viewInvMatrix*CalcEyeFromWindow(vec3(tc+vd, texture2D(texDepth, vec2(tc+vd)/vec2(1280.0, 720.0)).r));
      //  getLightShadows(wp, wpLight, tcLight, vd);
        getWpLightOnly(wpLight, tcLight, vd);
        wpLightToLight = (wpLight.xyz-camLightPos.xyz);
        float lightCamDist = sqrt(dot(wpLightToLight, wpLightToLight));

        vec3 wpToLight = (wp.xyz-camLightPos.xyz);
        float eyeCamDist = sqrt(dot(wpToLight, wpToLight));

        //float w = sx*sx+1.0+sy*sy;
        float w = 1.0;
        shadowFactor += w*clamp((eyeCamDist-lightCamDist)*1.0-0.025, 0.0, 1.0);
        smw += w;
      }
    }
    shadowFactor *= 1.0/smw;
   // shadowFactor *= 1.0/25.0;


    wpLightToLight = (wpLight.xyz-camLightPos.xyz);
    float dotLight = dot(wpLightToLight, -n);
    if (dotLight < 0.02) {
      shadowFactor += (0.02-dotLight)*10.0;
      shadowFactor = clamp(shadowFactor, 0.0 , 1.0);
    }

    float kkb = 1.0;
    lightDir = normalize(lightDir);
    wpToLight = normalize(wpToLight);
    float dotk = dot(lightDir.xyz, -wpToLight);
    kkb = clamp(dotk*12.0-0.9, 0.0, 1.0);
    kkb = 1.0;

   // shadowFactor *= clamp(-dotLight*10.0, 0.0, 1.0);

    valo *= (1.0-shadowFactor)*kkb;

  //  screen.rgb = (screen.rgb)*(valo*projCol.rgb*color.rgb*dm+(1.0-shadowFactor)*(clamp(dv, 0.0, 1.0)*0.5+0.5)*ambient);

    float transmit = getTransmit(wp);

    valo *= pow(transmit, transmitPow);
 //   valo *= transmit*transmit;

    vec3 screenValo = (screen.rgb)*(valo*projCol.rgb*color.rgb*dm+ambient);

    screen.rgb = mix(screenValo.rgb, screen.rgb, pMask.r);

   // screen.rgb = screenValo.rgb;

    screen.rgb *= screen.rgb;

 //   screen.rgb *= transmit;

   // screen.rgb = vec3(eyeCamDist*0.01);

   // screen.rgb = wpLight.xyz;

  //  screen.rg = tcLight.xy;
  //  screen.b = 0.0;

    //screen.rgb = vec3(eye.z*0.01);
 //  screen.rgb = vec3(eyeLight.z*0.01);

 //   screen.rgb = pMask.rgb;

  //  screen.rgb = vec3(transmit);

    screen.a = 1.0;

    frag = screen;
}

