#include <Adafruit_NeoPixel.h>
#ifdef __AVR__
  #include <avr/power.h>
#endif
//#define PIN 6
#define PIN 3
// Parameter 1 = number of pixels in strip, Parameter 2 = Arduino pin number (most are valid), Parameter 3 = pixel type flags, add together as needed:
//   NEO_KHZ800  800 KHz bitstream (most NeoPixel products w/WS2812 LEDs)
//   NEO_GRB     Pixels are wired for GRB bitstream (most NeoPixel products)
Adafruit_NeoPixel strip = Adafruit_NeoPixel(16, PIN, NEO_GRB + NEO_KHZ800);
//byte BnessPin = 3, RatePin = 4, ModePin = 3;
byte BnessPin = 2, RatePin = 4, ModePin = 2;
int  bness, rate, pot, numlevels = 20, Mode = 0; // bness used with value read from pot, numvalues set to no. of brightness levels to be used
int  numratelevels = 5;
bool oldState = HIGH;  //set the comparison state for the mode pin
//                    Yellow       Cyan         Green      Magenta,     Red,       Blue,      White          Yellow       Cyan         Green
byte AllColours[] = { 255, 255, 0, 0, 255, 255, 0, 255, 0, 255, 0, 255, 255, 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, 0, 0, 255, 255, 0, 255, 0 };
//****************************
void setup()
{
  // sets digital pin 3 as input to read mode button
  pinMode(ModePin, INPUT);
 attachInterrupt(digitalPinToInterrupt(ModePin), SetMode, FALLING);
  Serial.begin(115200);
  while (!Serial) { ; }
  Serial.println("Running...");
  // init random number generator
  randomSeed(analogRead(0));
  strip.begin();
  // Initialize all pixels to 'off'
  strip.show();
} // End of Setup()
//void(* resetFunc) (void) = 0; //declare reset function @ address 0
//****************************
void loop()
{
  switch (Mode)
    {
   case 0:
      Serial.println("Mode 0 - Off");
      colorWipe(strip.Color(0 , 0 , 0), 1);
//       resetFunc();  //call reset
    break;
    case 1:
      Serial.println("Mode 1 - ZoneOut()");
      ZoneOut();
    break;
    case 2:
      Serial.println("Mode 2 - MyRainbowCycle()");
      MyRainbowCycle(20);
      break;
    case 3:
      Serial.println("Mode 3 - random cycle");
      RandomCycle(200);
      break;
    case 4:
      Serial.println("Mode 4 - serial colour wipes");
      SerialColourWipes();
      break;
    case 5:
      Serial.println("Mode 5 - colour wipe red");
      GetBness();
      colorWipe(strip.Color(255 / bness, 0, 0), 100 * rate);
      GetBness();
      colorWipe(strip.Color(0, 0, 0), 100 * rate);
      break;
    case 6:
      Serial.println("Mode 6 - colour wipe green");
      GetBness();
      colorWipe(strip.Color(0, 255 / bness, 0), 100 * rate);
      GetBness();
      colorWipe(strip.Color(0, 0, 0), 100 * rate);
      break;
    case 7:
      Serial.println("Mode 7 - colour wipe blue");
      GetBness();
      colorWipe(strip.Color(0, 0, 255 / bness), 100 * rate);
      GetBness();
      colorWipe(strip.Color(0, 0, 0), 100 * rate);
      break;
     case 8:
      Serial.println("Mode 8 - rainbowCycle");
      rainbowCycle(1);
      break;
    }
} // End of loop()
//****************************
// Mode 1 - ZoneOut.... cyan to mid blue to mid magenta and loop (nice and relaxing)
void ZoneOut()
{
  uint16_t i, j, wait = 50;
  if (Mode != 1) return;                              // Check to see if user has changed mode. This is done regualrly.
  // set all to black
  for (i = 0; i < strip.numPixels(); i++)             // set each LED ...
    {
    if (Mode != 1) return;
    strip.setPixelColor(i, 0, 0, 0);                   // ... to black
    }
  // this is the start of the loop
  while(1)
    {
    if (Mode != 1) return;
    Serial.println("mid cyan to mid blue");
    GetBness();
    for (j = 0; j < 256; j++)
      {
      GetBness();
      for (i = 0; i < strip.numPixels(); i++)           // set each LED's colour as nec
        {
        if (Mode != 1) return;
        strip.setPixelColor(i, 0, (256 - j) / bness, 256 / bness);
        }
      strip.show();
      delay(3 * rate);
      }
    if (Mode != 1) return;
    Serial.println("mid blue to mid magenta");
    GetBness();
    for (j = 0; j < 256; j++)
      {
      GetBness();
      for (i = 0; i < strip.numPixels(); i++)          // set each LED's colour as nec
        {
        if (Mode != 1) return;
        strip.setPixelColor(i, j / bness, 0, 256 / bness);
        }
      strip.show();
      delay(3 * rate);
      }
    if (Mode != 1) return;
    Serial.println("mid magenta to mid cyan");
    for (j = 0; j < 256; j++)
      {
      GetBness();
      for (i = 0; i < strip.numPixels(); i++)         // set each LED's colour as nec
        {
        if (Mode != 1) return;
        strip.setPixelColor(i, (256 - j) / bness, j / bness, 256 / bness);
        }
      strip.show();
      delay(3 * rate);
      }
    }
  if (Mode != 1) return;
  Serial.println("Slowly fade to black");
  GetBness();
  for (j = 0; j < 256; j++)                           // 256 steps of brightness
    {
    GetBness();
    for (i = 0; i < strip.numPixels(); i++)           // set each LED's colour as necessary
      {
      if (Mode != 1) return;
      strip.setPixelColor(i, (256 - j) / bness, j / bness, 256 / bness);
      }
    }
}   // End of ZoneOut()
//****************************
// Mode 2 - MyRainbowCycle - Yellow, Cyan, Green, Magenta, Red, Blue cycling up the strip and repeating
void MyRainbowCycle(uint8_t wait)
{
  int offset = 0;
  uint16_t i, q;
  while(1)
    {
    if (Mode != 2) return;
    GetBness();
    for (i = 0; i < strip.numPixels(); i++)           // for each LED - counts 0 - 4
      {
      strip.setPixelColor(i, AllColours[offset] / bness, AllColours[offset + 1] / bness, AllColours[offset + 2] / bness);
      offset += 3;
      if (offset > 15)
        {
        offset = 0;
        }
      }
    strip.show();
    delay(50 * rate);
    }
} // End of MyRainbowCycle2()
//****************************
// Mode 3 - RandomCycle
 void RandomCycle(uint8_t wait)
{
  uint16_t i, j;
  if (Mode != 3) return;
  GetBness();
  for (i = 0; i < strip.numPixels(); i++)       // for each LED - counts 0 - 4
    {
    j = random(0, 6);
    strip.setPixelColor(i, AllColours[j] / bness, AllColours[j + 1] / bness, AllColours[j + 2] / bness);
    }
  strip.show();
  delay(wait * rate);
} // End of RandomCycle()
//****************************
// Mode 4 - SerialColourWipes - White Yellow Cyan Green Magenta Red Blue
void SerialColourWipes()
{
  if (Mode != 4) return;
  GetBness();
  colorWipe(strip.Color(255 / bness, 255 / bness, 255 / bness), 1); // white
  delay(500 * rate);
  if (Mode != 4) return;
  GetBness();
  colorWipe(strip.Color(255 / bness, 255 / bness, 0), 1);           // yellow
  delay(500 * rate);
  if (Mode != 4) return;
  GetBness();
  colorWipe(strip.Color(0, 255 / bness, 255 / bness), 1);           // cyan
  delay(500 * rate);
  if (Mode != 4) return;
  GetBness();
  colorWipe(strip.Color(0 , 255 / bness, 0), 1);                    // green
  delay(500 * rate);
  if (Mode != 4) return;
  GetBness();
  colorWipe(strip.Color(255 / bness, 0, 255 / bness), 1);           // magenta
  delay(500 * rate);
  if (Mode != 4) return;
  GetBness();
  colorWipe(strip.Color(255 / bness, 0, 0), 1);                     // red
  delay(500 * rate);
  if (Mode != 4) return;
  GetBness();
  colorWipe(strip.Color(0, 0, 255 / bness), 1);                     // blue
  delay(500 * rate);
} // End of SerialColourWipes()
//****************************
// This func is interrupt driven by the mode switch. It sets a new mode.
void SetMode()
{
  bool newState = digitalRead(ModePin);
  delay(20);
  if (newState == LOW && oldState == HIGH) {
    newState = digitalRead(ModePin);
    if (newState == LOW) {
 // }
  Mode += 1;
  if (Mode > 8)
    {
    Mode = 0;
    }
  mySPL("Mode = ", Mode);
  delay(500);                                                     // give user time to get finger off button
    }
  }
}  // End of SetMode()
//****************************
// Read the brightness pot and convert to value of 1 to numLevels (1 is brightest).
// Read the rate pot and convert to value of 1 to 5 (1 is fastest).
void GetBness()
{
  bness = analogRead(BnessPin) / (1024 / numlevels) + 1; // gives value of 1 to 20, where 1 is bright and 20 is dim
  rate = analogRead(RatePin);
  if (rate > 800) rate = 800;                                     // avoids another level when pot is near high extreme (> 1000)
  rate = rate / 200;                                              // gives value of 1 to 5, where 1 is fast and 10 is slow
  rate += 1;
} // End of GetBness()
 //****************************
