#version 430

layout(binding=0) uniform sampler2D tex;
layout(binding=1) uniform sampler2D texBump;
layout(binding=2) uniform sampler2D texRep;
layout(binding=3) uniform sampler2D texPrevFrame;
layout(binding=4) uniform sampler2D texPrevBlurFrame;
layout(binding=5) uniform sampler2D texPrevNorm;
layout(binding=6) uniform sampler2D texMul;
layout(binding=7) uniform sampler2D texRep2;
// layout(binding=7) uniform sampler2D texDepth;

layout(binding=0, rgba16f) uniform image2D velBuf;

// layout(early_fragment_tests) in;

in vec4 posG;
in vec4 posSG;
in vec4 prevPosG;
in vec4 prevPosSG;
in vec3 normalG;
in vec3 normalWSG;
in vec3 velG;
in vec2 uvG;
in vec3 tangentG;
in vec3 colorG;
in vec4 posW;
// in vec4 posInst;
in vec3 randG;
in vec4 posWM;

layout(location = 0) out vec4 frag; // .rgb
layout(location = 1) out vec4 frag2; // .xyz world space normal
layout(location = 2) out vec4 frag3; // .x emitBuf, .yz - screen space 2d velocity
layout(location = 3) out vec4 frag4; // .xyz world space velocity

uniform float g_time;


uniform float g_uvOfsX = 0.0;
uniform float g_uvOfsY = 0.0;
uniform vec4 g_uvScale = vec4(1.0, 1.0, 1.0, 1.0);

uniform float g_texBrightness = 1.0;

uniform float g_emitNew = 0.0;

uniform float g_prevAmount = 0.0;
uniform float g_prevBlurAmount = 0.0;

uniform float g_genUV = 0.0;


uniform mat4 modelViewMatrix;
uniform mat4 modelViewInvMatrix;
uniform mat4 viewInvMatrix;
uniform mat4 viewMatrix;
uniform mat4 projectionMatrix;
uniform mat4 projectionInvMatrix;

uniform float g_windowWidth = 1280.0;
uniform float g_windowHeight = 720.0;


uniform float g_prevBlurType = 1.0;


uniform float g_bump = 0.0;

const float zFar = 1.0;
const float zNear = 0.0;

uniform float g_mulTexAmp;


uniform vec4 g_animInfo = vec4(0.0, 0.0, 0.0, 0.0); // .x=anim cols, .y=anim frames, .z=anim rows, .w=anim frame int
uniform float g_animLoopFadeFrames = 0.0;

vec4 CalcEyeFromWindow(in vec3 windowSpace) {
    vec3 ndcPos;
    vec4 viewport = vec4(0.0, 0.0, g_windowWidth, g_windowHeight);
    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 - zNear - zFar) / (zFar - zNear);
    vec4 clipPos;
    clipPos.w = projectionMatrix[3][2]/(ndcPos.z-(projectionMatrix[2][2]/projectionMatrix[2][3]));
    clipPos.xyz = ndcPos * clipPos.w;
    return projectionInvMatrix * clipPos;
}

#define PI 3.1415926

vec2 latlong(vec3 v) {
  v = normalize(v);
  float theta = acos(v.z); // +z is up
  float phi = atan(v.y, v.x) + PI;
  return vec2(phi, theta)*vec2(.1591549, .6366198);
}


vec4 rotateXY(vec4 p, float a) {
  vec4 r = p;
  r.x = cos(a)*p.x - sin(a)*p.y;
  r.y = sin(a)*p.x + cos(a)*p.y;
  return r;
}

vec3 rotateXY3(vec3 p, float a) {
  return rotateXY(vec4(p, 0.0), a).xyz;
}


uniform float g_alpha = 1.0;

uniform float g_emitThr = 0.0;
uniform float g_emitAmp = 1.0;

uniform float g_alphaSub = 0.0;



// emit particles stuff

uniform float g_timeStep;

float rand(vec2 co){
  return fract(sin(dot(co.xy ,vec2(12.9898,78.233))) * 43758.5453);
}

