#include "FastLED.h"

// length of the trail
#define STRING_SIZE 12

#define NUM_STRIPS 8
#define NUM_LEDS_PER_STRIP 300
#define NUM_LEDS ((NUM_LEDS_PER_STRIP) * (NUM_STRIPS))

CRGB leds[NUM_LEDS];

// A 1-bit flag for each strip to signal whether it needs to be refreshed
uint8_t dirtystrips = -1;


// write a pixel and set the appropriate strip's dirty bit
void plotLED(int16_t lednum, CRGB col) {
  if (lednum < 0 || lednum >= NUM_LEDS)
    return;
  leds[lednum] = col;
  uint8_t bitset = 1;
  while (lednum >= NUM_LEDS_PER_STRIP)
    bitset <<= 1, lednum -= NUM_LEDS_PER_STRIP;
  dirtystrips |= bitset;
}


// show() only strips modified since the last showDirtyStrips()
void showDirtyStrips() {
  uint8_t strip = 0;
  while (dirtystrips) {
    if (dirtystrips & 1)
      FastLED[strip].showLeds(255);
    dirtystrips >>= 1;
    strip++;
  }
}


void setup() {
  Serial.begin(115200);
  FastLED.addLeds<NEOPIXEL, 8> (leds, 0 * NUM_LEDS_PER_STRIP, NUM_LEDS_PER_STRIP);
  FastLED.addLeds<NEOPIXEL, 27>(leds, 1 * NUM_LEDS_PER_STRIP, NUM_LEDS_PER_STRIP);
  FastLED.addLeds<NEOPIXEL, 16>(leds, 2 * NUM_LEDS_PER_STRIP, NUM_LEDS_PER_STRIP);
  FastLED.addLeds<NEOPIXEL, 17>(leds, 3 * NUM_LEDS_PER_STRIP, NUM_LEDS_PER_STRIP);
  FastLED.addLeds<NEOPIXEL, 25>(leds, 4 * NUM_LEDS_PER_STRIP, NUM_LEDS_PER_STRIP);
  FastLED.addLeds<NEOPIXEL, 26>(leds, 5 * NUM_LEDS_PER_STRIP, NUM_LEDS_PER_STRIP);
  FastLED.addLeds<NEOPIXEL, 12>(leds, 6 * NUM_LEDS_PER_STRIP, NUM_LEDS_PER_STRIP);
  FastLED.addLeds<NEOPIXEL, 13>(leds, 7 * NUM_LEDS_PER_STRIP, NUM_LEDS_PER_STRIP);
}


void loop() {
  static int16_t red_pos;
  
  if (red_pos >= NUM_LEDS + STRING_SIZE)
    red_pos = 0;
  plotLED(red_pos, CRGB::Red);
  plotLED(red_pos - STRING_SIZE, CRGB::Black);
  red_pos++;

  if (1)
    showDirtyStrips();
  else
    FastLED.show();

  // print frames per second
  uint16_t sample_frames = 50;
  static uint16_t frame;
  static uint32_t last_ms;
  if (++frame == sample_frames) {
    frame = 0;
    Serial.print(sample_frames * 1000.0 / (millis() - last_ms));
    Serial.println(" FPS");
    last_ms = millis();
  }
}