#include <FastLED.h>

#define CHIPSET     WS2812B
#define COLOR_ORDER     GRB                               // Green (G), Red (R), Blue (B)
#define BRIGHTNESS      255
#define VOLTS             5
#define MAX_AMPS        500                               // value in milliamps

#define NUM_LEDS         95
#define DATA_PIN          3

CRGB leds[NUM_LEDS];                                            // main LED array
CRGB test[10];                                                  // set up separate array for merging with main LED array
                                                                // kind of a sprite
int indx = 0;

unsigned long timeInterval  [4] = { 10, 20,  0,  0};            // timers reload with these values
unsigned long timeCounter   [4] = {  0,  0,  0,  0};            // these are the actual timers

boolean       reloadable    [4] = {true, true, false, false};   // this determines if the timer will restart upon reaching end
                                                                // put value in <timeCounter> for oneshot event 
                                                                // 3rd and 4th timers are not used in this example sketch

boolean       tick [4]  = {false, false, false, false};         // countdown timers flags
boolean       mainTick  = false;

unsigned long previousMicros = 0;


void setup() {

  Serial.begin(115200);

  FastLED.addLeds<CHIPSET,DATA_PIN,COLOR_ORDER>(leds, NUM_LEDS);
  FastLED.setMaxPowerInVoltsAndMilliamps(VOLTS, MAX_AMPS);
  FastLED.setBrightness(BRIGHTNESS);
  FastLED.clear();

  memcpy(timeCounter, timeInterval, 4 * sizeof(long));           // preload timers
}

void loop() {

  unsigned long currentMicros = micros();

// -------------------------------------------------------------------

  if (currentMicros - previousMicros >= 1000) {                   // 1 ms tick
    previousMicros = currentMicros;
    
    mainTick = true;                                              // 1 ms tick

    for (int i = 0; i < 4; i++) {                                 // keep track of 4 time intervals

      timeCounter[i]-- ;                                          // decrement counter

      if (timeCounter[i] == 0) {
        if (reloadable[i])  timeCounter[i] = timeInterval[i];     // reached zero, reload counter if it is reloadable
        tick[i] = true;                                           // emit a "tick"
      }
    }
  }

// -------------------------------------------------------------------

  if (mainTick) {   // 1 ms tick
    // fade sections of leds[] array at different rates
    // NOTE: "&leds[0]" and "leds" both point to the beginning of the array
    fadeToBlackBy(&leds[ 0], 30,  3);                     //   long tail
    fadeToBlackBy(&leds[30], 30, 10);                     //    mid tail
    fadeToBlackBy(&leds[60], 30, 20);                     //  short tail
    fadeToBlackBy(&leds[90],  5,  1);                     // looong tail
  }

// -------------------------------------------------------------------

  if (tick[0]) {                                          // 10 ms tick
    tick[0] = false;

    leds[indx] = CRGB::White;                           // add the "running dot" after fades are applied
    indx++;
    if (indx > (NUM_LEDS-1)) indx = 0;
  }

// -------------------------------------------------------------------

  if (tick[1]) {                                          // 20 ms tick
    tick[1] = false;                                      // clear flag so that code runs only once every tick

//  the &test[0] points to the array element directly
//  it allows the choice of a starting point for the fade function

//  fadeToBlackBy( test,    3, 16);                       // these two lines are the same
    fadeToBlackBy(&test[0], 3, 16);                       // fade only first three elements in the array
    fadeToBlackBy(&test[3], 1,  8);                       // fade only the third element in the array

    if(test[0].r < 64){                                   // pixels faded enough
      test[0]= CRGB(248, 11, 130);                      // reload pixels
      test[1]= CRGB(221, 245, 6);
      test[2]= CRGB(112, 16, 236);
      test[3]= CRGB(16, 236, 53);
    }
  }

// -------------------------------------------------------------------

  if (mainTick) {
    mainTick = false;
    memcpy(&leds[10], test,     4 * sizeof(CRGB));        // apply 4 pixels to leds[] array
    memcpy(&leds[50], &test[0], 4 * sizeof(CRGB));        // apply 4 pixels to leds[] array
    memcpy(&leds[80], &test[1], 3 * sizeof(CRGB));        // apply 3 pixels to leds[] array

    FastLED.show();                                       // send pixel data to the LED strip
  }
}