layout(binding=0, offset=0) uniform atomic_uint ac;
layout(binding=1, rgba16f) uniform image2D emittedPartPos; // write
layout(binding=2, rgba16f) uniform image2D emittedPartVel; // write
layout(binding=3, rgba16f) uniform image2D emittedPartCol; // write

uniform float g_emitColorThr = 0.01;
uniform float g_emitVelX = 0.0f;
uniform float g_emitVelY = 0.0f;
uniform float g_emitVelZ = 0.0f;
uniform float g_emitVelAmp = 1.0;

uniform float g_emitPercent = 0.0;
uniform float g_vortexAngSpeed = 0.0;

uniform float g_randEmitPos = 0.10;
uniform float g_emitGrayness = 0.0;

void emitParticles(vec3 norm) {
    vec2 norkor = vec2(gl_FragCoord.x/g_windowWidth, gl_FragCoord.y/g_windowHeight);
    float de = 100.0*rand(norkor.xy*1.0+g_randEmitPos*vec2(sin(g_time*6.69), cos(g_time*1.320)));
    float ep = g_emitPercent*g_timeStep/(0.07);
    float br = dot(vec3(1.0), frag.rgb)*frag.a;
    if (de > 100.0-ep && br*100.0>g_emitColorThr) {
        uint counter = atomicCounterIncrement(ac);
        uint cy = counter/(1280);
        uint cx = counter-uint(cy*1280);
        ivec2 listCoord = ivec2(cx,cy);
        vec3 rado = normalize(vec3(posWM.xyz));
        vec3 uppo = vec3(0.0, 1.0, 0.0);
        vec3 radde = cross(rado,uppo);
        vec4 emitVel = vec4(g_emitVelX, g_emitVelY, g_emitVelZ, 0.0)+vec4(radde, 0.0)*g_vortexAngSpeed;
        vec3 emitCol = frag.rgb*frag.a;
        emitCol = (1.0-g_emitGrayness)*emitCol+g_emitGrayness*vec3(dot(vec3(1.0), frag.rgb));

      //  imageStore(emittedPartPos, listCoord, modelMatrix*vec4(posW.xyz, posW.w));
        imageStore(emittedPartPos, listCoord, vec4(posWM.xyz+vec3(0.0, 0.0, 0.0), posWM.w));
        imageStore(emittedPartVel, listCoord, emitVel);
       // float nb = (dot(norm, vec3(0.0, -1.0, 0.0))*0.5+0.5);
        float nb = 1.0;
        imageStore(emittedPartCol, listCoord, vec4(emitCol*20.0*nb, 0.0));
       // frag.rgb = vec3(1.0);
    }
}

// emit particles stuff ends


uniform float texSub = 0.0;
uniform float texMult = 1.0;
vec3 texOp(vec3 t) {
    t.rgb -= vec3(texSub);
    t.rgb = clamp(t.rgb, 0.0, 10000.0);
    t.rgb *= texMult;
    return t;
}

uniform float bumpTexInd = 0.0;
uniform float bumpStrength = 1.0;
uniform float bumpTexDelta = 1.0;
uniform float bumpUvScale = 1.0;
vec2 getBumpG(sampler2D s, vec2 uv) {
    if (abs(bumpUvScale) < 0.0001) {
        uv.xy *= g_uvScale.xy;
    } else {
        uv *= bumpUvScale;
    }
    vec2 d = vec2(bumpTexDelta)/textureSize(s, 0);
    return vec2(texOp(texture2D(s, uv+d.xy).rgb).g-texOp(texture2D(s, uv-d.xy).rgb).g,
                texOp(texture2D(s, uv+d.yx).rgb).g-texOp(texture2D(s, uv-d.yx).rgb).g)*bumpStrength;
}


// TEXREPE begins

vec2 rotateXY2(vec2 p, float a) {
  vec2 r = p;
  r.x = cos(a)*p.x - sin(a)*p.y;
  r.y = sin(a)*p.x + cos(a)*p.y;
  return r;
}

