#version 430

in vec2 uv;

layout(location = 0) out vec4 frag;

uniform float g_time;
uniform float g_timeStep;


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 simMode = 0.0;


// TODO #include "noise.h"

uniform float noiseOct = 6.0;
uniform float noiseOctMul = 2.0;
uniform float noiseOctAmp = 0.5;
uniform float noiseSplineEnabled = 1.0;

// from pouet raymarching thread by las of mercury
float perlin(vec3 p) {
    vec3 i = floor(p);
    vec4 a = dot(i, vec3(1., 57., 21.)) + vec4(0., 57., 21., 78.);
    vec3 f = cos((p-i)*3.14159265)*(-.5)+.5;
    a = mix(sin(cos(a)*a),sin(cos(1.+a)*(1.+a)), f.x);
    a.xy = mix(a.xz, a.yw, f.y);
    return mix(a.x, a.y, f.z)-0.0;
}
float turb3(vec3 c) {
        float r=0.0;
        float s=0.5;
        for (int i=0;i<noiseOct;i++) {
                r+=s*(perlin(c));
                c*=noiseOctMul;
                s*=noiseOctAmp;
        }
        return r;
}

vec3 posd(float pw) {
    return vec3(sin(pw)*17.0, cos(pw*1.7)*13.0, sin(pw*0.78+cos(pw*0.4))*11.0)*32.0;
}

float getSplineValue(float t, float p0, float pt0, float p1, float pt1) {
    float t2 = t*t;
    float t3 = t*t*t;
    float p = (2.0*t3-3.0*t2+1.0)*p0+(t3-2.0*t2+t)*pt0+(-2.0*t3+3.0*t2)*p1+(t3-t2)*pt1;
    return p;
}

float perlin4(vec4 p) {
//    vec4 pf1 = floor(p);
//    vec4 pf2 = floor(p+vec4(1.0));

    float p1 = perlin(p.xyz+posd(floor(p.w)+1.0));
    float p2 = perlin(p.xyz+posd(floor(p.w)+2.0));

    if (noiseSplineEnabled > 0.5) {
        float p0 = perlin(p.xyz+posd(floor(p.w)));
        float p3 = perlin(p.xyz+posd(floor(p.w)+3.0));
        float ki = fract(p.w);
        return getSplineValue(ki, p1, p2-p0, p2, p3-p1);
    } else {
        float ki = smoothstep(0.0, 1.0, fract(p.w));
        return p1*(1.0-ki)+p2*(ki);
    }
}

float turb4(vec4 c) {
        float r=0.0;
        float s=0.5;
        for (int i=0;i<int(noiseOct);i++) {
                r+=s*(perlin4(c));
                c*=noiseOctMul;
                s*=noiseOctAmp;
        }
        return r;
}

// end of "noise.h"


uniform float noisePos = 0.0;
uniform float noiseOfsX = 0.0;
uniform float noiseOfsY = 0.0;
uniform float noiseFreq = 0.50;

uniform float dispAmp = 0.15;
uniform float dispExp = 5.0;


uniform float dirForceAmp = 0.0;
uniform float dirForceX = 0.0;
uniform float dirForceY = 0.0;
uniform float dirForceZ = 0.0;


layout(binding=0, rgba32f) uniform image2D pardexPos;
layout(binding=1, rgba32f) uniform image2D pardexAge;
layout(binding=2, rgba32f) uniform image2D pardexVel;

float deg2pi = 2.0*3.141592/360.0;

uniform float div = 1.0;

uniform float scaleX = 25.0;
uniform float scaleY = 25.0;
uniform float scaleZ = 25.0;

uniform float partNum = 0.0;

void applyDisp(inout float no) {
    no = (sign(no)*pow(abs(no), dispExp));
}

uniform float simDamp = 0.9;

uniform mat4 projectionMatrix;
uniform mat4 viewMatrix;
layout(binding=0) uniform sampler2D velBufBlur;
layout(binding=1) uniform sampler2D worldPosBlur;

uniform float pixVelAmp = 0.0;
uniform float pixVelDistExp = 0.25;

