#version 430

in vec2 uv;
in float no;

in vec3 norm;
in vec3 tan;
in vec3 bit;

in vec4 posWorld;
in vec4 posView;

layout(binding=0) uniform sampler2D tex;
layout(binding=1) uniform sampler2D texBump;
// layout(binding=2) uniform sampler2D texHill;

uniform float useEmitMap = 0.0;
layout(binding=3) uniform sampler2D texEmit;


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


// layout(binding=2, r32i) uniform iimage2D pardexActive;
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=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;


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

uniform float g_time;


uniform mat4 viewInvMatrix;
uniform mat4 modelViewInvMatrix;

uniform mat4 projectionMatrix;

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

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 texSub = 0.0;
uniform float texMul = 1.0;

vec3 texOp(vec3 t) {
    t.rgb -= vec3(texSub);
    t.rgb = clamp(t.rgb, 0.0, 10000.0);
    t.rgb *= texMul;
    return t;
}

uniform float uvScale = 1.0;

uniform float bumpTexInd = 0.0;


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


// todo: #include "bump.h"
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 *= uvScale;
    } else {
        uv *= bumpUvScale;
    }
    uv += vec2(0.5);
    vec2 d = vec2(bumpTexDelta)/textureSize(tex, 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;
}


float deg2pi = 2.0*3.141592/360.0;





uniform float dispBrightMul = 1.5;
uniform float dispBrightOfs = 0.1;
uniform float dispBrightExp = 4.0;

//uniform float hillTexFade = 1.0;
//uniform float hillTexFadeExp = 1.0;
//uniform float hillTexFadeOfs = 0.0;

uniform float specExp = 4.0;
uniform float specAmp = 2.0;


uniform float diffExp = 1.0;
uniform float diffAmp = 2.0;

uniform float ambient = 0.1;
uniform float texAmount = 0.5;


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 g_timeStep = 0.01667;

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

void main() {
    vec2 uvS = uv;
    // uvS.y = 1.0-uvS.y;
    vec2 uvc = uvS*uvScale+vec2(0.5);

    vec4 r = texture2D(tex, uvS*uvScale+vec2(0.5));


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

    ivec2 triBufSize = imageSize(pardexPos)/2;

/*
    vec4 ht = vec4(0.0);

    if (abs(hillTexFade) > 0.001) {
        //ht = texture2D(texHill, uvS*uvScale);
        ht = texture2D(tex, uvS*uvScale);
    }

    float hillFade = 0.0;


    hillFade = clamp(pow(hiller, hillTexFadeExp)*hillTexFade+hillTexFadeOfs, 0.0, 1.0);
*/

    float dispBright = pow(clamp(no*dispBrightMul+dispBrightOfs, 0.0, 1000.0), dispBrightExp);

    r.rgb = texOp(r.rgb);
  //  ht.rgb = texOp(ht.rgb);

  //  r.rgb = mix(r.rgb, ht.rgb, hillFade);

    r.rgb *= r.rgb;

    r.rgb = vec3(1.0)*(1.0-texAmount)+r.rgb*(texAmount);

    r.rgb = clamp(r.rgb, 0.0, 1000.0);


//    r.rgb = pow(r.rgb, vec3(2.0))*10.0+vec3(0.5);
//    r.rgb = pow(r.rgb, vec3(0.5));

     vec2 bu = vec2(0.0);

     if (bumpTexInd < 0) {
         bu = getBumpG(tex, uvc);
//         if (abs(hillTexFade) > 0.001) {
//            // vec2 buh = getBumpG(texHill, uvS);
//            vec2 buh = getBumpG(tex, uvS);
//            bu = mix(bu, buh, hillFade);
//         }
     } else {
         bu = getBumpG(texBump, uvc);
     }

     vec3 n = norm+bu.x*tan+bu.y*bit;
   //  vec3 n = norm;
     n = normalize(n);

    // nv = n;

 //    vec3 ld = vec3(0.0, 0.0, 1.0);
 //    r.rgb *= clamp(vec3(ambient)+vec3(pow(clamp(dot(ld,n), 0.0, 1.0), diffExp)*diffAmp), 0.0, 10000.0);

  //   r.rgb += vec3(pow(clamp(dot(ld,n), 0.0, 1.0), specExp)*specAmp);

     r.rgb = pow(r.rgb, vec3(diffExp))*diffAmp+vec3(ambient);

     r.rgb *= dispBright;


     r.rgb = clamp(r.rgb, 0.0, 10000.0);


     vec4 nv4 = (viewInvMatrix*vec4(n, 0.0));
  //   nv4.xyz /= nv4.w;
     vec3 nv = nv4.xyz;



     //   nv = n;
     //   vec3 nv = -(modelViewInvMatrix*vec4(norm, 0.0)).xyz;

        nv = normalize(nv);

      //  imageStore(meshDepth, ivec2(gl_FragCoord), vec4(fract(float(gl_FragCoord)*0.02), 0.0, 0.0, 0.0));

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

//int prevD = curD;


// BEGIN pardex emit




     float scrRand = rand(gl_FragCoord.xy);

     vec4 re = r;

     if (useEmitMap > 0.5) {
       re = texture2D(texEmit, uvc);
     }

     float surfBr = dot(clamp((re.rgb-vec3(0.03))/0.97, 0.0, 100000.0), vec3(1.0));

     r.rgb = vec3(1.0, 0.0, 0.0);

     float emitAm = emitAmount;
     if (emitAmountFromBright > 0.0001) {
       emitAm += emitAmountFromBright*surfBr;
     }
     float emitAgeMod = emitAge;
     if (emitAgeFromBright > 0.0001) {
       emitAgeMod += emitAgeFromBright*surfBr;
     }

     float dedi = abs(float(prevD-curD));
     float emd = emitIntersect*pow(1.0/(dedi+1.0), emitIntExp);
     emitAm += emd;

     if (scrRand > 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), re);
             imageStore(pardexPos, emitUV+ivec2(0, triBufSize.y), vec4(nv, 0.0));

         //    imageStore(pardexAge, emitUV, vec4(ageLeft*rand(uv), 0.0, 0.0, 0.0));

         //    float emitAgeLeft = ageLeft*(0.5+0.5*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, vec4(0.0, 0.0, 0.0, 0.0));
             imageStore(pardexVel, emitUV, emitVelDirAmp*vec4(emitVelDirX, emitVelDirY, emitVelDirZ, 0.0));

              r.rgb = vec3(1.50, 1.5, 0.5)*surfBr;
         }
     }

 //    r.rgb *= vec3(surfBr);
// end pardex emit



     r.rgb = pow(r.rgb, vec3(0.5));
     //r.rgb = normalize(norm.rgb)*1.0+vec3(0.0);

    // r.rgb = norm.rgb;
    // r.rgb = normalize(tan.rgb)*1.0+vec3(0.0);



    frag = r;





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

  //  if (emitCamDebug < 0.5) discard;

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

    vec3 normal = norm.xyz;

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

    // frag.yzw = nv.xyz;
    frag.yz = vec2(na1, na2);

   // frag.rgb = vec3(1.0, 0.0, 0.0);

    frag.w = 1.0;

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

    if (curD >= prevD) {
      discard;
    }


    frag2.rgb = normal.xyz;
}

