//JLockwood 2023
//for the childrens bedrooms/hall

//libraries to include
#include <FastLED.h>
//end

//settings that can be changed
#define DATA_PIN    7
#define LED_TYPE    WS2812B
#define COLOR_ORDER GRB
#define NUM_LEDS    575
//#define BRIGHTNESS          125
#define FRAMES_PER_SECOND  120
//end

//declare constants
const int button1Pin = 5; //button to change the color/pattern
const int button2Pin = 6; //button to change the brightness
#define BUTTON 9

//comet code
const int LEDSpeed = 20; //Speed of the LED animation effect. Make sure not to exceed the maxLEDSpeed value.
//end comet code

//end

//variables
int colorState = 1; //variable for reading button 1 status
int BRIGHTNESS = 5;
//int brightState = 0; //variable for reading button 2 status

//comet code
int maxLEDSpeed = 50;    //Identifies the maximum speed of the LED animation sequence
int LEDposition=0;       //Identifies the LED position in the strip that the comet is currently at. The maximum position is NUM_LEDS-1  (eg. 143)
int oldPosition=0;       //Holds the previous position of the comet.

byte hue = 0;            //Stores the Leading LED's hue value (colour)
byte sat = 255;          //Stores the Leading LED's saturation value
byte tailHue = 0;        //Stores the Comet tail hue value
int tailLength = 8;      //determines the length of the tail.
byte hueRange = 20;      //Colour variation of the tail (greater values have greater variation
byte intensity = 200;    //The default brightness of the leading LED
byte tailbrightness= intensity / 2;  //Affects the brightness and length of the tail
int animationDelay = 0;  //The greater the animation delay, the slower the LED sequence. Calculated from LEDSpeed and MaxSpeed.

unsigned long waitTime = 40000;  //The wait time for each comet is currently set for 1 minute (or 60000 milliseconds).
unsigned long endTime;   //The time when we no longer have to wait for the next comet

int cometNumber = 3;     //Used to choose which comet colour to show (***Don't change this variable***)
//end

//end

CRGB leds[NUM_LEDS];

//comet code
byte ledh[NUM_LEDS];
byte ledb[NUM_LEDS];
//end


void setup() {
  delay(2000); // 2 second delay for recovery
  
  //intialize serial
  // Make sure your Serial Terminal app is closed before powering your device
  Serial.begin(9600);
  Serial.println("Starting.....");//print Starting
  //end
  
  //declare buttons
  pinMode(5, INPUT_PULLUP);//button1 using internal pullups
  pinMode(6, INPUT_PULLUP);//button2 using internal pullups
  pinMode(BUTTON, INPUT_PULLUP);                                                      //Connect button to digital pin 9, and use the internal pullup resistor
  Serial.println("Buttons declared.....");
  //end

  // tell FastLED about the LED strip configuration
  FastLED.addLeds<LED_TYPE,DATA_PIN,COLOR_ORDER>(leds, NUM_LEDS).setCorrection(TypicalLEDStrip);
  //FastLED.addLeds<LED_TYPE,DATA_PIN,CLK_PIN,COLOR_ORDER>(leds, NUM_LEDS).setCorrection(TypicalLEDStrip);

  // set master brightness control
  FastLED.setBrightness(BRIGHTNESS);

  //comet code
  selectNextComet();                                                                  //Select the next comet colour
  //end
}


// List of patterns to cycle through.  Each is defined as a separate function below.
typedef void (*SimplePatternList[])();
SimplePatternList gPatterns = { rainbow, rainbowWithGlitter, confetti, sinelon, juggle, bpm };

uint8_t gCurrentPatternNumber = 0; // Index number of which pattern is current
uint8_t gHue = 0; // rotating "base color" used by many of the patterns
  