uniform float g_repTex1 = 0.0;
uniform float g_repUVBase1 = 1.0;
uniform float g_repUVOp1 = 0.0;
uniform float g_repUVAmp1 = 0.0;
uniform float g_repUVSca1 = 1.0;
uniform float g_repUVOfsX1 = 0.0;
uniform float g_repUVOfsY1 = 0.0;
uniform float g_repUVRot1 = 0.0;

uniform float g_repTex2 = 0.0;
uniform float g_repUVBase2 = 0.0;
uniform float g_repUVOp2 = 0.0;
uniform float g_repUVAmp2 = 0.0;
uniform float g_repUVSca2 = 1.0;
uniform float g_repUVOfsX2 = 0.0;
uniform float g_repUVOfsY2 = 0.0;
uniform float g_repUVRot2 = 0.0;

uniform float g_repTex3 = 0.0;
uniform float g_repUVBase3 = 0.0;
uniform float g_repUVOp3 = 0.0;
uniform float g_repUVAmp3 = 0.0;
uniform float g_repUVSca3 = 1.0;
uniform float g_repUVOfsX3 = 0.0;
uniform float g_repUVOfsY3 = 0.0;
uniform float g_repUVRot3 = 0.0;

uniform float g_repPow = 1.0;

vec4 texRepe(sampler2D tRep1, sampler2D tRep2, sampler2D tRep3, vec2 uv) {
   vec4 r = vec4(0.0);
   vec2 uvr;
   vec4 rep;

   //     float repLum = dot(vec3(0.299, 0.587, 0.114), rep.rgb);
   //     if (repLum > 0.001) {
   //       r.rgb /= repLum;
   //       repLum = pow(repLum, 1.0);
   //       r.rgb *= repLum;
   //     }

   float hp = 0.5*3.141592;

   if (g_repUVOp1 > 0.5) {
     uvr = rotateXY2(uv, hp*g_repUVRot1/90.0)+vec2(g_repUVOfsX1, g_repUVOfsY1);
     if (g_repTex1 < 0.5) {
       rep = texture2D(tRep1, uvr*g_repUVSca1);
     } else {
       rep = texture2D(tRep1, uvr*g_repUVSca1);
     }
     if (g_repUVOp1 < 1.5) {
      r.rgb += vec3(g_repUVBase1)+rep.rgb*rep.a*g_repUVAmp1;
     } else {
      r.rgb += (r.rgb+vec3(g_repUVBase1))*rep.rgb*rep.a*g_repUVAmp1;
     }
   }

   if (g_repUVOp2 > 0.5) {
     uvr = rotateXY2(uv, hp*g_repUVRot2/90.0)+vec2(g_repUVOfsX2, g_repUVOfsY2);
     if (g_repTex2 < 0.5) {
       rep = texture2D(tRep1, uvr*g_repUVSca2);
     } else {
       rep = texture2D(tRep2, uvr*g_repUVSca2);
     }
     if (g_repUVOp2 < 1.5) {
      r.rgb += vec3(g_repUVBase2)+rep.rgb*rep.a*g_repUVAmp2;
     } else {
      r.rgb *= vec3(g_repUVBase2)+rep.rgb*rep.a*g_repUVAmp2;
     }
   }
   if (g_repUVOp3 > 0.5) {
     uvr = rotateXY2(uv, hp*g_repUVRot3/90.0)+vec2(g_repUVOfsX3, g_repUVOfsY3);
     if (g_repTex3 < 0.5) {
       rep = texture2D(tRep1, uvr*g_repUVSca3);
     } else {
       rep = texture2D(tRep3, uvr*g_repUVSca3);
     }
     if (g_repUVOp3 < 1.5) {
      r.rgb += vec3(g_repUVBase3)+rep.rgb*rep.a*g_repUVAmp3;
     } else {
      r.rgb *= vec3(g_repUVBase3)+rep.rgb*rep.a*g_repUVAmp3;
     }
   }
   r = clamp(r, 0.0, 10000.0);
   r = pow(r, vec4(g_repPow));
   r.a = 1.0;
   return r;
}
// TEXREPE ends


