#include <FastLED.h>

//#define TIMING 0

#define LED_PIN     2
#define NUM_LEDS    51
#define LED_TYPE    WS2812
#define COLOR_ORDER GRB

#define MIN_BRIGHTNESS 10                  
#define MAX_BRIGHTNESS 255                
#define RED 0
#define GREEN 96

#define BUTTON_PIN1 3
#define SWITCH_PIN1 4
#define SWITCH_PIN2 5

struct CRGB leds[NUM_LEDS];
const uint8_t led_count[] = {51};

unsigned long previousMillis = 0UL;

// Timing logics
// Cycle period refrence: (1000UL = ~1 second)
unsigned long goofy = 300UL;     //bar graph jitter
unsigned long flash_time = 15UL; //arch flash
unsigned long sticky_interval = 35UL; //overall cycle speed
  unsigned long interval = sticky_interval; 
unsigned long sticky_solid = 55UL; //coolown from solid time
  unsigned long solid = sticky_solid;
unsigned long sticky_cooldown = 1000UL; //off time
  unsigned long cooldown = sticky_cooldown;   
unsigned long reset_time = 5000UL; //overheat time



unsigned long flashem; 

//  millis---->>
//       |..(current-previous)..>..................>>|
//  .....|.....................goofy.300...........|.......
//  .....|...............googy-flash_time.270.|......
//
//
//  millis---->>
//       |..(current-previous)..>..................>>| 
//                                 
//  30...|.interval-flash_time..................................|F!|
//  1000.|.interval................................................| Solid---->
//  1400.|.interval+solid.............................................| Cool--->  ***optional cooldown
//  1700.|.interval+solid+cooldown.....................................................| off---->
//  1700......................................................|i|F!|S!| Cool-------------------->|

int randNumber;
int randomMalf;
int randWand;
long malfunction = 10;

bool test_mode = false;
bool flash = true;
bool cooling = false;
bool overheat = false;
bool windDown = false;
volatile bool blast = false;

long counter = 0;

int buttonState = 0;
int switchState1 = 0;
int switchState2 = 0;
volatile int state = LOW; // To make sure variables shared between an ISR

byte brightness = 255;

void setup() {
  Serial.begin(115200);
  FastLED.addLeds<LED_TYPE, LED_PIN, COLOR_ORDER>(leds, NUM_LEDS); 
  FastLED.clear();
  //Serial.println(FastLED.getBrightness());
/*for(int i; i<50; i++){
    float curve = (i*i*1.);
    float curve2 = map(curve, 0., 5000., 255.0, 10.0);
    Serial.print(curve);
    Serial.print(" to ");
    Serial.println(curve2);
  };
  delay(100000);*/
  //button(s)
  pinMode(BUTTON_PIN1, INPUT_PULLUP); //HIGH when open
  attachInterrupt(digitalPinToInterrupt(BUTTON_PIN1),on,FALLING);//recommended for arduino board
  pinMode(SWITCH_PIN1, INPUT_PULLUP); //HIGH when open
  pinMode(SWITCH_PIN2, INPUT_PULLUP); //HIGH when open
}

void on() { 
  //ISR function
  state = !state; //toggle the state when the interrupt occurs
  blast = true;
  for(int r=0; r < 23; r++){
     leds[r] = CRGB(0, 255, 0);
  }
}

