#include <FastLED.h>
#include <SPI.h>
#include <Wire.h>

// LED strip configuration
#define NUM_LEDS 96
#define LED_PIN1 29
#define LED_PIN2 28

CRGB leds1[NUM_LEDS];
CRGB leds2[NUM_LEDS];
CRGB cpuled[1];

const int buttonUp = 2;
const int buttonDown = 3;

uint8_t gHue = 64;
int currentEffect = 0;
const int numEffects = 16;

void setup() {
  Serial.begin(115200);
  Serial.println("DiscoSlots");
  FastLED.addLeds<WS2813, LED_PIN1, GRB>(leds1, NUM_LEDS);
  FastLED.addLeds<WS2813, LED_PIN2, GRB>(leds2, NUM_LEDS);
  FastLED.addLeds<WS2812, 16, GRB>(cpuled, 1);
  FastLED.setBrightness(255);

  pinMode(buttonUp, INPUT_PULLUP);
  pinMode(buttonDown, INPUT_PULLUP);

  cpuled[0]=CRGB::Red;
  FastLED.show();
}

void loop() {
  void (*effects[])() = {
    rainbow, bpm, bouncingBalls, sinelonSmooth, colorWipe,
    fire, meteorRain, pacifica, noiseSmooth, dotsSmooth,
    cylon, colorWave, wave, fireIce, colorWaves, colorPulse
  };
  const char* effectNames[] = {
    "Rainbow", "BPM", "Bouncing Balls", "Sinelon Smooth", "Color Wipe",
    "Fire", "Meteor Rain", "Pacifica", "Noise Smooth", "Dots Smooth",
    "Cylon", "Color Wave", "Wave", "Fire Ice", "Color Waves", "Color Pulse"
  };

  static unsigned long lastButtonPress = 0;
  if (millis() - lastButtonPress > 200) {
    if (digitalRead(buttonUp) == LOW) {
      lastButtonPress = millis();
      currentEffect = (currentEffect + 1) % numEffects;
      Serial.println(effectNames[currentEffect]);
    }
    if (digitalRead(buttonDown) == LOW) {
      lastButtonPress = millis();
      currentEffect = (currentEffect - 1 + numEffects) % numEffects;
      Serial.println(effectNames[currentEffect]);
    }
    if (digitalRead(buttonUp) == LOW && digitalRead(buttonDown) == LOW) {
      lastButtonPress = millis();
      FastLED.clear();
      FastLED.show();
    }
  }

  effects[currentEffect]();
  FastLED.show();
  FastLED.delay(20);
}

// Define each effect function

void rainbow() {
  fill_rainbow(leds1, NUM_LEDS, gHue, 7);
  fill_rainbow(leds2, NUM_LEDS, gHue + 128, 7); // Opposite color
  gHue++;
}

void sinelonSmooth() {
  static int pos = 0;
  fadeToBlackBy(leds1, NUM_LEDS, 20);
  fadeToBlackBy(leds2, NUM_LEDS, 20);
  pos = beatsin16(13, 0, NUM_LEDS - 1);
  leds1[pos] += CHSV(gHue, 255, 192);
  leds2[NUM_LEDS - pos - 1] += CHSV(gHue + 128, 255, 192);
  gHue++;
}

void bpm() {
  uint8_t BeatsPerMinute = 62;
  CRGBPalette16 palette = PartyColors_p;
  uint8_t beat = beatsin8(BeatsPerMinute, 64, 255);
  for (int i = 0; i < NUM_LEDS; i++) {
    leds1[i] = ColorFromPalette(palette, gHue + (i * 2), beat - gHue + (i * 10));
    leds2[NUM_LEDS-i] = ColorFromPalette(palette, gHue + 128 + (i * 2), beat - gHue + 128 + (i * 10));
  }
  gHue++;
}

void colorWave() {
  static uint16_t startIndex = 0;
  startIndex += 5;
  fill_palette(leds1, NUM_LEDS, startIndex, 16, PartyColors_p, 255, LINEARBLEND);
  fill_palette(leds2, NUM_LEDS, startIndex + 128, 16, PartyColors_p, 255, LINEARBLEND);
}

void wave() {
  static uint8_t sineOffset = 0;
  sineOffset++;
  for (int i = 0; i < NUM_LEDS; i++) {
    leds1[i] = CHSV((sin8(i * 8 + sineOffset) - 128) / 2 + 128, 255, 255);
    leds2[i] = CHSV((sin8(i * 8 + sineOffset + 128) - 128) / 2 + 128, 255, 255);
  }
}

void colorPulse() {
  uint8_t beat = beatsin8(30, 0, 255);
  fill_solid(leds1, NUM_LEDS, CHSV(gHue, 255, beat));
  fill_solid(leds2, NUM_LEDS, CHSV(gHue + 128, 255, beat));
  gHue++;
}

