// https://www.instructables.com/LED-Chase-Effect-Using-an-Arduino/

int potentiometerPin = A7;  // Potentiometer connected to pin A7
int potValue = 0;  // Variable to store potentiometer reading
int minDelay = 20;  // Minimum delay (fastest speed)
int maxDelay = 300;  // Maximum delay (slowest speed)


int leds[] = {2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19};

// https://www.arduino.cc/reference/en/language/variables/utilities/sizeof/
// sizeof(); https://forum.arduino.cc/t/how-do-you-check-the-length-of-an-array/88325/7
int total_leds = sizeof(leds) / sizeof(int); // enter the number of LEDs you want to use here

int ledDelay;
int direction = 1;
int currentLED;
unsigned long changeTime;


int effect = 0;  // Effect selector (0 = Chase, 1 = Bounce, etc.)

unsigned long lastEffectChangeTime = 0;  // To track when to switch effects
unsigned long effectDuration = 6000;    // Effect duration in milliseconds (20 seconds)


void setup() {// put your setup code here, to run once
  Serial.begin(9600);// initialize serial communication at 9600 bits per second:
#ifdef debug
  Serial.println("Debug Enabled");
#else
  Serial.println("Debug Disabled");
#endif

  for (int i = 0; i < total_leds; i++) {
    pinMode(leds[i], OUTPUT); // declare LEDs as output
  }
  // changeTime = millis();
}

//this function for texting only
void loop1() {
  // Read the potentiometer value
  potValue = analogRead(potentiometerPin);

// Map the potentiometer value to an appropriate delay range
  ledDelay = map(potValue, 0, 1023, minDelay, maxDelay);

  if ((millis() - changeTime) > ledDelay) {
    //ledChase(false, false);  // Simple Chase
    //ledChase(true, false);   // Bounce Effect
    //ledChase(true, true);    // Knight Rider Effect
    //simpleChase();
    //bounce();
    //knightRider();
    //twoWayChase3();
    //runningLights();
    //randomBlink();
    //cometTail();
    //waveEffect();
    //stackerEffect();
    fireworks();
    changeTime = millis();
  }
}

void loop() {
  // Read the potentiometer value
  potValue = analogRead(potentiometerPin);

// Map the potentiometer value to an appropriate delay range
  ledDelay = map(potValue, 0, 1023, minDelay, maxDelay);

  // Check if it's time to change the effect
  if ((millis() - lastEffectChangeTime) > effectDuration) {
    effect++;
    if (effect > 10) {  // Assuming 5 effects (0 to 4)
      effect = 0;
    }
    lastEffectChangeTime = millis();  // Reset effect timer
  }

  if ((millis() - changeTime) > ledDelay) {
    changeTime = millis();

    switch(effect) {
      case 0:
        ledChase(false, false);  // Simple Chase
        break;
      case 1:
        ledChase(true, false);   // Bounce Effect
        break;
      case 2:
        ledChase(true, true);    // Knight Rider Effect
        break;
      case 3:
        twoWayChase();           // Middle to sides and back
        break;
      case 4:
        blinkAll();              // All LEDs Blink
        break;
      case 5:
        runningLights();         // Alternating Running Lights
        break;
      case 6:
        randomBlink();           // Random LED Blinking
        break;
      case 7:
        cometTail();             // Comet Tail Effect
        break;
      case 8:
        waveEffect();            // Wave Effect
        break;
      case 9:
        stackerEffect();         // Stacking LEDs
        break;
      case 10:
        fireworks();             // Fireworks Effect
        break;
    }
  }
}



void loop2() {
  // Read the potentiometer value
  potValue = analogRead(potentiometerPin);

// Map the potentiometer value to an appropriate delay range
  ledDelay = map(potValue, 0, 1023, minDelay, maxDelay);

  // Check if it's time to change the effect
  if ((millis() - lastEffectChangeTime) > effectDuration) {
    effect++;
    if (effect > 4) {  // Assuming 5 effects (0 to 4)
      effect = 0;
    }
    lastEffectChangeTime = millis();  // Reset effect timer
  }

  // Execute the current effect
  if ((millis() - changeTime) > ledDelay) {
    changeTime = millis();

    switch (effect) {
      case 0:
        simpleChase();
        break;
      case 1:
        bounce();
        break;
      case 2:
        twoWayChase();
        break;
      case 3:
        blinkAll();
        break;
      case 4:
        knightRider();
        break;
    }
  }
}

