//RossRobotics.com

//  5-16-22
//    -Initial Release
//  5-18-22
//    -Added Fire pattern
//  5-22-22
//    -trying to implement a non-blocking meteorRain without success
//    -Tried numerous examples but all use blocking code
//  5-26-22
//    -Had to ask reddit to get me close
//    -Huge thanks to the user https://www.reddit.com/user/christian_suryanto/
//       for the working meteorRain version!
//  5-27-22 (2am!)
//    -Adjusted settings (figured out how to change full animation intervals)
//    -Added color() feature.  Just sets all leds to a color via a pot
//  5-28-22
//    -Added swipe speed feature [swipeSpeed]
//    -Added swipe animation delay(non-blocking) feature.  [animationSpeed]
//  5-29-22
//    -Added selectable swipe color
//    -tweaked a few parameters

#include "FastLED.h"
#include "JC_Button.h"
#define NUM_LEDS 44
CRGB leds[NUM_LEDS];
#define PIN 3

//int swipe = 60;  //depreciated! This value is set by pot  //Speed of full animation.  Higher is Slower 
int animationSpeedInput = A3;  //how fast the swipe travels.
int swipeSpeedInput = A4;
int swipeColorInput = A1;
int buttonPin = 2;
int colorPin = A2; 
Button patternSelect(buttonPin, 25, false, false);  // Declare the butto
int buttonPushCounter = 1;

uint8_t thisdelay = 40;                                       // A delay value for the sequence(s)
uint8_t thishue = 0;                                          // Starting hue value.
int8_t thisrot = 1;                                           // Hue rotation speed. Includes direction.
uint8_t deltahue = 1;                                         // Hue change between pixels.
bool thisdir = 0; 

void setup() {
  //Serial.begin(9600);
  FastLED.addLeds<WS2812B, PIN, GRB>(leds, NUM_LEDS).setDither(0);
   patternSelect.begin();
   pinMode(animationSpeedInput, INPUT);
   pinMode(swipeSpeedInput, INPUT);
   pinMode(swipeColorInput, INPUT);
   pinMode(colorPin, INPUT);
}

void loop() { 

  readbutton();
 switch (buttonPushCounter) {
    case 1:
      buttonPushCounter == 1; {
  meteorRain(0xff,0xff,0xff,3, 64, false, 50);
	   break;
     }
	 
	case 2:
      buttonPushCounter == 2; {
        Fire(35,120,55);
        break;
      }
	  
	case 3:
      buttonPushCounter == 3; {
       color();
       break;
      }
      	case 4:
      buttonPushCounter == 4; {
  ChangeMe();
  EVERY_N_MILLISECONDS(thisdelay) {                           // FastLED based non-blocking delay to update/display the sequence.
    rainbow_march();
  }
  FastLED.show();
       break;
      }
  }
}

void readbutton() {
  patternSelect.read();
  if(patternSelect.wasPressed()) {
	buttonPushCounter++;
	  if (buttonPushCounter == 5 ) {
        buttonPushCounter = 1;
      }else{}
  }
}//end_readbutton

void color() {
  int mappedHue;
 mappedHue = map(analogRead(colorPin), 0, 1023, 0, 255);
  fill_solid( leds, NUM_LEDS, CHSV(mappedHue, 255, 255));
    FastLED.show();
  }
  
void Fire(int Cooling, int Sparking, int SpeedDelay) {
  static byte heat[NUM_LEDS];
  int cooldown;
  // Step 1.  Cool down every cell a little
  for( int i = 0; i < NUM_LEDS; i++) {
    cooldown = random(0, ((Cooling * 10) / NUM_LEDS) + 2);
   
    if(cooldown>heat[i]) {
      heat[i]=0;
    } else {
      heat[i]=heat[i]-cooldown;
    }
  }
 
  // Step 2.  Heat from each cell drifts 'up' and diffuses a little
  for( int k= NUM_LEDS - 1; k >= 2; k--) {
    heat[k] = (heat[k - 1] + heat[k - 2] + heat[k - 2]) / 3;
  }
   
  // Step 3.  Randomly ignite new 'sparks' near the bottom
  if( random(255) < Sparking ) {
    int y = random(7);
    heat[y] = heat[y] + random(160,255);
    //heat[y] = random(160,255);
  }

  // Step 4.  Convert heat to LED colors
  for( int j = 0; j < NUM_LEDS; j++) {
    setPixelHeatColor(j, heat[j] );
  }
  showStrip();
  delay(SpeedDelay);
}

void setPixelHeatColor (int Pixel, byte temperature) {
  // Scale 'heat' down from 0-255 to 0-191
  byte t192 = round((temperature/255.0)*191);
  // calculate ramp up from
  byte heatramp = t192 & 0x3F; // 0..63
  heatramp <<= 2; // scale up to 0..252
 
  // figure out which third of the spectrum we're in:
  if( t192 > 0x80) {                     // hottest
    setPixel(Pixel, 255, 255, heatramp);
  } else if( t192 > 0x40 ) {             // middle
    setPixel(Pixel, 255, heatramp, 0);
  } else {                               // coolest
    setPixel(Pixel, heatramp, 0, 0);
  }
}