void main() {

  vec3 normal = normalG;
  vec2 uv = uvG;
  vec3 tangent = tangentG;
  vec3 color = colorG;

  vec2 myUv;
  if (g_genUV < 0.5) {
    myUv = uv*g_uvScale.xy;
  } else {
    myUv = posW.xy*g_uvScale.xy;
  }
//  myUv += randG.yz;
  myUv.y *= -1.0;
  myUv.x += g_uvOfsX;
  myUv.y += g_uvOfsY;

  if (g_animInfo.y > 1.0) { // if anim frames
      float animFramesPerRow = g_animInfo.x;
      float animFrames = g_animInfo.y;
      float animRows = g_animInfo.z;
      float animFrameInt = g_animInfo.w;

      float animWidth = 1.0/animFramesPerRow;
      float animHeight = 1.0/animRows;

      myUv *= vec2(animWidth, animHeight);
      myUv.x += fract(animFrameInt/animFramesPerRow)*animFramesPerRow*animWidth;
      myUv.y += floor(animFrameInt/animFramesPerRow)*animHeight;
  }

 vec4 diffuse = vec4(0.0);

 diffuse = texRepe(tex, texRep, texRep2, myUv.xy);
 diffuse.rgb *= diffuse.rgb;
 diffuse.rgb *= g_texBrightness;

// diffuse.rgb += vec3(g_texAmbient);

  vec3 lightPos = vec3(0.0*cos(g_time*2.0), 0.0, 5.0);
  vec3 surfPos = vec3(0.0f, 0.0f, 0.0f);
  vec3 lightDir = lightPos-surfPos;

  float lightDist = sqrt(dot(lightDir, lightDir));

  vec3 myNormal = normalize(normal);
  tangent = normalize(tangent);
  vec3 biTangent = cross(myNormal, tangent);


 // vec4 normFromTex = texture2D(texNorm, myUv*1.0);
 // float bumpX = -(normFromTex.x-0.5)*g_bump;
 // float bumpY = -(normFromTex.y-0.5)*g_bump;

  vec2 bu = vec2(0.0);
  if (bumpTexInd > 0.5) {
    bu = getBumpG(texBump, myUv.xy)*g_bump;
  } else {
    bu = getBumpG(tex, myUv.xy)*g_bump;
  }
  //tangent = vec3(1.0, 0.0, 0.0);
  myNormal += tangent*bu.x + biTangent*bu.y;
  myNormal = normalize(myNormal);


  float normalLen = sqrt(dot(myNormal, myNormal));

  float d = dot(lightDir, myNormal)/(lightDist*normalLen);
  d = clamp(d, 0.0, 1.0);


  float specPow = 320.0;

  frag = diffuse*0.50;

  vec4 c = frag;


  c.rgb *= 0.20+vec3(clamp(myNormal.x, 0.0, 1.0))*0.80;
  frag3.rgb = clamp(c.rgb-g_emitThr, 0.0, 1000.0)*g_emitAmp; // emitBuf

  if (g_prevBlurType < 0.5) {
      vec2 norkor = vec2(gl_FragCoord.x/g_windowWidth, gl_FragCoord.y/g_windowHeight);
      vec2 paske = (norkor.xy)-0.1*myNormal.xy;
      paske = clamp(paske, 0.0, 1.0);
      vec3 prevb = texture2D(texPrevBlurFrame, paske).rgb;
      c.rgb += g_prevBlurAmount*pow(clamp(prevb-vec3(0.01), 0.0, 8.0),vec3(2.0));

  } else {
      vec3 normalB = (projectionMatrix*viewMatrix*vec4(myNormal, 0.0)).xyz;
      normalB /= normalB.z;
      vec2 norkor = vec2(gl_FragCoord.x/g_windowWidth, gl_FragCoord.y/g_windowHeight);
      vec2 paske = (norkor.xy)-0.025*(normalB.xy);
      paske = clamp(paske, 0.0, 1.0);
      float kk = 1.0;

      vec3 jorge = texture2D(texPrevNorm, paske).rgb*1.0;
      vec4 efe = CalcEyeFromWindow(vec3(paske.x*g_windowWidth, paske.y*g_windowHeight, jorge.r));
      vec3 seze = (viewInvMatrix*vec4(efe.xyz, 1.0)).xyz*1.0;

      vec3 jorgeC = texture2D(texPrevNorm, norkor).rgb*1.0;
      vec4 efeC = CalcEyeFromWindow(vec3(norkor.x*g_windowWidth, norkor.y*g_windowHeight, jorgeC.r));
      vec3 sezeC = (viewInvMatrix*vec4(efeC.xyz, 1.0)).xyz*1.0;

      vec3 sp = seze-sezeC;
      float kx = (dot(sp, sp));
      float dok = 1.0/(clamp(kx, 0.0, 10000.0)*0.0+1.0);
      // if (kx > 100.0) dok = 0.0;
      dok = clamp(dok, 0.0, 100.0);

      sp/=sqrt(kx);
      float dok2 = dot(sp, -normal)+0.0;
      dok2 = clamp(dok2, 0.0, 1.0);

      float dok2a = 0.30;
      dok *= (dok2*dok2a+1.0-dok2a);

   //   dok = 1.0;

      vec3 prevb = texture2D(texPrevBlurFrame, paske).rgb*clamp(dok*kk, 0.0, 1.0);
      c.rgb += (0.0+1.0*g_prevBlurAmount*(pow(clamp(prevb-vec3(0.0), 0.0, 8.0),vec3(2.0))-vec3(0.0)));


      //c.rgb = seze*0.01;
      //c.rgb = vec3(dok);
  }

  c.rgb = clamp(c.rgb, 0.0, 10.0);

  c.a = diffuse.a*g_alpha;

  c.a -= g_alphaSub;

//  vec4 dep = texelFetch(texDepth, ivec2(gl_FragCoord.xy), 0);
//  if (gl_FragCoord.z > dep.x) {
//    discard;
//  }

  frag = c;

  if (c.a < 0.001) {
      discard;
  }


  if (g_mulTexAmp > 0.001) {
    vec4 mu = texture2D(texMul, myUv);
    float gee = clamp(g_mulTexAmp, 0.0, 1.0);
    frag = frag*(1.0-gee)+gee*frag*dot(vec3(0.3333), mu.rgb);
  }


 // inster = posInst.x;
 // frag.rgb = vec3(inster*0.01);// vec3(float(int(fract((inster)*0.1)*5.0))*0.2);
//  frag.a = c.a;

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

 // frag.rgb = tangent.rgb;


//  vec4 velVal = vec4(vel, 0.0, 0.0);

  vec3 vel = 25.0*(prevPosG.xyz - posG.xyz);
  vec4 velVal = vec4(vel, 0.0);

//    imageStore(velBuf, ivec2(gl_FragCoord.xy), velVal);

  frag4 = vec4(velVal.xyz, 1.0);
//  frag4 = vec4(1.0);


 //   emitParticles(myNormal);


 // frag.rg
  //frag.rgb = 0.1*posG.xyz;
  //frag.rgb = 0.1*prevPosG.xyz;
 // frag.b = 0.0;

 // frag.rgb = vec3(inster)*0.5;

//  frag.a = 1.0;


  biTangent = cross(normalWSG.xyz, tangent);
  vec3 myNormalWSG = normalWSG.xyz+tangent.xyz*bu.x+biTangent*bu.y;

  frag2.rgb = normalize(myNormalWSG);
  //frag2 = clamp(frag2, -1.0, 1.0);
  frag2.a = 1.0;

//  frag3 = pow(frag3, vec4(0.5));
  float bright = dot(frag.rgb, vec3(1.0));
  frag3.r = frag.a*clamp(bright-g_emitThr, 0.0, 1000.0)*g_emitAmp; // emitBuf

  vec2 vel2D = 25.0*(prevPosSG.xy/prevPosSG.w - posSG.xy/posSG.w);
 // frag3.r = 0.0;
  frag3.gb = vel2D;
  frag3.a = 1.0;

  //frag.r = 1.0;


}