void ledChase(bool bounce, bool knightRider) {
  static int direction = 1;  // 1 = forward, -1 = backward
  static int currentLED = 0; // Current LED index

  allLedsOff();  // Turn off all LEDs
  digitalWrite(leds[currentLED], HIGH);  // Light up the current LED

  // Update the current LED based on the direction
  currentLED += direction;

  // Handle bouncing logic
  if (bounce || knightRider) {
    if (currentLED == total_leds || currentLED < 0) {
      direction = -direction;  // Reverse direction
      currentLED += direction; // Adjust the position back into bounds
    }
  } else {
    // For simple chase, reset to the first LED when reaching the end
    if (currentLED == total_leds) {
      currentLED = 0;
    }
  }

  // If it's the Knight Rider effect, allow the LEDs to move inward (similar to bounce)
  if (knightRider && (currentLED == total_leds || currentLED < 0)) {
    direction = -direction;
  }
}


void simpleChase() {
  allLedsOff();
  currentLED += direction;
  if (currentLED == -1) {
    direction = 1;
  }
  else if (currentLED == total_leds) {
    direction = -1;
  }
  digitalWrite(leds[currentLED], HIGH);
}

void bounce() {
  allLedsOff();
  digitalWrite(leds[currentLED], HIGH);
  currentLED += direction;
  if (currentLED == total_leds || currentLED < 0) {
    direction = -direction;  // Reverse direction when hitting the ends
  }
}

void twoWayChase1() {
  allLedsOff();
  for (int i = 0; i < total_leds / 2; i++) {
    digitalWrite(leds[i], HIGH);
    digitalWrite(leds[total_leds - 1 - i], HIGH);
  }
}

void twoWayChase2() {
  allLedsOff();  // Turn all LEDs off
  
  static int leftSide = total_leds / 2 - 1;  // Start from the middle-left LED
  static int rightSide = total_leds / 2;     // Start from the middle-right LED
  
  // Turn on the LEDs at leftSide and rightSide
  digitalWrite(leds[leftSide], HIGH);
  digitalWrite(leds[rightSide], HIGH);
  
  // Move the chase outward
  leftSide--;
  rightSide++;
  
  // Reset when the chase reaches the edges
  if (leftSide < 0 && rightSide >= total_leds) {
    leftSide = total_leds / 2 - 1;
    rightSide = total_leds / 2;
  }
}

void twoWayChase3() {
  allLedsOff();  // Turn all LEDs off
  
  static int leftSide = total_leds / 2 - 1;  // Start from the middle-left LED
  static int rightSide = total_leds / 2;     // Start from the middle-right LED
  static int direction = 1;  // 1 = outward, -1 = inward
  
  // Turn on the LEDs at leftSide and rightSide
  digitalWrite(leds[leftSide], HIGH);
  digitalWrite(leds[rightSide], HIGH);
  
  // Move based on direction
  leftSide -= direction;
  rightSide += direction;
  
  // Check if we reached the edges (outward movement)
  if (leftSide < 0 && rightSide >= total_leds) {
    direction = -1;  // Reverse direction, go inward
    leftSide = 0;    // Start from the edges for inward movement
    rightSide = total_leds - 1;
  }
  
  // Check if we reached the middle (inward movement)
  if (leftSide >= total_leds / 2 - 1 && rightSide <= total_leds / 2) {
    direction = 1;  // Reverse direction, go outward again
    leftSide = total_leds / 2 - 1;  // Reset to the middle for outward movement
    rightSide = total_leds / 2;
  }
}


void twoWayChase() {
  allLedsOff();  // Turn all LEDs off
  
  static int leftSide = 0;  // Start from the first LED on the left
  static int rightSide = total_leds - 1;  // Start from the last LED on the right
  
  // Turn on the LEDs at leftSide and rightSide
  digitalWrite(leds[leftSide], HIGH);
  digitalWrite(leds[rightSide], HIGH);
  
  // Move the chase inward
  leftSide++;
  rightSide--;
  
  // Reset when they cross over in the middle
  if (leftSide > rightSide) {
    leftSide = 0;
    rightSide = total_leds - 1;
  }
}

