//source: https://editor.soulmatelights.com/gallery/560-sand-automata

#include "FastLED.h"
#define DATA_PIN 2
#define BRIGHTNESS 255
#define NUM_LEDS 256
#define LED_COLS 16
#define LED_ROWS 16
#define LED_TYPE    WS2812B
#define COLOR_ORDER GRB
CRGB leds[NUM_LEDS];
//#define FRAMES_PER_SECOND 60
const uint8_t kMatrixWidth = 16;
const uint8_t kMatrixHeight = 16;

const bool    kMatrixSerpentineLayout = false;
#define hue 30

void randomdot() {
  byte a = LED_COLS / 2; //random8(LED_COLS/4)+LED_COLS*3/8; //
  if (!random8(4)) leds[XY(a, LED_ROWS - 1)].setHue(random8(hue, hue + 45)); // 0 or 1     
}

void updatesand() {
  int index, indexXadd1Y, indexXsub1Y, indexXYadd1;
  for (int y = 0; y < LED_ROWS - 1; y++) {
    for (int x = 1; x < LED_COLS - 1; x++) {
      index = XY(x, y); indexXadd1Y = XY(x + 1, y); indexXsub1Y = XY(x - 1, y); indexXYadd1 = XY(x, y + 1);
      if (!leds[index] && !leds[indexXYadd1]) continue;
      if (!leds[index] && leds[indexXYadd1]) {leds[index] = leds[indexXYadd1]; leds[indexXYadd1] = 0;} 
        else if (leds[index] && leds[indexXYadd1] && !leds[indexXsub1Y] && !leds[indexXadd1Y]) 
         if (random8(2)) {leds[indexXsub1Y] = leds[indexXYadd1]; leds[indexXYadd1] = 0;} else {leds[indexXadd1Y] = leds[indexXYadd1]; leds[indexXYadd1] = 0;} 
          else if (leds[index] && leds[indexXYadd1] && !leds[indexXsub1Y] && leds[indexXadd1Y]) {leds[indexXsub1Y] = leds[indexXYadd1]; leds[indexXYadd1] = 0;} 
           else if (leds[index] && leds[indexXYadd1] && leds[indexXsub1Y] && !leds[indexXadd1Y]) {leds[indexXadd1Y] = leds[indexXYadd1]; leds[indexXYadd1] = 0;}
    }
  }
}

void randomdel() {
  for (int i = 0; i < NUM_LEDS; i++) {
    if (!random8(3)) leds[i] = 0;
  }
}

void falldown() {
  for (int y = 0; y < LED_ROWS - 1; y++) {
    for (int x = 0; x < LED_COLS; x++) {
      if (!leds[XY(x, y)] && leds[XY(x, y + 1)]) {leds[XY(x, y)] = leds[XY(x, y + 1)]; leds[XY(x, y + 1)] = 0;}
    }
  }
}

void countSand() {
  uint16_t totalSand = 0;
  for (uint16_t i = 0; i < NUM_LEDS; i++) {
    if (leds[i]) {
      totalSand++;
      if (totalSand >= NUM_LEDS / 3 + random8(30)) {randomdel(); falldown(); falldown(); falldown(); break;}  //N_LEDS/3 this how many sands on screen? maybe time to delete some? 
    }
  }
}

void setup() {
  FastLED.addLeds<LED_TYPE,DATA_PIN,COLOR_ORDER>(leds, NUM_LEDS); //setCorrection(TypicalLEDStrip);
  //FastLED.addLeds<LED_TYPE,DATA_PIN,CLK_PIN,COLOR_ORDER>(leds, NUM_LEDS).setCorrection(TypicalLEDStrip);
  FastLED.setBrightness(BRIGHTNESS);
 //Serial.begin(115200);
}

void loop() 
{
  EVERY_N_MILLISECONDS(30) {// 80 is speed of effect
    updatesand(); 
    randomdot();}  
    countSand();
      FastLED.show();

}
 
uint16_t XY( uint8_t x, uint8_t y)
{
  uint16_t i;
  if( kMatrixSerpentineLayout == false) {
    i = (y * kMatrixWidth) + x;
  }
  if( kMatrixSerpentineLayout == true) {
    if( y & 0x01) {
      // Odd rows run backwards
      uint8_t reverseX = (kMatrixWidth - 1) - x;
      i = (y * kMatrixWidth) + reverseX;
    } else {
      // Even rows run forwards
      i = (y * kMatrixWidth) + x;
    }
  }
  return i;
}