/******************************************************************************
 * Addressable LED Strip Fireworks Show
 * By Joe Larson, the 3D Printing Professor
 * http://3dpprofessor.com
 ******************************************************************************/

#include "FastLED.h"

/***************************************
 *        Hardware definitions         *
 ***************************************/
#define NUM_LEDS 20
#define LED_DATA_PIN 3
#define POT_PIN A3
/***************************************/
#define Brightness 255
CRGB leds[NUM_LEDS];
int curmode = 1;
CRGBPalette16 gPal;

void setup() 
{
  // put your setup code here, to run once:
  Serial.begin(115200);


  pinMode(POT_PIN, INPUT);

  randomSeed(analogRead(A0));

  FastLED.addLeds<WS2812B, LED_DATA_PIN, GRB>(leds, NUM_LEDS); // Added "GRB" for my manufacturer who decided to mix up the colors
  FastLED.setBrightness(Brightness);
}

const int numModes = 4;
void loop() 
{
  fireworks();
}

/******************************************************************************/
const int numsparks = NUM_LEDS/3;
uint8_t fworkType[numsparks];
float fworkHeat[numsparks];
CRGB fworkColor[numsparks];
float fworkLocation[numsparks];
float fworkVelocity[numsparks];

void fireworks()
{
  fadeToBlackBy(leds, NUM_LEDS, 96);
  //fill_solid(leds, NUM_LEDS, CRGB::Black);   

  if (sparkCount() == 0 || random(100) <= 2) // randomly fire off a new one
  {
    newSpark(1, CRGB(29, 48, 48), 0, 1.5 + (float)random(13)/4.0);
  }
  updateFireworks();
  showFireworks();

  FastLED.show();
  delay(50);
}

void updateFireworks() 
{
  for (int i = 0; i < numsparks; i++)
  {
    switch (fworkType[i])
    {
      case 0:
        break;
      case 1: // rising
        
        //fworkLocation[i] += constrain(fworkVelocity[i], -1.0, 1.0);
        //fworkVelocity[i] -= .08; // gravity
        fworkLocation[i] = random(6,13);
        fworkVelocity[i] = 0.99; // gravity
        if (fworkVelocity[i] < 1.0) { // Explode
          fworkType[i] = 0;
          int numNew = 4 + random16(4);
          float newDiamter = ((float)random16(4) / 3.0) + 1.0;
          CRGB newColor = CHSV(random(255), 192, 255);
          for (int j = 0; j < numNew; j++)
          {
            float newVel = (newDiamter / 2.0) - ((newDiamter/(float)numNew) * (float)(j));
            newSpark(2, newColor, fworkLocation[i]+newVel, newVel + (float)random16(10)/20.0);
          }
        }
        break;
      case 2: // exploding
        fworkLocation[i] += fworkVelocity[i];
        fworkHeat[i] -= 0.07;
        if (fworkHeat[i] <= 0)
        {
          fworkType[i] = 0;
        }
        else if (fworkHeat[i] < 0.5)
        {
          fworkColor[i] -= CRGB(128,128,128);
          fworkVelocity[i] /= 3;
          if (random8(100) < 50)
            fworkVelocity[i] += 0.05;
          else 
            fworkVelocity[i] -= 0.05;
          
          if (random8(100) < 50)
            fworkColor[i] += CRGB(25,25,25);
          else
            fworkColor[i] -= CRGB(25,25,25);
        }
        break;
    }
  }
}
void newSpark(uint8_t type, CRGB newCol, int newLoc, float newVel)
{
  int newfirework = nextdead();
  if (newfirework < 0)
  {
    //Serial.println("Out of new sparks for now");
    return;
  }
  fworkType[newfirework] = type;
  fworkHeat[newfirework] = 1.0;
  fworkColor[newfirework] = newCol;
  fworkLocation[newfirework] = newLoc;
  fworkVelocity[newfirework] = newVel;
}

void showFireworks()
{
  for (int i = 0; i < numsparks; i++)
  {
    //Serial.print(fworkVelocity[i]); Serial.print(",");
    if (fworkType[i] > 0)
    {
      if ((fworkLocation[i] < 0)|| (fworkLocation[i] >= NUM_LEDS))
      {
        fworkType[i] = 0; // just kill it if it goes out of bounds.
      }
      else
      {
        leds[(int)fworkLocation[i]] += fworkColor[i];
      }
    }
  }
  //Serial.println();
}

int sparkCount()
{
  int retval = 0;

  for (int i = 0; i < numsparks; i++)
    if (fworkType[i] > 0)
      retval++;

  return retval;
}

int nextdead()
{
  for (int i = 0; i < numsparks; i++)
  {
    if (fworkType[i] == 0)
      return i;
  }
  return -1;
}
uno:A5.2
uno:A4.2
uno:AREF
uno:GND.1
uno:13
uno:12
uno:11
uno:10
uno:9
uno:8
uno:7
uno:6
uno:5
uno:4
uno:3
uno:2
uno:1
uno:0
uno:IOREF
uno:RESET
uno:3.3V
uno:5V
uno:GND.2
uno:GND.3
uno:VIN
uno:A0
uno:A1
uno:A2
uno:A3
uno:A4
uno:A5
neopixels:DOUT
neopixels:VDD
neopixels:VSS
neopixels:DIN