#version 330 core
struct Emitter {
  vec3 pos;
  vec3 scale;
  float cumprob;
  float width;
  float height;
  float rot;
};

in vec2 UV;
layout(location = 0) out vec4 state;
layout(location = 1) out vec4 oColor;
layout(location = 2) out vec4 acc;
uniform sampler2D prevState;
uniform sampler2D prevColor;
uniform sampler2D prevAcc;
uniform sampler2D sprites[8];
uniform int frame;
uniform float dt;
uniform vec3 impulsepos;
uniform float impulseforce;
uniform float impulsedecay;
uniform float impulseradius;
uniform float curlAmplitude;
uniform float curlFreq;
uniform int fallbackId;
uniform Emitter emitters[8];


float noise3D(vec3 p)
{
  return fract(sin(dot(p ,vec3(12.9898,78.233,128.852))) * 43758.5453)*2.0-1.0;
}

float simplex3D(vec3 p)
{
  
  float f3 = 1.0/3.0;
  float s = (p.x+p.y+p.z)*f3;
  int i = int(floor(p.x+s));
  int j = int(floor(p.y+s));
  int k = int(floor(p.z+s));
  
  float g3 = 1.0/6.0;
  float t = float((i+j+k))*g3;
  float x0 = float(i)-t;
  float y0 = float(j)-t;
  float z0 = float(k)-t;
  x0 = p.x-x0;
  y0 = p.y-y0;
  z0 = p.z-z0;
  
  int i1,j1,k1;
  int i2,j2,k2;
  
  if(x0>=y0)
  {
    if(y0>=z0){ i1=1; j1=0; k1=0; i2=1; j2=1; k2=0; } // X Y Z order
    else if(x0>=z0){ i1=1; j1=0; k1=0; i2=1; j2=0; k2=1; } // X Z Y order
    else { i1=0; j1=0; k1=1; i2=1; j2=0; k2=1; }  // Z X Z order
  }
  else 
  { 
    if(y0<z0) { i1=0; j1=0; k1=1; i2=0; j2=1; k2=1; } // Z Y X order
    else if(x0<z0) { i1=0; j1=1; k1=0; i2=0; j2=1; k2=1; } // Y Z X order
    else { i1=0; j1=1; k1=0; i2=1; j2=1; k2=0; } // Y X Z order
  }
  
  float x1 = x0 - float(i1) + g3; 
  float y1 = y0 - float(j1) + g3;
  float z1 = z0 - float(k1) + g3;
  float x2 = x0 - float(i2) + 2.0*g3; 
  float y2 = y0 - float(j2) + 2.0*g3;
  float z2 = z0 - float(k2) + 2.0*g3;
  float x3 = x0 - 1.0 + 3.0*g3; 
  float y3 = y0 - 1.0 + 3.0*g3;
  float z3 = z0 - 1.0 + 3.0*g3; 
         
  vec3 ijk0 = vec3(i,j,k);
  vec3 ijk1 = vec3(i+i1,j+j1,k+k1); 
  vec3 ijk2 = vec3(i+i2,j+j2,k+k2);
  vec3 ijk3 = vec3(i+1,j+1,k+1);  
            
  vec3 gr0 = normalize(vec3(noise3D(ijk0),noise3D(ijk0*2.01),noise3D(ijk0*2.02)));
  vec3 gr1 = normalize(vec3(noise3D(ijk1),noise3D(ijk1*2.01),noise3D(ijk1*2.02)));
  vec3 gr2 = normalize(vec3(noise3D(ijk2),noise3D(ijk2*2.01),noise3D(ijk2*2.02)));
  vec3 gr3 = normalize(vec3(noise3D(ijk3),noise3D(ijk3*2.01),noise3D(ijk3*2.02)));
  
  float n0 = 0.0;
  float n1 = 0.0;
  float n2 = 0.0;
  float n3 = 0.0;

  float t0 = 0.5 - x0*x0 - y0*y0 - z0*z0;
  if(t0>=0.0)
  {
    t0*=t0;
    n0 = t0 * t0 * dot(gr0, vec3(x0, y0, z0));
  }
  float t1 = 0.5 - x1*x1 - y1*y1 - z1*z1;
  if(t1>=0.0)
  {
    t1*=t1;
    n1 = t1 * t1 * dot(gr1, vec3(x1, y1, z1));
  }
  float t2 = 0.5 - x2*x2 - y2*y2 - z2*z2;
  if(t2>=0.0)
  {
    t2 *= t2;
    n2 = t2 * t2 * dot(gr2, vec3(x2, y2, z2));
  }
  float t3 = 0.5 - x3*x3 - y3*y3 - z3*z3;
  if(t3>=0.0)
  {
    t3 *= t3;
    n3 = t3 * t3 * dot(gr3, vec3(x3, y3, z3));
  }
  return 96.0*(n0+n1+n2+n3);
  
}

vec3 simplex3Dvec3(vec3 p) {
  float s  = simplex3D(p);
  float s1 = simplex3D(vec3( p.y - 19.1 + 0.01*float(frame) , p.z + 33.4 , p.x + 47.2 ));
  float s2 = simplex3D(vec3( p.z + 74.2 , p.x - 124.5 - 0.001*float(frame) , p.y + 99.4 ));
  vec3 c = vec3( s , s1 , s2 );
  return c;
}

#define ITERATIONS 4


// *** Change these to suit your range of random numbers..

// *** Use this for integer stepped ranges, ie Value-Noise/Perlin noise functions.
#define HASHSCALE1 .1031
#define HASHSCALE3 vec3(.1031, .1030, .0973)
#define HASHSCALE4 vec4(.1031, .1030, .0973, .1099)

