#include <Adafruit_NeoPixel.h>

// --------- Pin Definitions ---------
#define SELECT_BUTTON_PIN 2  // Button to select a color & sparkle
#define EFFECT_BUTTON_PIN 3  // Button to run the pulse effect & trigger solenoid
#define SOLENOID_PIN      4  // Controls the MOSFET for the solenoid
#define NEOPIXEL_PIN      6  // Data pin for the NeoPixel strip

// --------- NeoPixel Settings ---------
#define NUM_PIXELS   180    // Full 3 m strip at 60 LEDs/m
#define TAIL_LENGTH   8     // Number of LEDs lit in the moving pulse
#define SPARK_TIME   2000   // try 2000
#define PULSE_SPEED 20      //try 20 for ~5 seconds
// --------- Color Options ---------
// Define a list of colors (using Adafruit_NeoPixel's Color() function).
// You can add or remove colors as desired.
uint32_t colorOptions[] = {
  Adafruit_NeoPixel::Color(255, 0, 0),    // Red
  Adafruit_NeoPixel::Color(0, 255, 0),    // Green
  Adafruit_NeoPixel::Color(0, 0, 255),    // Blue
  Adafruit_NeoPixel::Color(255, 255, 0),  // Yellow
  Adafruit_NeoPixel::Color(255, 0, 255),  // Magenta
  Adafruit_NeoPixel::Color(0, 255, 255),  // Cyan
  Adafruit_NeoPixel::Color(255, 128, 0)   // Orange
};
const int numColors = sizeof(colorOptions) / sizeof(colorOptions[0]);

// Default selected color is white.
uint32_t selectedColor = Adafruit_NeoPixel::Color(255, 255, 255);

Adafruit_NeoPixel strip(NUM_PIXELS, NEOPIXEL_PIN, NEO_GRB + NEO_KHZ800);

void setup() {
  // Initialize buttons with internal pull-ups
  pinMode(SELECT_BUTTON_PIN, INPUT_PULLUP);
  pinMode(EFFECT_BUTTON_PIN, INPUT_PULLUP);
  
  // Set up the solenoid control pin
  pinMode(SOLENOID_PIN, OUTPUT);
  digitalWrite(SOLENOID_PIN, LOW); // Ensure the solenoid is off
  
  // Initialize the NeoPixel strip
  strip.begin();
  strip.show(); // Turn all pixels off
  
  // Seed the random number generator with noise from an unconnected analog pin.
  randomSeed(analogRead(A0));
}

void loop() {
  // ----- Color Select & Sparkle Effect -----
  // When the select button is pressed, pick a random color and sparkle the strip.
  if (digitalRead(SELECT_BUTTON_PIN) == LOW) {
    delay(50); // debounce
    if (digitalRead(SELECT_BUTTON_PIN) == LOW) { // Confirm button is still pressed
      // Pick a random color from the list.
      int index = random(0, numColors);
      selectedColor = colorOptions[index];
      
      // Sparkle the scepter with the chosen color for 3 seconds.
      sparklingEffect(selectedColor, SPARK_TIME);
      
      // Wait until the button is released.
      while (digitalRead(SELECT_BUTTON_PIN) == LOW) { 
        delay(10);
      }
      delay(100); // extra debounce
    }
  }
  
  // ----- Pulse Effect & Solenoid Activation -----
  // When the effect button is pressed, run the pulse effect (using the preselected color)
  // and then activate the solenoid briefly.
  if (digitalRead(EFFECT_BUTTON_PIN) == LOW) {
    delay(50); // debounce
    if (digitalRead(EFFECT_BUTTON_PIN) == LOW) { // Confirm button is still pressed
      pulseEffect(selectedColor);
      
      // Briefly pulse the solenoid (adjust duration as needed)
      digitalWrite(SOLENOID_PIN, HIGH);
      delay(100);  // 100 ms pulse; change if necessary
      digitalWrite(SOLENOID_PIN, LOW);
      
      // Wait until the button is released.
      while (digitalRead(EFFECT_BUTTON_PIN) == LOW) {
        delay(10);
      }
      delay(100); // extra debounce
    }
  }
}

// -----------------
// sparklingEffect()
// -----------------
// Flashes the scepter by lighting 10 random pixels in the given color.
// The effect runs for the specified duration (in milliseconds).
void sparklingEffect(uint32_t color, unsigned long durationMillis) {
  unsigned long startTime = millis();
  while (millis() - startTime < durationMillis) {
    strip.clear();
    // Light up 10 random pixels.
    for (int i = 0; i < 10; i++) {
      int pix = random(NUM_PIXELS);
      strip.setPixelColor(pix, color);
    }
    strip.show();
    delay(100);  // Cycle speed (adjust for faster or slower sparkles)
  }
  strip.clear();
  strip.show();
}

// -----------------
// pulseEffect()
// -----------------
// Creates a moving “pulse” (with a fading tail) from the bottom to the top of the strip,
// using the specified color. After the pulse, the strip is cleared.
void pulseEffect(uint32_t color) {
  // The pulse will travel from pixel NUM_PIXELS-1 to 0 with a tail of TAIL_LENGTH pixels.
  for (int i = NUM_PIXELS + TAIL_LENGTH - 1; i >= 0; i--) {
    strip.clear();
    // Light the tail (the head is the current i, and the tail fades behind it)
    for (int j = 0; j < TAIL_LENGTH; j++) {
      int pixelIndex = i + j;
      if (pixelIndex >= 0 && pixelIndex < NUM_PIXELS) {
        // Calculate a fading effect: the head is full brightness, fading with each pixel behind.
        uint8_t brightness = 255 - (j * (255 / TAIL_LENGTH));
        // Decompose the selected color into its RGB components.
        uint8_t r = (color >> 16) & 0xFF;
        uint8_t g = (color >> 8) & 0xFF;
        uint8_t b = color & 0xFF;
        // Scale the components by the brightness.
        r = (uint8_t)(((uint16_t)r * brightness) / 255);
        g = (uint8_t)(((uint16_t)g * brightness) / 255);
        b = (uint8_t)(((uint16_t)b * brightness) / 255);
        uint32_t fadeColor = strip.Color(r, g, b);
        strip.setPixelColor(pixelIndex, fadeColor);
      }
    }
    strip.show();
    delay(PULSE_SPEED);  // Adjust the speed of the pulse (smaller = faster)
  }
  strip.clear();
  strip.show();
  //for changing colors
  int index = random(0, numColors);
  selectedColor = colorOptions[index];
}
NOCOMNCVCCGNDINLED1PWRRelay Module