#version 430

in vec4 posModel;
in vec4 posWorld;
in vec4 posView;
in vec3 normal;
in vec2 uv;

in vec4 prevPos;

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

layout(binding=3, r32i) uniform iimage2D pardexFree;
layout(binding=4, rgba32f) uniform image2D pardexPos;
layout(binding=5, rgba32f) uniform image2D pardexAge;
layout(binding=6, rgba32f) uniform image2D pardexVel;
layout(binding=7, r32i) uniform iimage2D meshDepth;

layout(binding=2, r32i) uniform iimage2D meshDepthPrev;


layout(binding=0, offset=0) uniform atomic_uint ac_active;
layout(binding=1, offset=0) uniform atomic_uint ac_free;
layout(binding=2, offset=0) uniform atomic_uint ac_emits;


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

#define PI 3.1415926

uniform float g_timeStep = 0.01667;

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

// google glsl rand gave this, thanks and credit flies to
// http://stackoverflow.com/questions/4200224/random-noise-functions-for-glsl
float rand(vec2 co){
  return fract(sin(dot(co.xy ,vec2(12.9898,78.233))) * 43758.5453);
}

float atanSafe(float y, float x) {
    float ret=0.0;
    if (x!=0.0) {
        if (x>0.0) {
            ret=atan(y/x);
        } else	{
            ret=atan(y/x)+3.141592;
        }
    } else {
        if (y>=0.0) {
            ret=0.5*3.141592;
        } else {
            ret=-0.5*3.141592;
        }
    }
    return ret;
}


ivec2 getTrip(int idt) {
    ivec2 triBufSize = imageSize(pardexPos)/2;
    ivec2 trip;
    trip.y = idt/triBufSize.x;
    trip.x = idt-trip.y*triBufSize.x;
    return trip;
}

//int getInd(ivec2 uvi) {
//    return uvi.x+uvi.y*int(imageSize(pardexPos).x);
//}

layout(binding=0) uniform sampler2D tex;
layout(binding=1) uniform sampler2D texBump;
layout(binding=2) uniform sampler2D texRep;
layout(binding=6) uniform sampler2D texMul;
layout(binding=7) uniform sampler2D texRep2;



uniform float emitAmount = 0.0;
uniform float emitAmountFromBright = 0.0;
uniform float emitAge = 1.0;
uniform float emitAgeFromBright = 0.0;
uniform float emitCamDebug = 0.0;

uniform float emitVelDirAmp = 0.0;
uniform float emitVelDirX = 0.0;
uniform float emitVelDirY = 0.0;
uniform float emitVelDirZ = 0.0;

uniform float emitIntersect = 0.0;
uniform float emitIntExp = 1.0;




uniform mat4 projectionMatrix;
uniform mat4 viewInvMatrix;


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



uniform float g_genUV = 0.0;
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);