void loop() {
  // Set the brightness.
  FastLED.setBrightness(brightness);
  
  //Serial.println(buttonState);

  randNumber = random(37,51);
  randWand = random(27, 31); 
  //Serial.print("-------------------------> Random: ");
  //Serial.println(randNumber);

  // leds[37] Top of bargraph...
  // leds[50] Bottom of bargraph...

  // Main for loop to control cycle time...
  for (int i = 33; i < 37;) {

    leds[randNumber-1] = CRGB(0, 0, 0);  // Make top light of powerpack bargraph flash
    FastLED.show();
    
    unsigned long currentMillis = millis();
    buttonState = digitalRead(BUTTON_PIN1);
    switchState1 = digitalRead(SWITCH_PIN1);
    switchState2 = digitalRead(SWITCH_PIN2);

/*  //DEBUG
    Serial.print(currentMillis);
    Serial.print(" vs ");
    Serial.print(previousMillis);
    Serial.print(" = ");
    Serial.println(currentMillis - previousMillis);
*/    
    // Run Goofy loop for bargraph(s):
    if(currentMillis - previousMillis > goofy){  
      // Set "Static" Lights
      //leds[23] = CRGB(150,0,0); 
      leds[24] = CRGB::White;
      leds[25] = CRGB(150, 200, 0);
      leds[26] = CRGB(150, 0, 0);
      //leds[32] = CRGB(255, 127, 0);
      // Switch & Brightness Control of overal setup....
      if(switchState1==0){ // up
        if(brightness>5){brightness = brightness-5;};
        FastLED.setBrightness(brightness);
        if(i%2 != 1){leds[32] = CRGB(0, 255, 0);} else {leds[32].nscale8(240);};
      } else {leds[32] = CRGB(255, 127, 0);};
      if(switchState2==0){  // up
        if(brightness<255){brightness = brightness+5;};
        FastLED.setBrightness(brightness);
        if(i%2 != 1){leds[23] = CRGB(255, 255, 0);} else {leds[23].nscale8(240);}; 
      } else {leds[23] = CRGB(150,0,0);};
      // fuel gage logics:
      // leds[37] top of fuel gague
      // leds[51] bottom of fuel gague
      //Serial.print("Random: ");
      //Serial.println(randNumber);
      for (int a = randNumber; a < 51; a++) {
      //for (int a = 51; a > randNumber; a--) {  // This should work????
        //Serial.println(a);
        leds[a] = CRGB(150, 0, 150);
        //leds[random(randNumber,18)] = CRGB(250, 0, 250); 
        FastLED.show();    
      };
      
      //employ the goofy factor...
      //chase "fire" up/down the wand...
      for (int a = random(0,23); a < 23; a++) {
        leds[a] = CHSV(0, 0, 0);
      }
      int q = random(0,200);
      if(q >= 23 && q <= 27 || q==34){leds[q] = CRGB(0,0,0);};
    }

    if(currentMillis - previousMillis > goofy-flash_time){
      leds[randNumber] = CRGB(200, 255, 255); 
      FastLED.show();
      // Make the rod glow a bit, because its cool...
      if(buttonState==0){
        float breath = (exp(sin((currentMillis - previousMillis)/5000.0*PI)) - 0.36787944)*108.0;
        breath = map(breath, 75, 166, 0, 255);
        for(int n = random(-50,3); n >= 0; n--){
          leds[n] = CHSV(breath, 255, 255);
        };
        for(int n = random(20,60); n <= 22; n++){
          leds[n] = CHSV(breath, 255, 255);
        };
      };
    }
    if(currentMillis - previousMillis > goofy){  
      for (int a = randWand; a < 32; a++) {
        leds[a] = CRGB(255, 180, 0);
        //leds[random(randNumber,18)] = CRGB(250, 0, 250); 
        FastLED.show();
      }
    }
    if(currentMillis - previousMillis > goofy-flash_time){
      leds[randWand] = CRGB(255, 127, 0);
      FastLED.show();
    }

    /*if(interval-flash_time < flash_time){
      flashem = flash_time;
      //Serial.println("Overide flash time");
    }
    else{flashem = interval-flash_time;};*/
    flashem = flash_time;

    // Create an "arch flash" before solid color light.
    if(flash == true && currentMillis - previousMillis > flash_time){
      //Serial.println("Flash!");
      //Serial.println(currentMillis - previousMillis);
      //Serial.print("Strobe: ");
      //Serial.println(i);
      leds[i] = CRGB(250, 250, 255);
      FastLED.show(); 
    };

    // turn on a solid color light (hot cyclotron)
    if(cooling == false && currentMillis - previousMillis > interval){
      //Serial.println("Solid...");
      //Serial.println(currentMillis - previousMillis);
      //Serial.print("Color: ");
      //Serial.println(i);
      int n = (random(0-100));
      if(n <= 75){leds[i] = CRGB(158, 229, 255);}
      else if(n > 75 && n <= 95){leds[i] = CRGB(255, 170, 0);} 
      else if(n > 95 && n <= 100){leds[i] = CRGB(0, 225, 0);};  
      leds[randNumber] = CRGB(0, 0, 0);    
      FastLED.show();
      flash = false;
    };  
    
    //after a "solid" period, begin the optional cooldown phase...
    if(overheat == false && currentMillis - previousMillis > interval+solid){
      // Serial.print delay hack...
      Serial.print(counter);
      Serial.println(" Cooling...");
      //
      leds[i] = CRGB::Red;
      //Serial.println((currentMillis - previousMillis)*1.0);
      //Serial.print("cooling: ");Serial.println(breath);
      float a = 235;
      //float b = (254.8+(random(70-80)/1000.));
      //float c = (254.9+(random(20-50)/1000.));
      float b = (251.0+counter/10.0);
      float c = (253.0+counter/10.0);
      float d = 240; 
      if(i==33){
        //leds[1].nscale8(a);
        leds[35].nscale8(b);
        leds[36].nscale8(c);
        leds[i].nscale8(d);
        //Serial.print("0=");
        //Serial.println(currentMillis - previousMillis);     
      }else if(i==34){    
        //leds[2].nscale8(a);
        leds[36].nscale8(b);
        leds[33].nscale8(c);
        leds[i].nscale8(d);
        //Serial.print("1=");
        //Serial.println(currentMillis - previousMillis);
      }else if(i==35){
        //leds[3].nscale8(a);
        leds[33].nscale8(b);
        leds[34].nscale8(c);
        leds[i].nscale8(d);
        //Serial.print("2=");
        //Serial.println(currentMillis - previousMillis);
      }else if(i==36){
        //leds[0].nscale8(a);
        leds[34].nscale8(b);
        leds[36].nscale8(c);
        leds[i].nscale8(d);
        //Serial.print("3=");
        //Serial.println(currentMillis - previousMillis);
      };
      if(buttonState==0){  // Blast button released (off)...
        for(int r=0; r < 23; r++){
          leds[r].nscale8(a);      
        } 
      };
      if(buttonState==1){  // Blast Button Pressed....
        for(int n = 22; n >= 0; n--){
          float breath = (exp(sin((currentMillis - previousMillis)/5000.0*PI)) - 0.36787944)*108.0;
          //Serial.println(breath);
          breath = map(breath, 75, 166, 0, 255);
          leds[n] = CHSV(breath, 255, 255);
        };
      }
      FastLED.show();
      cooling = true;
    };    
    
    // down time (but still warm)
    if(currentMillis - previousMillis > (interval+solid+cooldown)){
      //Serial.println("off...");
      //Serial.println(currentMillis - previousMillis);
      //Serial.print("OFF: ");
      //Serial.println(i);
      if(overheat == false){
        //leds[i] = CRGB(10, 0, 0); 
      };
      if(overheat == true){
        //Serial.println("Overheat active!");
        leds[i] = CHSV(0,255,200); // dim a smidge
        //Serial.println(curve);
        //Serial.println((currentMillis - previousMillis)*1.0);
        //Overheat wand alert:
        leds[23] = CRGB(255, 0, 255);
        leds[32] = CRGB(255, 0, 255);
        if(counter%2 != 1){leds[26] = CRGB(0, 255, 0);} else {leds[26] = CRGB(255, 0, 0);};
        if(counter%2 != 1){leds[25] = CRGB(0, 0, 0);} else {leds[25] = CRGB(150, 200, 0);};
      };

      FastLED.show();
      if(windDown == false && i == 36){counter++;};  //increase malfunction counter 
      //Serial.print("the count is: ");
      //Serial.println(counter);

      // reset logic and step to next LED
      flash = true;
      cooling = false;
      if(windDown == true){
        cooldown = sticky_cooldown/counter;
        if(i == 36){counter--;};
        if(counter <= 6){
          overheat = false;
          //Serial.println("Overheat CLEAR!");
          //Serial.print("loops before malfunction: ");
          //Serial.println(malfunction);
          //Serial.println(interval+solid+cooldown); 
        };
      };

      previousMillis = currentMillis; 
      i++;
    };
  
    // Reset Malfunction logic and generate another randomly...  Spooky!
    if(counter == 1 && windDown == true){
      //Serial.println(counter);
      cooldown = sticky_cooldown;
      windDown = false;
      malfunction = random(3, 50);  //Between 3 and 50 cycles (12 seconds to 4 min)
      if(test_mode==true){malfunction = random(3, 5);};  //testing
      //Serial.print("loops before malfunction: ");
      //Serial.println(malfunction);
    };  

//////////////  Malfunction script
    if(counter >= malfunction && windDown == false){
      if(buttonState == true){
        leds[(random(0,23))] = CRGB(0,0,0);
      };
      // Wand bargraph green...
      for(int n = 27; n < 32; n++){leds[n] = CRGB::LawnGreen;}; 
      //Serial.println("MALFUNCTION!");
      //Serial.println(currentMillis - previousMillis);
      //int g = 0;
      while((currentMillis - previousMillis) < reset_time){
        //g++;
        currentMillis = millis(); 
        //Serial.print(g);
        //Serial.print(" Malfunctioning...");
        //Serial.print(currentMillis - previousMillis);  
        //Serial.print(" VS ");
        //Serial.println(reset_time);
        for (int i = 33; i < 37; i++) {
          float breath = (exp(sin((currentMillis - previousMillis)/5000.0*PI)) - 0.36787944)*108.0;
          float color = (exp(sin((currentMillis - previousMillis)/5000.0*PI)) - 0.36787944)*108.0;
          //float color;
          breath = map(breath, 0, 255, MIN_BRIGHTNESS, MAX_BRIGHTNESS);
          color = map(color, 0, 255, RED, GREEN);
          //Serial.println(color);
          leds[i] = CHSV(color, 255, breath);

          //introduce *some* random sparks into cyclotron:
          randomMalf = random(0,80);
          if(randomMalf <= 37 && randomMalf >= 33){randomMalf=randomMalf; leds[randomMalf] = CRGB(158, 229, 255);}; 
          // Dim the wand and bargraph
          for(int n = 0; n < 23; n++){leds[n].nscale8(230);};
          for(int n = 27; n < 32; n++){leds[n].nscale8(230);};
          FastLED.show();
          //lots of random particles into fuel meter:
          for (int a = 37; a < 51; a++) {
            if(randomMalf > 37 && randomMalf < 51){randomMalf=randomMalf; leds[randomMalf] = CRGB(0,10,0);};
            if(randomMalf >= 0 && randomMalf < 23){randomMalf=randomMalf; leds[randomMalf] = CRGB(255,0,255);};
            if(randomMalf > 27 && randomMalf < 32){randomMalf=randomMalf; leds[randomMalf] = CRGB::Lime;};
            leds[a] = CRGB(255,255,255); 
            FastLED.show();
          };
        }
        cooldown = sticky_cooldown/counter; 
        //interval = sticky_interval/counter;          
        //solid = sticky_solid/counter;
        if(malfunction >= 7){overheat = true;};
        windDown = true;
        counter = malfunction;
        counter++;       
        //previousMillis = currentMillis;
      }
      if(windDown==true){previousMillis = currentMillis;};
    };
  
  }; //For loop

  //blackout bargraphs at the end of the loop
  for (int a = 50; a >= 37; a--) {
      leds[a] = CRGB::Black; 
      FastLED.show();  
    //Serial.println(i);
  };
    for (int a = 31; a >= 27; a--) {
      leds[a] = CRGB::Black; 
      FastLED.show();  
    //Serial.println(i);
  }; 
}