// For smaller input rangers like audio tick or 0-1 UVs use these...
//#define HASHSCALE1 443.8975
//#define HASHSCALE3 vec3(443.897, 441.423, 437.195)
//#define HASHSCALE4 vec3(443.897, 441.423, 437.195, 444.129)



//----------------------------------------------------------------------------------------
//  1 out, 1 in...
float hash11(float p)
{
  vec3 p3  = fract(vec3(p) * HASHSCALE1);
    p3 += dot(p3, p3.yzx + 19.19);
    return fract((p3.x + p3.y) * p3.z);
}

//----------------------------------------------------------------------------------------
//  1 out, 2 in...
float rand(vec2 p)
{
  vec3 p3  = fract(vec3(p.xyx) * HASHSCALE1);
    p3 += dot(p3, p3.yzx + 19.19);
    return fract((p3.x + p3.y) * p3.z);
}

float hash1( float n ) 
{
    return rand(vec2(n, 2.0*n*n));
}

vec3 velocity(vec3 p) {
  const float coef = 0.5;
  const vec3 dx = coef * vec3(1.0, 0.0, 0.0);
  const vec3 dy = coef * vec3(1.0, 0.0, 0.0);
  const vec3 dz = coef * vec3(1.0, 0.0, 0.0);

  const float v = 0.0;
  p.x += v*(rand(123.0*UV + 5.0*float(frame)) - .5) * dt / (1./60.);
  p.y += v*(rand(UV.yx*432534.2 - float(frame)) - .5) * dt / (1./60.);
  p.z += v*(rand(vec2(213.0*UV.x + 1234.0 * float(frame), 9432.0*UV.y)) - .5) * dt / (1./60.);

  vec3 t3a = simplex3Dvec3(p - dx);
  vec3 t3b = simplex3Dvec3(p + dx);
  vec3 t3c = simplex3Dvec3(p - dy);
  vec3 t3d = simplex3Dvec3(p + dy);
  vec3 t3e = simplex3Dvec3(p - dz);
  vec3 t3f = simplex3Dvec3(p + dz);
  return (vec3(
      (t3d.z - t3c.z) - (t3f.y - t3e.y),
      (t3f.x - t3e.x) - (t3b.z - t3a.z),
      (t3b.y - t3a.y) - (t3d.x - t3c.x)
    ));
}

uniform float lyfetime; // = 10.0;

void emit() {
  float r = 3.0 + 0. * rand(vec2(
      231.0*UV.y + 43.3*float(frame),
      UV.x
    ));
  float t = 2.0 * 3.1415 * rand(vec2(2343.0*UV.x+UV.y, 23.432*float(frame)));
  float p =       3.1415 * rand(vec2(float(143.54*frame), 1234.2*UV.y+UV.x));
  state = vec4(r * sin(p) * cos(t),
               r * sin(p) * sin(t),
               r * cos(p),
               0.2*lyfetime*rand(vec2(float(frame) + 432.0*UV.x, 23.4 * UV.y))
               );
}

void emitsprite() {
  float pi = 3.141;
  float cb = rand(vec2(483.0 + .2*frame, -42.5 - frame) * UV)*2.0*pi;
  float dd = rand(vec2(2.0 - .5*frame, -.235 + frame) * UV)*2.0*pi;
  int type = (rand(vec2(64.5*frame, .235 + frame) * UV) > 0.75) ? 1 : 0;
  float rr = (type == 0) ? 13.0 : 3.0;
  state = vec4(emitters[0].pos + rr*vec3(sin(dd)*cos(cb),
              sin(dd)*sin(cb),
              cos(dd)),
              lyfetime*rand(vec2(float(frame) + 4.0*UV.x, 2.4 * UV.y)));
  if (type == 0 && fallbackId == 1) { // todo: rocket variable when to do this
    state.xyz = (vec3(
       cb, 0.0, dd
      ) - vec3(pi, -0.0, pi)) * 10.0;
    state.y = pow(abs(state.x + .24*max(0.0, state.z)), 0.5);
      state.xyz = state.xyz - vec3(0.0, 3.0, -24.0);
  }
  oColor = (type == 1) ? 1.2*vec4(vec3(0.5, 0.1, 0.2), 1.0)
                       : vec4(0.3, 0.22, 0.1, 1.0);
  acc = vec4(0.0);
}

const float tam = 1.0;
void main() {
  state = texture(prevState, UV);
  oColor = texture(prevColor, UV);
  acc = texture(prevAcc, UV);

// TODO: velocity initialization
  if (frame == 0 || texture(prevState, UV).a > lyfetime) {
      emitsprite();
  } else {
    for (float t = 0.0; t < tam; t += 1.0) {
      vec3 impulsedir = normalize(state.xyz - impulsepos);
      vec3 dCn = curlAmplitude * float(length(texture(prevAcc, UV).xyz) > 0.0) * velocity(curlFreq * 0.75*(state.xyz + .1*vec3(state.a, 0.0, state.x)) + .01*frame ) * dt;
      vec3 accel = impulseforce * impulsedir / pow(max(length(state.xyz - impulsepos) - impulseradius, 0.5), 1.0);
      vec4 v = vec4(impulsedecay * texture(prevAcc, UV).xyz + accel * dt, acc.w);
      vec3 dImp = v.xyz * dt;
      vec3 dx  = dCn + dImp;
      acc = v;
      acc.xyz += dCn;
      state.xyz += dx;
    }
  }

  // acc = vec4(vec3(0.0), 1.0);
  state.a += dt;
}

// todo. store somewhere the idx of emitter