uniform float collElast = 0.9;
uniform float collFrict = 0.8;


layout(binding=2) uniform sampler2D texWorldPos;
layout(binding=3) uniform sampler2D texDepthNorm;

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

float zn = 0.0;
float zf = 1.0;
uniform mat4 projSecond;
uniform mat4 projInvSecond;
uniform mat4 viewInvSecond;
uniform mat4 viewSecond;
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 = projSecond[3][2]/(ndcPos.z-(projSecond[2][2]/projSecond[2][3]));
    clipPos.xyz = ndcPos * clipPos.w;
    return projInvSecond * clipPos;
}


uniform float layers = 6.0;
uniform float skroller = 0.0;


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

    vec4 screen = vec4(0.0);

    ivec2 triBufSize = imageSize(pardexPos)/2;

    float ageLeft = imageLoad(pardexAge, uvs).x;

    int divi = int(div);
    int pid = uvs.x+uvs.y*triBufSize.x;

    if (ageLeft <= 0.0 && simMode>0.5) { discard; return; }

    if (pid >= partNum) { discard; return; }

    int vid = pid;
    vec3 tvp = vec3(0.0f);

    int divio = divi;
    int lay = int(layers);
    if (lay > 0) {
      divi = divi/lay;
    }

    int vpr = (divi);

    int vl = vid/(vpr*vpr);
    vid = vid-vl*vpr*vpr;

    int vr = vid/vpr;
    int vx = vid-vr*vpr;
    float vprf = float(divi-1);
    tvp.xy = vec2(float(vx)*2.0/vprf-1.0, float(vr)*2.0/vprf-1.0);
    tvp.z = float(vl)*0.05;




    //tvp.xyz = vec3(sin(pidf*0.001), cos(pidf*0.012), 1.0*sin(pidf*0.014+cos(pidf*0.0123)));

    float no = 0.0;
    float no2 = 0.0;

    vec3 tvpm = tvp;
    vec2 dotPos = vec2(floor(tvpm.x)+0.5, floor(tvpm.y)+0.5);

    // noise

    float evo = 0.001*0.0;
    float movex = 0.2*0.0;
//    no = turb4(vec4(tvp.xyz*noiseFreq+vec3(noiseOfsX, noiseOfsY+g_time*movex, 0.0), noisePos+g_time*evo));
//    no2 = turb4(vec4(tvp.xyz*noiseFreq*1.32+vec3(noiseOfsX+0.76, noiseOfsY+1.23+g_time*movex, 0.0), noisePos+g_time*evo));

    vec3 tvn = tvp.xyz;

    //float pidi = (smoothstep(0.0, 1.0, float(pid)*0.001-g_time*0.0)-0.0)*g_time*32.0;

   // float pidi = (smoothstep(0.0, 1.0, float(pid)*0.0005)*0.0-1.0)*(g_time*1.0+0.00+float(pid)*0.0)*16.0+g_time*300.0;
    //no = turb4(vec4(tvp.xy*noiseFreq+vec2(noiseOfsX, noiseOfsY+g_time*0.0), 0.0, noisePos+g_time*0.02));
   // no2 = turb4(vec4(tvp.xy*noiseFreq*1.32+vec2(noiseOfsX+0.76, noiseOfsY+1.23), 0.0, noisePos));

//    // spikes
////    float frk = noiseFreq;
////    tvpm.xy *= frk;
////    vec3 dd = (vec3(tvpm.xy, 0.0)-vec3(dotPos, 0.0))*2.0;
////    no = length(dd)*1.0;

//    // balles
////    float frk = noiseFreq;
////    tvpm.xy *= frk;
////    vec3 dd = (vec3(tvpm.xy, 0.0)-vec3(dotPos, 0.0))*2.0;
////    float def = dot(dd, dd);
////    if (def<1.0) {
////        no = sqrt(1.0-def);
////    } else {
////        no = 0.0;
////    }
////    no = 1.0-no;