void loop()
{
  //read the buttons

  if(digitalRead(5) == LOW){      //checks state of button1
    ++colorState;                 //adds one to button colorState (Pattern/Color changer)
    Serial.println(colorState);   //returns state of button 1.
    if(colorState == 15){
      FastLED.clear(true);          //clears FastLED
    }
    delay(200);                   //tenth of a second delay to reduce double button taps
  }

  if(digitalRead(6) == LOW){
    BRIGHTNESS = BRIGHTNESS + 25;         //increases BRIGHTNESS by 25 (MAX 255, limited by code below)
    Serial.println(BRIGHTNESS);
    FastLED.setBrightness(BRIGHTNESS);    //send BRIGHTNESS to FastLED
    delay(200);
  }
  
  if(colorState == 20){           //number of programs -1
    colorState = 1;               //reset color state
  }

  if(BRIGHTNESS >= 140){                   //keeps lights from overdrawing power
    BRIGHTNESS = 5;                        //reset BRIGHTNESS state
  }
  //end

  gPatterns[gCurrentPatternNumber]();


  if(colorState == 1){
   // Call the current pattern function once, updating the 'leds' array
   //gPatterns[gCurrentPatternNumber]();

    // send the 'leds' array out to the actual LED strip
   FastLED.show();  
   // insert a delay to keep the framerate modest
   FastLED.delay(1000/FRAMES_PER_SECOND); 

   // do some periodic updates
    EVERY_N_MILLISECONDS( 20 ) { gHue++; } // slowly cycle the "base color" through the rainbow
   //EVERY_N_SECONDS( 10 ) { nextPattern(); } // change patterns periodically
  }

  if(colorState == 2){
    // Call the current pattern function once, updating the 'leds' array
    gCurrentPatternNumber = 1;
    // send the 'leds' array out to the actual LED strip
    FastLED.show();  
    // insert a delay to keep the framerate modest
    FastLED.delay(1000/FRAMES_PER_SECOND); 

    // do some periodic updates
    EVERY_N_MILLISECONDS( 20 ) { gHue++; } // slowly cycle the "base color" through the rainbow
    //EVERY_N_SECONDS( 10 ) { nextPattern(); } // change patterns periodically
  }

  if(colorState == 3){
    // Call the current pattern function once, updating the 'leds' array
    gCurrentPatternNumber = 2;
    // send the 'leds' array out to the actual LED strip
    FastLED.show();  
    // insert a delay to keep the framerate modest
    FastLED.delay(1000/FRAMES_PER_SECOND); 

    // do some periodic updates
    EVERY_N_MILLISECONDS( 20 ) { gHue++; } // slowly cycle the "base color" through the rainbow
    //EVERY_N_SECONDS( 10 ) { nextPattern(); } // change patterns periodically
  }

  if(colorState == 4){
    // Call the current pattern function once, updating the 'leds' array
    gCurrentPatternNumber = 3;
    // send the 'leds' array out to the actual LED strip
    FastLED.show();  
    // insert a delay to keep the framerate modest
    FastLED.delay(1000/FRAMES_PER_SECOND); 

    // do some periodic updates
    EVERY_N_MILLISECONDS( 20 ) { gHue++; } // slowly cycle the "base color" through the rainbow
    //EVERY_N_SECONDS( 10 ) { nextPattern(); } // change patterns periodically
  }  

  if(colorState == 5){
    // Call the current pattern function once, updating the 'leds' array
    gCurrentPatternNumber = 4;
    // send the 'leds' array out to the actual LED strip
    FastLED.show();  
    // insert a delay to keep the framerate modest
    FastLED.delay(1000/FRAMES_PER_SECOND); 

    // do some periodic updates
    EVERY_N_MILLISECONDS( 20 ) { gHue++; } // slowly cycle the "base color" through the rainbow
    //EVERY_N_SECONDS( 10 ) { nextPattern(); } // change patterns periodically
  } 

  if(colorState == 6){
    // Call the current pattern function once, updating the 'leds' array
    gCurrentPatternNumber = 5;
    // send the 'leds' array out to the actual LED strip
    FastLED.show();  
    // insert a delay to keep the framerate modest
    FastLED.delay(1000/FRAMES_PER_SECOND); 

    // do some periodic updates
    EVERY_N_MILLISECONDS( 20 ) { gHue++; } // slowly cycle the "base color" through the rainbow
    //EVERY_N_SECONDS( 10 ) { nextPattern(); } // change patterns periodically
  }  

  if(colorState == 7){
    fill_solid(leds,NUM_LEDS, CRGB::Red);
    FastLED.show();
  }  
  
  if(colorState == 8){
    fill_solid(leds,NUM_LEDS, CRGB::Orange);
    FastLED.show();
  }

  if(colorState == 9){
    fill_solid(leds,NUM_LEDS, CRGB::Yellow);
    FastLED.show();
  }

  if(colorState == 10){
    fill_solid(leds,NUM_LEDS, CRGB::Green);
    FastLED.show();
  }  
  
  if(colorState == 11){
    fill_solid(leds,NUM_LEDS, CRGB::Blue);
    FastLED.show();
  }

  if(colorState == 12){
    fill_solid(leds,NUM_LEDS, CRGB::Indigo);
    FastLED.show();
  }

  if(colorState == 13){
    fill_solid(leds,NUM_LEDS, CRGB::Violet);
    FastLED.show();
  }  

  if(colorState == 14){
    fill_solid(leds,NUM_LEDS, CRGB::HotPink);
    FastLED.show();
  }  

  if(colorState == 15){
    fill_solid(leds,NUM_LEDS, CRGB::FairyLight);
    FastLED.show();
  }  

  if(colorState == 16){
    showLED(LEDposition, hue, sat, intensity);

    //Adjust the hue of the tail so that it is a different colour from the leading LED. To reduce variation in tail colour, reduce the hueRange.
    if(hue>(254-hueRange)){
      tailHue = random((hue-hueRange),hue);
    } else {
      tailHue = random(hue, (hue+hueRange)); 
    }
    
    tailbrightness = random(50, 100);                      //Randomly select the brightness of the trailing LED (provides sparkling tail)

    
    leds[LEDposition]=CHSV((tailHue),sat,tailbrightness);  //Set the colour, saturation and brightness of the trailing LED
    fadeLEDs(tailLength);                                  //Fade the tail so that the tail brightness dwindles down to nothingness.
    setDelay(LEDSpeed);                                    //

    LEDposition++;
    if(LEDposition>(NUM_LEDS-1)){
      
      for(int i=0; i<50; i++){
        showLED(LEDposition, hue, sat, intensity);
        fadeLEDs(tailLength);
        setDelay(LEDSpeed);
      } 
      LEDposition=0;
      selectNextComet();                                  //Select the next comet colour
      waitForNextComet();                                 //wait for the next comet (either 60 seconds or button press)
    }
  }


}

