#include <Adafruit_NeoPixel.h>
#define BUTTON_PIN 6
#define PIXEL_PIN  2  // Digital IO pin connected to the NeoPixels.
#define NUM_LEDS 10   // set to an even number
#define MIRROR 5     // set to 1/2 of the LED count
Adafruit_NeoPixel strip = Adafruit_NeoPixel(NUM_LEDS, PIXEL_PIN, NEO_GRB + NEO_KHZ800);

boolean oldState = HIGH;
int     mode     = 0;    // Currently-active animation mode, 0-4
byte lastMode = 0;
unsigned long lastRead;
int intervalDebounce = 50;
byte aniStep = 0;
int CurrentAni = 0;
uint32_t CurrentColor = 0;
int  CurrentPixel = 0;
unsigned long msecLastAniStep = 0;
int AniStepDelay = 250;
bool AniDone = true;


void setup() {
  strip.begin();
  strip.show(); // Initialize all pixels to 'off'
  pinMode(BUTTON_PIN, INPUT_PULLUP);
}

void loop() {

  unsigned long now = millis();

  StepAni();

  if (now - lastRead >= intervalDebounce) {
    // Get current button state.
    boolean newState = digitalRead(BUTTON_PIN);
    if (newState != oldState) {
      lastRead = now;//start debouncing..
      oldState = newState;
      if (newState == LOW) {     // Yes, still low
        if (++mode > 4) mode = 0; // Advance to next mode, wrap around after #8
      }
    }
  }

  if (mode != lastMode) {
    switch (mode) {          // Start the new animation...
      case 0:
        AniDone = false;
        CurrentPixel = 0;
        colorWipe(strip.Color(  0,   0,   0), 50);    // Black/off
        break;
      case 1:
        AniDone = false;
        CurrentPixel = 0;
        colorWipe(strip.Color(240,   240,   240), 50);    // White
        break;
      case 2:
        AniDone = false;
        CurrentPixel = 0;
        colorWipe(strip.Color(  0, 255,   0), 50);    // Green
        break;
      case 3:
        AniDone = false;
        CurrentPixel = 0;
        colorWipe(strip.Color(  0,   0, 255), 50);    // Blue
        break;
      case 4:
        CurrentAni = 1;//fire
        AniStepDelay = 15;
        colorBlack();
        break;
    }
    lastMode = mode;
  }
}


void StepAni ()
{ //see if it's time for a step..
  unsigned long currMilli = millis();
  if (currMilli - msecLastAniStep >= AniStepDelay)
  {
    msecLastAniStep = currMilli;
    switch (CurrentAni) {
      case 0: colorWipe(CurrentColor, AniStepDelay); break;
      case 1: Fire(55, 120, 15, 1); break;
    }
  }
}


void colorBlack() {
  for (int i = 0; i < strip.numPixels(); i++) {
    strip.setPixelColor(i, strip.Color(  0,   0,   0));
  }
  strip.show();
}

void colorWipe(uint32_t color, unsigned long wait)
{
  CurrentAni = 0;
  AniStepDelay = wait;
  CurrentColor  = color;

  if (!AniDone)
  {
    strip.setPixelColor(CurrentPixel, CurrentColor);
    strip.show();
  }

  if (CurrentPixel < strip.numPixels())
  {
    CurrentPixel++;
  } else
  {
    CurrentPixel = 0;
    AniDone = true;
  }
}


void Fire(int Cooling, int Sparking, int SpeedDelay, int Mirror) {
  static byte heat[NUM_LEDS];
  int cooldown;

  // Step 1.  Cool down every cell a little
  for ( int i = 0; i < NUM_LEDS; i++) {
    cooldown = random(0, ((Cooling * 10) / NUM_LEDS) + 2);

    if (cooldown > heat[i]) {
      heat[i] = 0;
    } else {
      heat[i] = heat[i] - cooldown;
    }
  }

  // Step 2.  Heat from each cell drifts 'up' and diffuses a little
  for ( int k = NUM_LEDS - 1; k >= 2; k--) {
    heat[k] = (heat[k - 1] + heat[k - 2] + heat[k - 2]) / 3;
  }

  // Step 3.  Randomly ignite new 'sparks' near the bottom
  if ( random(255) < Sparking ) {
    int y = random(7);
    heat[y] = heat[y] + random(160, 255);
    //heat[y] = random(160,255);
  }

  // Step 4.  Convert heat to LED colors
  for ( int j = 0; j < NUM_LEDS - MIRROR; j++) {
    setPixelHeatColor(j + MIRROR, heat[j] );
    setPixelHeatColor(MIRROR - j, heat[j] );
  }

  showStrip();
  // delay(SpeedDelay);
}

void setPixelHeatColor (int Pixel, byte temperature) {
  // Scale 'heat' down from 0-255 to 0-191
  byte t192 = round((temperature / 255.0) * 191);

  // calculate ramp up from
  byte heatramp = t192 & 0x3F; // 0..63
  heatramp <<= 2; // scale up to 0..252

  // figure out which third of the spectrum we're in:
  if ( t192 > 0x80) {                    // hottest
    setPixel(Pixel, 255, 255, heatramp);
  } else if ( t192 > 0x40 ) {            // middle
    setPixel(Pixel, 255, heatramp, 0);
  } else {                               // coolest
    setPixel(Pixel, heatramp, 0, 0);
  }
}

void showStrip() {
#ifdef ADAFRUIT_NEOPIXEL_H
  // NeoPixel
  strip.show();
#endif
#ifndef ADAFRUIT_NEOPIXEL_H
  // FastLED
  FastLED.show();
#endif
}

void setPixel(int Pixel, byte red, byte green, byte blue) {
#ifdef ADAFRUIT_NEOPIXEL_H
  // NeoPixel
  strip.setPixelColor(Pixel, strip.Color(red, green, blue));
#endif
#ifndef ADAFRUIT_NEOPIXEL_H
  // FastLED
  leds[Pixel].r = red;
  leds[Pixel].g = green;
  leds[Pixel].b = blue;
#endif
}

void setAll(byte red, byte green, byte blue) {
  for (int i = 0; i < NUM_LEDS; i++ ) {
    setPixel(i, red, green, blue);
  }
  showStrip();
}