#pragma kernel InitDust
#pragma kernel UpdateDust

struct Particle {
  float3 position;
  float3 velocity;
  uint enabled;
  uint airborne;
};

RWStructuredBuffer<float4> positions;
RWStructuredBuffer<Particle> particles;
RWStructuredBuffer<uint> numParticlesConsumed;
RWStructuredBuffer<uint> freeParticles;
uint numParticles;
uint particlesToInitialize;
float3 particleInitPos;
float3 initialVelocity;
float deltaTime;

float3 mop1Pos;
float3 mop2Pos;

float CleanRadius;

float size;
float gravity;
float particleVel;

// Hash function www.cs.ubc.ca/~rbridson/docs/schechter-sca08-turbulence.pdf
uint hash(uint state) {
  state ^= 2747636419u;
  state *= 2654435769u;
  state ^= state >> 16;
  state *= 2654435769u;
  state ^= state >> 16;
  state *= 2654435769u;
  return state;
}

float scaleToRange01(uint state) { return state / 4294967295.0; }

[numthreads(64, 1, 1)] void InitDust(uint3 id
                                     : SV_DispatchThreadID) {
  if (id.x > particlesToInitialize) {
    return;
  }

  uint i = freeParticles[id.x];

  // if (particles[id.x].enabled != 0 || numParticlesInitialized[0] >=
  // numParticles ) {
  while (i <= numParticles) {
    if (i == numParticles) {
      return;
    }

    if (particles[i].enabled == 0) {
      break;
    }

    i++;
  }

  uint randState = i;

  randState = hash(randState);
  float dv = scaleToRange01(randState) * 3.14f * 2.0f;
  float dx = cos(dv);
  float dy = sin(dv);
  randState = hash(randState);
  float dz = scaleToRange01(randState);

  float3 nvel = float3(dx, dy, dz) * particleVel;

  randState = hash(randState);
  nvel.xy *= scaleToRange01(randState);

  randState = hash(randState);
  float3 iv = initialVelocity * scaleToRange01(randState);

  particles[i].velocity = iv + nvel;

  particles[i].position = particleInitPos;
  particles[i].position.z = 0.01;
  particles[i].enabled = 1;
  particles[i].airborne = 1;
  positions[i] = float4(particles[i].position, 1 * size);
  // particles[i].position = positions[i].xyz;

  // InterlockedAdd(numParticlesInitialized[0],1);
}

    [numthreads(64, 1, 1)] void UpdateDust(uint3 id
                                           : SV_DispatchThreadID) {
  uint i = id.x;

  if (particles[i].enabled == 0) {
    return;
  }

  float3 pos = particles[i].position;

  if (particles[i].position.z > 0) {
    particles[i].velocity.z -= gravity * deltaTime;
  } else {
    particles[i].velocity -= particles[i].velocity * deltaTime * 15;

    if (particles[i].airborne == 1) {
      particles[i].airborne = 0;

      // Increase splattered particles
      InterlockedAdd(numParticlesConsumed[2], 1);
    }

    float3 offset1 = mop1Pos - pos;
    offset1.z = 0;
    float dist1 = dot(offset1, offset1);

    float3 offset2 = mop2Pos - pos;
    offset2.z = 0;
    float dist2 = dot(offset2, offset2);

    float dist = min(dist1, dist2);

    if (dist < CleanRadius) {
      particles[i].enabled = 0;
      // Increase cleaned particles
      if (dist1 < dist2) { // Mop1 is the one that cleaned
        InterlockedAdd(numParticlesConsumed[0], 1);
      } else {
        InterlockedAdd(numParticlesConsumed[1], 1);
      }
    }
  }

  particles[i].position += particles[i].velocity * deltaTime;
  positions[i] = float4(particles[i].position, particles[i].enabled * size);
}