#define ARRAY_SIZE(A) (sizeof(A) / sizeof((A)[0]))



void nextPattern()
{
  // add one to the current pattern number, and wrap around at the end
  gCurrentPatternNumber = (gCurrentPatternNumber + 1) % ARRAY_SIZE( gPatterns);
}

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

void rainbowWithGlitter() 
{
  // built-in FastLED rainbow, plus some random sparkly glitter
  rainbow();
  addGlitter(80);
}

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

void confetti() 
{
  // random colored speckles that blink in and fade smoothly
  fadeToBlackBy( leds, NUM_LEDS, 10);
  int pos = random16(NUM_LEDS);
  leds[pos] += CHSV( gHue + random8(64), 200, 255);
}

void sinelon()
{
  // a colored dot sweeping back and forth, with fading trails
  fadeToBlackBy( leds, NUM_LEDS, 20);
  int pos = beatsin16( 13, 0, NUM_LEDS-1 );
  leds[pos] += CHSV( gHue, 255, 192);
}

void bpm()
{
  // colored stripes pulsing at a defined Beats-Per-Minute (BPM)
  uint8_t BeatsPerMinute = 62;
  CRGBPalette16 palette = PartyColors_p;
  uint8_t beat = beatsin8( BeatsPerMinute, 64, 255);
  for( int i = 0; i < NUM_LEDS; i++) { //9948
    leds[i] = ColorFromPalette(palette, gHue+(i*2), beat-gHue+(i*10));
  }
}

void juggle() {
  // eight colored dots, weaving in and out of sync with each other
  fadeToBlackBy( leds, NUM_LEDS, 20);
  uint8_t dothue = 0;
  for( int i = 0; i < 8; i++) {
    leds[beatsin16( i+7, 0, NUM_LEDS-1 )] |= CHSV(dothue, 200, 255);
    dothue += 32;
  }
}

//comet code
void showLED(int pos, byte LEDhue, byte LEDsat, byte LEDbright){
  leds[pos] = CHSV(LEDhue,LEDsat,LEDbright);
  FastLED.show();
}


//===================================================================================================================================================
// fadeLEDs(): This function is used to fade the LEDs back to black (OFF) 
//===================================================================================================================================================
void fadeLEDs(int fadeVal){
  for (int i = 0; i<NUM_LEDS; i++){
    leds[i].fadeToBlackBy( fadeVal );
  }
}


//===================================================================================================================================================
// setDelay() : is where the speed of the LED animation sequence is controlled. The speed of the animation is controlled by the LEDSpeed variable.
//              and cannot go faster than the maxLEDSpeed variable.
//===================================================================================================================================================
void setDelay(int LSpeed){
  animationDelay = maxLEDSpeed - abs(LSpeed);
  delay(animationDelay);
}


//===================================================================================================================================================
//selectNextComet() : This is where we select either the Blue, the Pink or the White comet         
//===================================================================================================================================================
void selectNextComet(){
  cometNumber++;
  if(cometNumber>3){
    cometNumber=1;
  }

  switch(cometNumber){
    case 1:  {    //Blue Comet
      hue = 160;
      sat = 255;
      hueRange=20;
      break;
    }
 
    case 2:  {   //Pink Comet
      hue = 224;
      sat = 120;
      hueRange=10;
      break;
    }
    
    default: {   //White Comet
      hue = 0;
      sat = 0;
      hueRange = 0;
      break;
    }  
  }
}


//===================================================================================================================================================
// waitForNextComet() : Is where we either wait for 60 seconds for another comet to come, or initiate another comet with a button press.
//                      The button will only be "ACTIVE" while we are waiting for the next comet. It has no effect while a comet is currently running         
//===================================================================================================================================================
void waitForNextComet(){
    endTime = millis() + waitTime;

    while(millis()<endTime){
      if(digitalRead(BUTTON)==LOW){
         break;
      }
    }
}
//end