// Fill the dots one after the other with a color
void colorWipe(uint32_t c, uint8_t wait)
{
  for (uint16_t i = 0; i < strip.numPixels(); i++)
    {
    strip.setPixelColor(i, c);                                     // i = which pixel, c = colour
    strip.show();
    delay(wait);
    }
} // End of colorWipe()
//****************************
// Slightly different, this makes the rainbow equally distributed throughout
void rainbowCycle(uint8_t wait) {
  uint16_t i, j;
  for(j=0; j<256; j++) { // cycles all colors on wheel
    for(i=0; i< strip.numPixels(); i++) {
      strip.setPixelColor(i, Wheel(((i * 256 / strip.numPixels()) + j) & 255));
    }
    strip.show();
    delay(wait);
  }
} // End of rainbowCycle
// Input a value 0 to 255 to get a color value.
// The colours are a transition r - g - b - back to r.
uint32_t Wheel(byte WheelPos) {
  WheelPos = 255 - WheelPos;
  if(WheelPos < 85) {
    return strip.Color(255 - WheelPos * 3, 0, WheelPos * 3);
  }
  if(WheelPos < 170) {
    WheelPos -= 85;
    return strip.Color(0, WheelPos * 3, 255 - WheelPos * 3);
  }
  WheelPos -= 170;
  return strip.Color(WheelPos * 3, 255 - WheelPos * 3, 0);
}
//****************************
// So sick of multiple statements just to print a var and its value. This does it in 1.
void mySPL(String p1, unsigned long p2)
{
  Serial.print(p1);
  Serial.println(p2);
} // End of mySPL()
/****************************************/