void blinkAll() {
  static bool state = false;  // Keeps track of whether the LEDs are on or off
  static unsigned long lastBlinkTime = 0;  // Store the last time the LEDs toggled
  int blinkInterval = 500;  // Set a fixed blink interval (500ms = 0.5 seconds)

  // Check if it's time to toggle the LEDs
  if (millis() - lastBlinkTime >= blinkInterval) {
    if (state) {
      allLedsOff();  // Turn all LEDs off
    } else {
      for (int i = 0; i < total_leds; i++) {
        digitalWrite(leds[i], HIGH);  // Turn all LEDs on
      }
    }

    state = !state;  // Toggle the state for the next call
    lastBlinkTime = millis();  // Update the last blink time
  }
}


void blinkAll2() {
  static bool state = false;  // Keeps track of whether the LEDs are on or off

  if (state) {
    // If state is true, turn all LEDs off
    allLedsOff();
  } else {
    // If state is false, turn all LEDs on
    allLedsOn();
    
  }

  state = !state;  // Toggle the state for the next call
}


void blinkAll1() {
  static bool on = false;
   static bool off = false;
  
  if (on) {
  allLedsOn();
  }
  on = !on;
  if (off) {
  allLedsOff();
  }
  off = !off;
}

void knightRider() {
  allLedsOff();
  digitalWrite(leds[currentLED], HIGH);
  currentLED += direction;
  if (currentLED == total_leds || currentLED == -1) {
    direction = -direction;
  }
}

void runningLights() {
  static bool alternate = false;

  allLedsOff();  // Turn all LEDs off
  for (int i = 0; i < total_leds; i += 2) {
    if (alternate) {
      digitalWrite(leds[i], HIGH);  // Light up every other LED
    } else {
      digitalWrite(leds[i + 1], HIGH);  // Invert the pattern
    }
  }
  alternate = !alternate;  // Toggle the pattern
}

void randomBlink() {
  allLedsOff();  // Turn off all LEDs

  int numLedsToLight = random(1, total_leds);  // Choose how many LEDs to light up
  for (int i = 0; i < numLedsToLight; i++) {
    int randomLed = random(0, total_leds);  // Pick a random LED
    digitalWrite(leds[randomLed], HIGH);  // Turn it on
  }
}

void cometTail() {
  static int position = 0;
  int tailLength = 4;  // Number of LEDs in the tail
  float fadeAmount = 0.3;  // Brightness decrease per tail LED

  allLedsOff();  // Turn off all LEDs
  
  // Light up the head of the comet
  digitalWrite(leds[position], HIGH);

  // Create the tail by lighting previous LEDs with decreasing brightness
  for (int i = 1; i < tailLength; i++) {
    int tailPos = position - i;
    if (tailPos >= 0) {
      analogWrite(leds[tailPos], 255 - (i * fadeAmount * 255));
    }
  }

  // Move the position forward
  position++;
  if (position >= total_leds) {
    position = 0;
  }
}

////////////////////////////////////Stacker (LEDs Filling Up)
void stackerEffect() {
  static int position = 0;
  
  if (position < total_leds) {
    digitalWrite(leds[position], HIGH);  // Light up the current LED
    position++;
  } else {
    allLedsOff();  // Reset and turn all LEDs off after it's filled
    position = 0;
  }
}



//////////////////////////////////// Wave (Sinusoidal Motion)
void waveEffect() {
  float speed = 0.2;  // Adjust this to change wave speed
  float amplitude = 127;  // Wave amplitude
  int center = 128;  // Center value for the wave

  for (int i = 0; i < total_leds; i++) {
    int brightness = int(sin(i * speed + millis() / 100.0) * amplitude + center);
    analogWrite(leds[i], brightness);  // Write brightness to each LED
  }
}

///////////////////////////////////Fireworks (Exploding LEDs)
void fireworks() {
  allLedsOff();  // Turn off all LEDs
  
  // Choose a random "explosion" center
  int centerLed = random(0, total_leds);

  // Light the center LED
  digitalWrite(leds[centerLed], HIGH);
  
  // Light up adjacent LEDs
  if (centerLed - 1 >= 0) digitalWrite(leds[centerLed - 1], HIGH);
  if (centerLed + 1 < total_leds) digitalWrite(leds[centerLed + 1], HIGH);
  
  delay(100);  // Short delay for explosion effect
  allLedsOff();  // Clear the LEDs for next explosion
}


//////////////////////////////////////////////////////////////////////////////////////////////

void allLedsOff() {
  for (int x = 0; x < total_leds; x++) {
    digitalWrite(leds[x], LOW);
  }
}

void allLedsOn() {
  for (int x = 0; x < total_leds; x++) {
    digitalWrite(leds[x], HIGH);
  }
}