byte * Wheel(byte WheelPos) {
  static byte c[3];
 
  if(WheelPos < 85) {
   c[0]=WheelPos * 3;
   c[1]=255 - WheelPos * 3;
   c[2]=0;
  } else if(WheelPos < 170) {
   WheelPos -= 85;
   c[0]=255 - WheelPos * 3;
   c[1]=0;
   c[2]=WheelPos * 3;
  } else {
   WheelPos -= 170;
   c[0]=0;
   c[1]=WheelPos * 3;
   c[2]=255 - WheelPos * 3;
  }
  return c;
}

uint16_t i = 0;

void meteorRain(byte red, byte green, byte blue, byte meteorSize, byte meteorTrailDecay, boolean meteorRandomDecay, int SpeedDelay) 
 {  
int animationSpeed = map(analogRead(animationSpeedInput), 0, 1023, 100, 0); //time between swipes
int swipeColor = map(analogRead(swipeColorInput), 0, 1023, 0, 255);
int swipeSpeed = map(analogRead(swipeSpeedInput), 0, 1023, 50, 10); //speed of swipe crossing
//Serial.println(swipeSpeed);
  
  if (i == 0) setAll(0,0,0);
  EVERY_N_MILLIS_I(timeObj, swipeSpeed){
    timeObj.setPeriod(swipeSpeed);
    // fade brightness all LEDs one step
    for(int j=0; j<NUM_LEDS; j++) {
      if( (!meteorRandomDecay) || (random(10)>5) ) {
        fadeToBlack(j, meteorTrailDecay );        
      }
    }
    // draw meteor
    for(int j = 0; j < meteorSize; j++) {
      if( ( i-j <NUM_LEDS) && (i-j>=0) ) {
        leds[i-j] = CHSV(swipeColor, 255, 255);
      }
    }
    showStrip();
    if (i < NUM_LEDS+animationSpeed) i++; else i = 0;
  }
}

void rainbow_march() {                                        // The fill_rainbow call doesn't support brightness levels. You would need to change the max_bright value.
  if (thisdir == 0) thishue += thisrot; else thishue-= thisrot;  // I could use signed math, but 'thisdir' works with other routines.
  fill_rainbow(leds, NUM_LEDS, thishue, deltahue);
} // rainbow_march()

void ChangeMe() {                                             // A time (rather than loop) based demo sequencer. This gives us full control over the length of each sequence.
  
  uint8_t secondHand = (millis() / 1000) % 90;                // Change '60' to a different value to change length of the loop.
  static uint8_t lastSecond = 99;                             // Static variable, means it's only defined once. This is our 'debounce' variable.

  if (lastSecond != secondHand) {                             // Debounce to make sure we're not repeating an assignment.
    lastSecond = secondHand;
    switch(secondHand) {
     // case  0: thisrot=5; deltahue=10; break;
      case  5: thisdir=-1; deltahue=5; break;
      //case 10: thisrot=5; break;
      //case 15: thisrot=5; thisdir=-1; deltahue=20; break;
     // case 20: deltahue=30; break;
     // case 25: deltahue=2; thisrot=5; break;
     // case 30: break;
    }
  }
  showStrip();
} // ChangeMe()


void fadeToBlack(int ledNo, byte fadeValue) {
 #ifdef ADAFRUIT_NEOPIXEL_H
    uint32_t oldColor;
    uint8_t r, g, b;
    int value;
   
    oldColor = strip.getPixelColor(ledNo);
    r = (oldColor & 0x00ff0000UL) >> 16;
    g = (oldColor & 0x0000ff00UL) >> 8;
    b = (oldColor & 0x000000ffUL);

    r=(r<=10)? 0 : (int) r-(r*fadeValue/256);
    g=(g<=10)? 0 : (int) g-(g*fadeValue/256);
    b=(b<=10)? 0 : (int) b-(b*fadeValue/256);
   
    strip.setPixelColor(ledNo, r,g,b);
 #endif
 #ifndef ADAFRUIT_NEOPIXEL_H
   // FastLED
   leds[ledNo].fadeToBlackBy( fadeValue );
 #endif  
}

void showStrip() {
 #ifdef ADAFRUIT_NEOPIXEL_H
   // NeoPixel
   strip.show();
 #endif
 #ifndef ADAFRUIT_NEOPIXEL_H
   // FastLED
   FastLED.show();
 #endif
}

void setPixel(int Pixel, byte red, byte green, byte blue) {
 #ifdef ADAFRUIT_NEOPIXEL_H
   // NeoPixel
   strip.setPixelColor(Pixel, strip.Color(red, green, blue));
 #endif
 #ifndef ADAFRUIT_NEOPIXEL_H
   // FastLED
   leds[Pixel].r = red;
   leds[Pixel].g = green;
   leds[Pixel].b = blue;
 #endif
}

void setAll(byte red, byte green, byte blue) {
  for(int i = 0; i < NUM_LEDS; i++ ) {
    //setPixel(i, red, green, blue);
    leds[i] = CRGB(red, green, blue);
  }
}