//    // modulos
////    no = 1.0-no;
////    float notu = turb4(vec4((dotPos.xy+tvp.xy*0.5)*noiseFreq+vec2(noiseOfsX, noiseOfsY), 0.0, noisePos));
////    no *= notu;
////    no = 1.0-no;


    if (simMode < 0.5) {

        float dPerRow = scaleY/vprf;

        vec3 tvnm = tvn;

     //   float skroller = g_time*1.0;

      //  tvnm.y -= floor(skroller)*2.0/scaleY;

        float purugum = floor(skroller)*2.0/vprf-0.0;

        float indo = vr+purugum;

        tvnm.y += purugum;

        no = turb4(vec4(tvnm.xyz*noiseFreq+vec3(noiseOfsX, noiseOfsY+g_time*movex, 0.0), noisePos+g_time*evo));
        no2 = turb4(vec4(tvnm.xyz*noiseFreq*1.32+vec3(noiseOfsX+0.76, noiseOfsY+1.23+g_time*movex, 0.0), noisePos+0.0+g_time*evo));

        applyDisp(no);
        applyDisp(no2);

        //float pam = (1.0-smoothstep(0.0, 1.0, float(pid)*0.001))*12.0;
        float pam = 5.0;

        tvp.x += no*dispAmp*pam;
        tvp.z += no2*dispAmp*pam;

        tvp.xyz *= vec3(scaleX, scaleY, scaleZ);

        float scrollPos = 0.0*fract(skroller)*2.0*dPerRow;
      //  float scrollPos = fract(skroller)*2.0/vprf;
      //  tvp.y -= (tvnm.y-purugum);
         tvp.y -= fract(skroller)*2.0*dPerRow;
    //    tvp.y -= fract(scrollPos/(scaleY/divi))*(scaleY/divi);
     //   tvp.y -= fract(scrollPos/(divi))*divi;


        imageStore(pardexPos, uvs, vec4(tvp, 0.0));

        imageStore(pardexPos, uvs+triBufSize.xy, vec4(scrollPos, indo, 0.0, 0.0));

    } else {
        vec4 prevPos = imageLoad(pardexPos, uvs);
        vec4 pp = prevPos;
        vec4 np = pp;


        no = turb4(vec4(pp.xyz*noiseFreq+vec3(noiseOfsX, noiseOfsY+g_time*movex, 0.0), noisePos+g_time*evo));
        no2 = turb4(vec4(pp.zxy*noiseFreq*1.32+vec3(noiseOfsX+0.76, noiseOfsY+1.23+g_time*movex, 0.0), noisePos+1.72+g_time*evo));
        float no3 = turb4(vec4(pp.yzx*noiseFreq*1.92+vec3(noiseOfsX+1.26, noiseOfsY+0.53+g_time*movex, 0.0), noisePos+0.32+g_time*0.5*evo));

        applyDisp(no);
        applyDisp(no2);
        applyDisp(no3);

        vec3 vel = imageLoad(pardexVel, uvs).xyz;

        vec3 velD = vec3(0.0);

        velD += dirForceAmp*vec3(dirForceX, dirForceY, dirForceZ);

        velD.x += no*dispAmp;
        velD.y += no3*dispAmp;
        velD.z += no2*dispAmp;

        if (abs(pixVelAmp) > 0.001) {
          // get blurred world position
          vec4 ps = viewMatrix*vec4(np.xyz, 1.0);
          ps = projectionMatrix*ps;
          ps /= ps.w;
          vec2 psuv = ps.xy*0.5+vec2(0.5);
          // psuv.y = 1.0-psuv.y;
          vec4 wpBlur = texture2D(worldPosBlur, psuv);

          vec3 pToWpBlur = np.xyz-wpBlur.xyz;
          float distWpBlur = pow(dot(pToWpBlur, pToWpBlur), pixVelDistExp);
          float mgWp = 1.0/(distWpBlur+0.1);
          mgWp = clamp(mgWp, 0.0, 1.0);

          vec4 velBlur = texture2D(velBufBlur, psuv);
          velD.xyz += -velBlur.xyz*mgWp*pixVelAmp*25.0;
        }

        vec3 newVel = vel.xyz+velD.xyz;
        newVel.xyz *= simDamp;

        vec3 newPos = np.xyz + newVel*g_timeStep;

        vec4 npp = viewSecond*vec4(newPos, 1.0);
        npp = projSecond*npp;
        npp /= npp.w;
        vec2 nps = npp.xy*0.5+vec2(0.5);
//        vec2 texSize = textureSize(texWorldPos, 0);
//        ivec2 npsn = ivec2(nps*texSize);
        // npwp = New Pos World Pos
        //vec3 npwp = texelFetch(texWorldPos, npsn, 0).xyz;
        vec3 npwp = texture2D(texWorldPos, nps).xyz;

        // npn = New Pos Normal
        //vec3 npn = vec3(texelFetch(texDepthNorm, npsn, 0).yz, 0.0);
        vec3 npa = -vec3(texture2D(texDepthNorm, nps).yzw);

        vec3 npn = vec3(cos(npa.y), 0.0, sin(npa.y));
        npn.xy = rotateXY2(npn.xy, npa.x);

      //  npn.z = sqrt(1.0-dot(npn.xy, npn.xy));
        npn = normalize(npn);
        // npn = -npn;

        vec3 npwpd = npwp-newPos;

        float npDist = sqrt(dot(npwpd, npwpd));



        vec4 spp = viewSecond*vec4(pp.xyz, 1.0);
        spp = projSecond*spp;
        spp /= spp.w;
        vec2 ps = spp.xy*0.5+vec2(0.5);
        // vec2 texSize = textureSize(texWorldPos, 0);
       //  ivec2 psn = ivec2(ps*texSize);
        // npwp = New Pos World Pos
//        vec3 pwp = texelFetch(texWorldPos, psn, 0).xyz;
        vec3 pwp = texture2D(texWorldPos, ps).xyz;

        // npn = New Pos Normal
        //vec3 npn = vec3(texelFetch(texDepthNorm, npsn, 0).yz, 0.0);
        // vec3 npn = vec3(texture2D(texDepthNorm, nps).yz, 0.0);
        // npn.z = sqrt(1.0-dot(npn.xy, npn.xy));
        // npn = -npn;

        vec3 pwpd = pwp-pp.xyz;

        float pDist = sqrt(dot(pwpd, pwpd));







      //  float distForce = 1.0/(npDist+0.01);

        vec3 kkb = npwp.xyz;
       // vec3 kkb = np.xyz;
        float kkk = dot(kkb.xy, kkb.xy);

        kkk = pow(kkk, 1.50);
     //   newVel = kkk*vec3(0.0, 0.0, -0.002);


      //   if (npDist < 3.0 && (npDist<(pDist+0.5))) {
          if (npDist < 4.0) {
          float dotVel = dot(normalize(newVel), npn);
          if (dotVel < 0.0) { // going against the normal should mean going into the surface -> collide


            vec3 vr = reflect(newVel, npn);
            vec3 tanComp = (vr+newVel)*0.5;
         //   vec3 norComp = (vr+newVel)*0.5;
            float cel = collElast;
            newVel = -(vr-tanComp)*cel-tanComp*(1.0-collFrict);

            // kvg - keeps the values on the ground
            float kvg = clamp(1.0/(npDist*1.0+0.10), 0.0, 1.0);

           // float kvg = clamp((4.0-npDist)*0.25, 0.0, 1.0);
          //  float kvg = clamp((npDist)*0.5, 0.0, 1.0);
            newVel += 5.0*pow(kvg, 2.50)*npn.xyz;


            // newVel = -1.0*newVel;

            //newVel = -100.0*newVel;
           // newVel = vec3(0.0, 0.0, -16.0);
          }
        }

       // newVel = vec3(0.0, 0.0, -64.0);
        vel.xyz = newVel;

        newPos = np.xyz+vel.xyz*g_timeStep;


        imageStore(pardexVel, uvs, vec4(vel, 0.0));
        imageStore(pardexPos, uvs, vec4(newPos, 0.0));
    }

    vec4 age = imageLoad(pardexAge, uvs);
    age.x -= g_timeStep;
  //  age.y += g_timeStep;
    imageStore(pardexAge, uvs, age);


    discard;

    screen.a = 1.0;
    frag = screen;


}
