#include <FastLED.h>
#include "intervals.h" // contains timing start, stop and effect

#define NUMPIX    120
#define DATAPIN   4
#define MAXBRIGHT 255
#define BUTTONPIN 2
CRGB led[NUMPIX];

int eventCount;
byte numEvents = (sizeof(interval) / sizeof(interval[0])) / 3; // SEE INTERVALS.H // events to start
unsigned long startTime;

void setup() {
  FastLED.addLeds<WS2812B, DATAPIN, GRB>(led, NUMPIX);
  FastLED.setBrightness(MAXBRIGHT);
  FastLED.clear();
  FastLED.show();
  Serial.begin(115200);
  randomSeed(analogRead(A0));

  while (!digitalRead(BUTTONPIN));
  delay(random(255)); // not needed
  startTime = millis(); // this is time zero
}

void loop() {
  pixie(); // the effects - follow along in "intervals.h" (remember offset vs realtime)
  // showTiming(); // the timing, no lights
  // readEventStats(); 
}

void pixie() {
  unsigned long thisEvent = millis() - startTime;
  if (thisEvent > 255091UL + startTime ) // One ms after start of last effect
    while (1); // STOP THE SHOW

  if (thisEvent > interval[eventCount]) {
    Serial.print("(C");
    Serial.print(eventCount / 3);
    Serial.print(".E");
    Serial.print(interval[eventCount + 2]);
    Serial.print(")");

    switch (interval[eventCount + 2]) {
      case (0):  fill_color(  0,   0,   0); break; // blk
      case (1):  fill_color(255, 255, 255); break; // wht
      case (2):  rainbow(); break;
      // case (2):  fill_color(random(255), random(255), random(255)); break; // pseudo rainbow
      case (3):  fadetoblack(); break;
      case (4):  fadetowhite(); break;
      case (5):  fill_color(255,   0,   0); break; // red
      case (6):  fill_color(  0,   0, 255); break; // blu
      case (7):  fill_color(160,  32, 240); break; // pur
      case (8):  fill_color(143, 237, 251); break; // cyn
      case (9):  fill_color(255, 102,   0); break; // org
      case (10): fill_color(255,   2, 141); break; // pnk
      case (11): fill_color(255, 245,   0); break; // yel
      case (12): fill_color(  0, 255,   0); break; // grn
      case (13): fade_green_to_white(); break;
      case (14): fade_blue_to_black(); break;
      case (15): fade_red_to_white(); break;
    }
    eventCount += 3;
  }
}

//**************************************************
// EFFECTS
//**************************************************

void fill_color(int red, int grn, int blu) {
  fill_solid(led, NUMPIX, CRGB(red, grn, blu));
  FastLED.show();
}

void rainbow() { // effect 02
  uint8_t thisHue = beatsin16(10, 0, 255);
  fill_rainbow(led, NUMPIX, thisHue, 10);
  FastLED.show();
}

void fadetoblack() { // effect 03
  for (int i = 0; i < 256; i++) {
    for (int j = 0; j < NUMPIX; j++) {
      led[j] = CRGB(255 - i, 255 - i, 255 - i);
    }
    FastLED.show();
  }
}

void fadetowhite() { // effect 04
  for (int i = 0; i < 256; i++) {
    for (int j = 0; j < NUMPIX; j++) {
      led[j] = CRGB(i, i, i);
    }
    FastLED.show();
  }
}

void fade_green_to_white() { // effect 13
  for (int i = 0; i < 256; i++) {
    for (int j = 0; j < NUMPIX; j++) {
      led[j] = CRGB(i, 255, i);
    }
    FastLED.show();
  }
}

void fade_blue_to_black() { // effect 13
  for (int i = 0; i < 256; i++) {
    for (int j = 0; j < NUMPIX; j++) {
      led[j] = CRGB(0, 0, 255 - i);
    }
    FastLED.show();
  }
}

void fade_red_to_white() { // effect 13
  for (int i = 0; i < 256; i++) {
    for (int j = 0; j < NUMPIX; j++) {
      led[j] = CRGB(255, i, i);
    }
    FastLED.show();
  }
}

//**************************************************
// MORE EFFECTS
//**************************************************

void twinkle_fill() {
  for (int i = 0; i < 150; i++) {
    led[random(120)] = CRGB::White;
    FastLED.show();
  }
}

//**************************************************
// UTILITY FUNCTIONS
//**************************************************
void numberOfEvents() { // show number of events
  Serial.print("Number of events: ");
  Serial.println(numEvents);
}

void spacePad(unsigned long value) { // format values on the serial monitor
  for (double i = 100000; i > 1; i /= 10) {
    if (value < i) Serial.print(" ");
  }
}

void readEventStats() { // read and print event start times
  Serial.println(startTime);
  for (int i = 0; i < numEvents; i++) {
    startTime = interval[eventCount];
    Serial.print(i); Serial.print(" "); Serial.println(startTime);
    eventCount += 3;
    delay(250);
  }
}

void showTiming() { // show event timing offset (from zero), realtime and effect
  unsigned long thisEvent = millis() - startTime;
  if (thisEvent > 255091UL ) // one ms after start of last effect
    while (1); // STOP THE SHOW
  if (thisEvent > interval[eventCount]) {
    Serial.print("START (offset ");
    spacePad(interval[eventCount]);
    Serial.print(interval[eventCount]);
    Serial.print(" | realtime ");
    spacePad(interval[eventCount]);
    Serial.print(interval[eventCount] + startTime);
    Serial.print(")");

    Serial.print(" END (offset ");
    spacePad(interval[eventCount + 1]);
    Serial.print(interval[eventCount + 1]);
    Serial.print(" | realtime ");
    spacePad(interval[eventCount + 1]);
    Serial.print(interval[eventCount + 1] + startTime);
    Serial.print(")");

    Serial.print(" STEP ");
    Serial.print(eventCount / 3);
    Serial.print(" EFFECT ");
    Serial.print(interval[eventCount + 2]);

    eventCount += 3;
    Serial.println();
  }
}
START