void bouncingBalls() {
  static float Gravity = -9.81;
  static int StartHeight = 1;
  static float ImpactVelocityStart = sqrt(-2 * Gravity * StartHeight);
  static float Height[NUM_LEDS];
  static float ImpactVelocity[NUM_LEDS];
  static float TimeSinceLastBounce[NUM_LEDS];
  static int Position[NUM_LEDS];
  static long ClockTimeSinceLastBounce[NUM_LEDS];
  static float Dampening[NUM_LEDS];

  for (int i = 0; i < NUM_LEDS; i++) {
    ClockTimeSinceLastBounce[i] = millis();
    Height[i] = StartHeight;
    Position[i] = 0;
    ImpactVelocity[i] = ImpactVelocityStart;
    TimeSinceLastBounce[i] = 0;
    Dampening[i] = 0.90 - float(i) / pow(NUM_LEDS, 2);
  }

  for (int i = 0; i < NUM_LEDS; i++) {
    TimeSinceLastBounce[i] = millis() - ClockTimeSinceLastBounce[i];
    Height[i] = 0.5 * Gravity * pow(TimeSinceLastBounce[i] / 1000, 2.0) + ImpactVelocity[i] * TimeSinceLastBounce[i] / 1000;

    if (Height[i] < 0) {
      Height[i] = 0;
      ImpactVelocity[i] = Dampening[i] * ImpactVelocity[i];
      ClockTimeSinceLastBounce[i] = millis();

      if (ImpactVelocity[i] < 0.01) {
        ImpactVelocity[i] = ImpactVelocityStart;
      }
    }
    Position[i] = round(Height[i] * (NUM_LEDS - 1) / StartHeight);
  }

  for (int i = 0; i < NUM_LEDS; i++) {
    leds1[Position[i]] = CHSV(gHue + 64 * i / NUM_LEDS, 200, 255);
    leds2[NUM_LEDS - Position[i] - 1] = CHSV(gHue + 128 + 64 * i / NUM_LEDS, 200, 255);
  }
  gHue++;
}

void colorWipe() {
  static int pos = 0;
  fill_solid(leds1, pos, CHSV(gHue, 255, 255));
  fill_solid(leds2, pos, CHSV(gHue + 128, 255, 255));
  pos++;
  if (pos >= NUM_LEDS) pos = 0;
  gHue++;
}

void fire() {
  static byte heat1[NUM_LEDS];
  static byte heat2[NUM_LEDS];
  for (int i = 0; i < NUM_LEDS; i++) {
    heat1[i] = qsub8(heat1[i], random8(0, ((55 * 10) / NUM_LEDS) + 2));
    heat2[i] = qsub8(heat1[i], random8(0, ((55 * 10) / NUM_LEDS) + 2));
  }
  for (int k = NUM_LEDS - 1; k >= 2; k--) {
    heat1[k] = (heat1[k - 1] + heat1[k - 2] + heat1[k - 2]) / 3;
    heat2[k] = (heat2[k - 1] + heat2[k - 2] + heat2[k - 2]) / 3;
  }
  if (random8() < 120) {
    int y1 = random8(7);
    int y2 = random8(7);
    heat1[y1] = qadd8(heat1[y1], random8(160, 255));
    heat1[y2] = qadd8(heat1[y2], random8(160, 255));
  }
  for (int j = 0; j < NUM_LEDS; j++) {
    leds1[j] = HeatColor(heat1[j]);
    leds2[j] = HeatColor(heat2[j]);
  }
  gHue++;
}

void fireIce() {
  static byte heat[NUM_LEDS];
  for (int i = 0; i < NUM_LEDS; i++) {
    heat[i] = qsub8(heat[i], random8(0, ((55 * 10) / NUM_LEDS) + 2));
  }
  for (int k = NUM_LEDS - 1; k >= 2; k--) {
    heat[k] = (heat[k - 1] + heat[k - 2] + heat[k - 2]) / 3;
  }
  if (random8() < 120) {
    int y = random8(7);
    heat[y] = qadd8(heat[y], random8(160, 255));
  }
  for (int j = 0; j < NUM_LEDS; j++) {
    leds1[j] = HeatColor(heat[j]);
    leds2[j] = CHSV((240 - (heat[j] / 3)), 255, heat[j]); // Cool color effect
  }
  gHue++;
}

void meteorRain() {
  static byte meteorSize = 10;
  static byte meteorTrailDecay = 64;
  static boolean meteorRandomDecay = true;
  static int pos = 0;

  for (int j = 0; j < NUM_LEDS; j++) {
    if ((!meteorRandomDecay) || (random8() > 128)) {
      fadeToBlackBy(leds1, NUM_LEDS, meteorTrailDecay);
      fadeToBlackBy(leds2, NUM_LEDS, meteorTrailDecay);
    }
  }

  for (int j = 0; j < meteorSize; j++) {
    if ((pos - j < NUM_LEDS) && (pos - j >= 0)) {
      leds1[pos - j] = CHSV(gHue, 255, 255);
      leds2[pos - j] = CHSV(gHue + 128, 255, 255);
    }
  }

  pos++;
  if (pos >= NUM_LEDS) pos = 0;
  gHue++;
}

