/**
 * @author Noah Geeler
 * @date 30.05.2024
*/

#include <Adafruit_NeoPixel.h>

const int LED_PIN = 9;     // GPIO an dem die NeoPixel angeschlossen sind.
const int LED_AMOUNT = 30; // Anzahl Neopixel die am GPIO angeschlossen sind.
const int SPEED = 25;
const int LED_OFFSET = 15; // RGB offset

int r[LED_AMOUNT];
int g[LED_AMOUNT];
int b[LED_AMOUNT];
// CYCLES
// 0 - G++
// 1 - R--
// 2 - B++
// 3 - G--
// 4 - R++
// 5 - B--
int cycle[LED_AMOUNT];

// NeoPixel-Objet erzeugen:
Adafruit_NeoPixel pixels = Adafruit_NeoPixel(LED_AMOUNT, LED_PIN, NEO_GRB + NEO_KHZ800);

//---------------------------------------------------------------------
void setup()
{
  Serial.begin(9600); // For debugging
  pixels.begin();

  // Set all pixels to red
  // This is the initial setup for all the pixels, because the loop starts with red
  for (int i = 0; i < LED_AMOUNT; i++)
  {
    // Setup pixels initially
    cycle[i] = 0;
    r[i] = 255;
    g[i] = 0;
    b[i] = 0;
    // Update pixels with offset
    calculatePixels(i, LED_OFFSET, 0);
  }
}

//---------------------------------------------------------------------
void loop()
{
  // Create a fancy RGB effect ✨
  for (int i = 0; i < LED_AMOUNT; i++)
  {
    // Set the color of each pixel
    pixels.setPixelColor(i, pixels.Color(r[i], g[i], b[i]));
    calculatePixels(i, 0, 0);
    pixels.show(); // Update the pixels
    // delay(10); // Maybe add a delay here
  }
}

// This calculates the pixels color according to the cycle
// The overflow is used to calculate the next color in the next
// cycle, if it exceeds the limit (255 or below 0)
void calculatePixels(int i, int offset, int overflow)
{
  if (cycle[i] == 0)
  {
    g[i] += SPEED + offset * i + overflow;
    if (!(g[i] >= 255))
      return;
    overflow = g[i] - 255;
    g[i] = 255;
    cycle[i]++;
    if (overflow > 0)
    {
      calculatePixels(i, 0, overflow);
    }
  }
  else if (cycle[i] == 1)
  {
    r[i] -= SPEED + offset * i + overflow;
    if (!(r[i] <= 0))
      return;
    overflow = r[i] * -1;
    r[i] = 0;
    cycle[i]++;
    if (overflow > 0)
    {
      calculatePixels(i, 0, overflow);
    }
  }
  else if (cycle[i] == 2)
  {
    b[i] += SPEED + offset * i + overflow;
    if (!(b[i] >= 255))
      return;
    overflow = b[i] - 255;
    b[i] = 255;
    cycle[i]++;
    if (overflow > 0)
    {
      calculatePixels(i, 0, overflow);
    }
  }
  else if (cycle[i] == 3)
  {
    g[i] -= SPEED + offset * i + overflow;
    if (!(g[i] <= 0))
      return;
    overflow = g[i] * -1;
    g[i] = 0;
    cycle[i]++;
    if (overflow > 0)
    {
      calculatePixels(i, 0, overflow);
    }
  }
  else if (cycle[i] == 4)
  {
    r[i] += SPEED + offset * i + overflow;
    if (!(r[i] >= 255))
      return;
    overflow = r[i] - 255;
    r[i] = 255;
    cycle[i]++;
    if (overflow > 0)
    {
      calculatePixels(i, 0, overflow);
    }
  }
  else if (cycle[i] >= 5)
  {
    b[i] -= SPEED + offset * i + overflow;
    if (!(b[i] <= 0))
      return;
    overflow = b[i] * -1;
    b[i] = 0;
    cycle[i] = 0;
    if (overflow > 0)
    {
      calculatePixels(i, 0, overflow);
    }
  }
}