#include <Arduino.h>
#include <FastLED.h>

#define LED_TYPE WS2812B
#define COLOR_ORDER RGB
#define NUM_LEDS 100
#define PIN 32
#define BRIGHTNESS 100
#define FPS 120
#define NUM_PATTERNS 7

CRGB leds[NUM_LEDS];

int posx, posy, dx, dy, current_pattern;

bool panelUpFade = true;

typedef void (*PatternList[])();

uint8_t hue = 0, panelBrightness = 0;
CHSV panelColor;

void confetti() {
  fadeToBlackBy(leds, NUM_LEDS, 10);
  int pos = random16(NUM_LEDS);
  leds[pos] += CHSV(hue + random8(64), 255, 255);
}

void setup_pong() {
  posx = random(0, 9);
  posy = random(0, 10);

  dx = random(0, 1);
  if (dx == 0) {
    dx = -1;
  }
  dy = random(0, 1);
  if (dy == 0) {
    dy = -1;
  } 
}

void pong() {
  int nposx = posx + dx;
  if (nposx < 0 || nposx > 9) {
    dx = dx * -1;
    nposx = posx + dx;
  }
  posx = nposx;

  int nposy = posy + dy;
  if (nposy < 0 || nposy > 10) {
    dy = dy * -1;
    nposy = posy + dy;
  }
  posy = nposy;

  fadeToBlackBy(leds, NUM_LEDS, 10);

  if (posx < 10 && posy < 10) {
    leds[(posy * 10) + posx] += CHSV(hue, 255, 255);
  }
}

void rainbow() 
{
  // FastLED's built-in rainbow generator
  fill_rainbow(leds, NUM_LEDS, hue, 7);
}

void addGlitter( fract8 chanceOfGlitter) 
{
  if( random8() < chanceOfGlitter) {
    leds[ random16(NUM_LEDS) ] += CRGB::White;
  }
}

void raindbowWithGlitter() {
  rainbow();
  addGlitter(80);
}

void fullPanel() {
  if (panelUpFade) {
    if (panelBrightness < 255) {
      panelBrightness = min(255, panelBrightness + (255 / FPS));
    } else {
      panelUpFade = false;
    }
  } else {
    if (panelBrightness > 0) {
      panelBrightness = max(0, panelBrightness - (255 / FPS));
    } else {
      panelUpFade = true;
    }
  }
  fill_solid(leds, NUM_LEDS, CRGB(CHSV(hue, 255, panelBrightness)));
}

void scanLineHorz() {
  fadeToBlackBy(leds, NUM_LEDS, 10);
  for (int i = (posx / 5) % (int)sqrt(NUM_LEDS);
    i < NUM_LEDS;
    i += (int)sqrt(NUM_LEDS)) {
    leds[i] = CRGB(CHSV(hue, 255, 255));
  }
  posx++;
}

void scanLineVert() {
  fadeToBlackBy(leds, NUM_LEDS, 10);
  for (int i = (posx / 5) % (int)sqrt(NUM_LEDS) * (int)sqrt(NUM_LEDS);
    i < ((posx / 5) % (int)sqrt(NUM_LEDS) * (int)sqrt(NUM_LEDS)) +
        (int)sqrt(NUM_LEDS);
    i++) {
    leds[i] = CRGB(CHSV(hue, 255, 255));
  }
  posx++;
}

PatternList patterns = {
  confetti,
  pong,
  rainbow,
  raindbowWithGlitter,
  fullPanel,
  scanLineHorz,
  scanLineVert
};

void nextPattern(){
  current_pattern = (current_pattern + 1) % NUM_PATTERNS;
  panelUpFade = true;
}

void setup() {
  FastLED.addLeds<LED_TYPE, PIN, COLOR_ORDER>(leds, NUM_LEDS).setCorrection(TypicalLEDStrip);

  FastLED.setBrightness(BRIGHTNESS);

  FastLED.showColor(CRGB(0, 0, 0));

  setup_pong();
}

void loop() {
  //confetti();
  //pong();

  patterns[current_pattern]();

  FastLED.show();
  FastLED.delay(1000/FPS * 2);

  EVERY_N_MILLISECONDS(20) {hue++;}
  EVERY_N_SECONDS(10) {
    setup_pong();
    nextPattern();
  }
}