void pacifica() {
  static const CRGBPalette16 pacifica_palette_1 = CRGBPalette16(CRGB::Blue, CRGB::DeepSkyBlue, CRGB::Cyan, CRGB::Aquamarine);
  static const CRGBPalette16 pacifica_palette_2 = CRGBPalette16(CRGB::Aqua, CRGB::Aquamarine, CRGB::Azure, CRGB::DeepSkyBlue);
  static const CRGBPalette16 pacifica_palette_3 = CRGBPalette16(CRGB::Teal, CRGB::DarkTurquoise, CRGB::DeepSkyBlue, CRGB::Blue);
  static const CRGBPalette16 pacifica_palette_4 = CRGBPalette16(CRGB::DarkCyan, CRGB::MediumBlue, CRGB::Blue, CRGB::DarkBlue);

  uint16_t sCIStart1 = beatsin16(6, 0, 65535);
  uint16_t sCIStart2 = beatsin16(7, 0, 65535);
  uint16_t sCIStart3 = beatsin16(8, 0, 65535);
  uint16_t sCIStart4 = beatsin16(9, 0, 65535);

  for (uint16_t i = 0; i < NUM_LEDS; i++) {
    uint16_t sCI1 = sCIStart1 + i * (65536 / NUM_LEDS);
    uint16_t sCI2 = sCIStart2 + i * (65536 / NUM_LEDS);
    uint16_t sCI3 = sCIStart3 + i * (65536 / NUM_LEDS);
    uint16_t sCI4 = sCIStart4 + i * (65536 / NUM_LEDS);

    leds1[i] = ColorFromPalette(pacifica_palette_1, sCI1 >> 8, 255, LINEARBLEND);
    leds1[i] += ColorFromPalette(pacifica_palette_2, sCI2 >> 8, 255, LINEARBLEND);
    leds1[i] += ColorFromPalette(pacifica_palette_3, sCI3 >> 8, 255, LINEARBLEND);
    leds1[i] += ColorFromPalette(pacifica_palette_4, sCI4 >> 8, 255, LINEARBLEND);

    leds2[i] = ColorFromPalette(pacifica_palette_1, sCI1 >> 8, 255, LINEARBLEND);
    leds2[i] += ColorFromPalette(pacifica_palette_2, sCI2 >> 8, 255, LINEARBLEND);
    leds2[i] += ColorFromPalette(pacifica_palette_3, sCI3 >> 8, 255, LINEARBLEND);
    leds2[i] += ColorFromPalette(pacifica_palette_4, sCI4 >> 8, 255, LINEARBLEND);
  }
  gHue++;
}

void noiseSmooth() {
  static uint16_t x = random16();
  static uint16_t y = random16();
  static uint16_t z = random16();
  static uint16_t scale = 30;

  for (uint16_t i = 0; i < NUM_LEDS; i++) {
    uint8_t noise = inoise8(x + i * scale, y, z);
    leds1[i] = CHSV(noise, 255, 255);
    leds2[i] = CHSV(noise + 128, 255, 255);
  }
  x++;
  y++;
  z++;
}

void dotsSmooth() {
  static uint8_t startIndex = 0;
  startIndex++;
  fill_rainbow(leds1, NUM_LEDS, startIndex, 16);
  fill_rainbow(leds2, NUM_LEDS, startIndex + 128, 16);

  for (int i = 0; i < NUM_LEDS; i++) {
    if (random8() < 50) {
      leds1[i] = CRGB::White;
      leds2[i] = CRGB::White;
    }
  }
  gHue++;
}

void cylon() {
  static uint8_t hue = 0;
  for (int i = 0; i < NUM_LEDS; i++) {
    leds1[i] = CHSV(hue++, 255, 255);
    leds2[i] = CHSV(hue + 128, 255, 255);
    FastLED.show();
    fadeToBlackBy(leds1, NUM_LEDS, 20);
    fadeToBlackBy(leds2, NUM_LEDS, 20);
    delay(10);
  }
}

void colorWaves() {
  uint8_t startIndex = gHue;
  fill_rainbow(leds1, NUM_LEDS, startIndex, 5);
  fill_rainbow(leds2, NUM_LEDS, startIndex + 128, 5);

  uint8_t wave1 = beatsin8(9, 0, NUM_LEDS - 1);
  uint8_t wave2 = beatsin8(7, 0, NUM_LEDS - 1);
  uint8_t wave3 = beatsin8(13, 0, NUM_LEDS - 1);

  leds1[wave1] = CRGB::White;
  leds2[wave2] = CRGB::White;
  leds1[wave3] = CRGB::White;
  leds2[wave3] = CRGB::White;

  gHue++;
}
BOOTSELLED1239USBRaspberryPiPico©2020RP2-8020/21P64M15.00TTT