void main() {
  ivec2 uv = ivec2(gl_FragCoord.xy);

   frag.rgb = vec3(1.50, 0.5, 0.5);
  float r = rand(vec2(uv));

  float ts = clamp(g_timeStep/0.01667, 0.0, 3.0);


  ivec2 triBufSize = imageSize(pardexPos)/2;
  vec2 myUv = vec2(0.0);
  if (g_genUV < 0.5) {
    myUv = uv*g_uvScale.xy;
  } else {
    myUv = posModel.xy*g_uvScale.xy;
  }
  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 nv4 = (viewInvMatrix*vec4(normal, 0.0));
//   nv4.xyz /= nv4.w;
  vec3 nv = nv4.xyz;

  //  frag.rgb = posWorld.xyz;
    int curD = int(-posView.z*1000.0);
  //    if (curD < 0) curD = 0;
    int prevD = imageAtomicMin(meshDepth, ivec2(gl_FragCoord), curD);


  vec4 surfColor = texRepe(tex, texRep, texRep2, myUv);
  frag.rgb = surfColor.rgb;

  float surfBr = dot(surfColor.rgb, vec3(1.0));
  float emitAm = emitAmount;
  if (emitAmountFromBright > 0.0001) {
    emitAm += emitAmountFromBright*surfBr;
  }
  float emitAgeMod = emitAge;
  if (emitAgeFromBright > 0.0001) {
    emitAgeMod += emitAgeFromBright*surfBr;
  }

  int prevDK = imageLoad(meshDepthPrev, ivec2(gl_FragCoord)).x;

  float dedi = abs(float(prevDK-curD));
 // float emd = emitIntersect*pow(1.0/(dedi*2.0+0.10), emitIntExp);
  float emd = 0.0;
  if (emitIntersect > 0.001) {
    emd = 100.0*pow(clamp(1.0-dedi/emitIntersect, 0.0, 10.0), emitIntExp);
    // emd = 100.0*pow(clamp(emitIntersect-dedi, 0.0, 10.0), emitIntExp);
  }
  emitAm += emd;


  vec3 vel = 1.0*(prevPos.xyz - posWorld.xyz);
//  vec4 velVal = vec4(vel, 0.0);
//  imageStore(velBuf, ivec2(gl_FragCoord.xy), velVal);
//  frag4 = vec4(velVal.xyz, 1.0)


  if (r > 1.0-emitAm*0.01*ts) {
      // emit a pardicle
      int emitNumber = int(atomicCounterIncrement(ac_emits));
      int freeLeft = int(atomicCounter(ac_free));
      if (emitNumber < freeLeft) {
          int freeIndex = imageLoad(pardexFree, getTrip(emitNumber)).x;

        //  int activeNumber = int(atomicCounterIncrement(ac_active));
        //  int activeIndex = freeIndex+1;
        //  imageStore(pardexActive, getTrip(activeNumber), ivec4(activeIndex));

          ivec2 emitUV = getTrip(freeIndex);

          float ageLeft;
       //   ageLeft = pow(dot(posModel.xyz, posModel.xyz)*4.0, 2.0);
       //   ageLeft = clamp(ageLeft, 0.1, 4.0);
          ageLeft = emitAgeMod;

          imageStore(pardexPos, emitUV, vec4(posWorld));
          imageStore(pardexPos, emitUV+ivec2(triBufSize.x, 0), surfColor);
          imageStore(pardexPos, emitUV+ivec2(0, triBufSize.y), vec4(nv, 0.0));

       //   float emitAgeLeft = ageLeft*rand(uv);
          float emitAgeLeft = ageLeft*rand(uv);
          imageStore(pardexAge, emitUV, vec4(emitAgeLeft, emitAgeLeft, 0.0, 0.0)); // .x will count down to 0.0, .y will remain in emit value
          imageStore(pardexVel, emitUV, emitVelDirAmp*vec4(emitVelDirX, emitVelDirY, emitVelDirZ, 0.0));
    //      imageStore(pardexVel, emitUV, vec4(0.0, 0.0, -100.0, 0.0));

    //      imageStore(pardexVel, emitUV, vec4(-10.0*vel, 0.0));


           frag.rgb = vec3(1.50, 1.5, 0.5);
      }
  }


//  imageStore(meshDepth, ivec2(gl_FragCoord), ivec4(curD));

//  vec4 pp = (projectionMatrix*posView);
//  pp /= pp.w;
//    frag.x = pp.z;
//    frag.x = gl_FragDepth;
  frag.x = gl_FragCoord.z;


  float na1 = atanSafe(nv.y, nv.x);
  float nkm = sqrt(dot(nv.xy, nv.xy));
  float na2 = -0.5*3.141592;
  if (abs(nkm) > 0.001) {
    na2 = acos(nkm);
  } else {
    if (nv.z > 0.0) {
      na2 = 0.5*3.141592;
    } else {
      na2 = -0.5*3.141592;
    }
  }

 // frag.yzw = normal.xyz;
  frag.yz = vec2(na1, na2);
  frag.w = 1.0;


//  frag.a = 1.;
//   frag.r = 1.0+float(curD)*0.001;
//   frag.a = 1.0;

  if (curD >= prevD) {
    discard;
  }

//  if (emitCamDebug < 0.5) discard;



  frag2.rgb = nv.xyz;

//  frag.a = 1.0;
}


