   LIGHT_MODE_ALL_RED,
   LIGHT_MODE_ALL_GREEN,
   LIGHT_MODE_ALL_BLUE,
+  LIGHT_MODE_ALL_PURPLE,
   LIGHT_MODE_SOLID_RAINBOW,
   LIGHT_MODE_CHASE,
-  LIGHT_MODE_FADE,
-  LIGHT_MODE_PULSE,
-  LIGHT_MODE_MEDLEY,
-
-  LIGHT_MODE_END = LIGHT_MODE_MEDLEY
+
+  LIGHT_MODE_END = LIGHT_MODE_CHASE
 };
 
-volatile byte lightMode = LIGHT_MODE_START;
-volatile int lastChange = 0;
-volatile boolean interruptFired = false;
+volatile byte     lightMode       = LIGHT_MODE_START;
+volatile int      lastChange      = 0;
+volatile boolean  buttonPressedInterruptFired  = false;
+int16_t           lastKnobValue   = 0;
+uint16_t          speedValue      = 0;
+uint16_t          brightnessValue = 0;
+
+// What direction is the light going
+int directionModifier = 1;
+uint16_t pixelCounter = 0;
+uint8_t colorIndex = 0;
+
+uint16_t interruptsFired = 0;
 
 // Called when the button is pressed
 void buttonISR() {
   if ((millis() - lastChange) > DEBOUNCE_TIME) {
-    interruptFired = true;
+    buttonPressedInterruptFired = true;
     lastChange = millis();
     if (lightMode == LIGHT_MODE_END) {
       lightMode = LIGHT_MODE_START;
     } else {
       lightMode++;
     }
-    detachInterrupt(digitalPinToInterrupt(BUTTON_SINK));
-  }
+    interruptsFired++;
+    //detachInterrupt(digitalPinToInterrupt(BUTTON_SINK));
+  }
+}
+
+void setup() {
+//  Serial.begin(9600);
+//  Serial.println("Starting light box ...");
+  randomSeed(analogRead(1));
+
+  // Configure the pins
+  pinMode(KNOB_PWR,  OUTPUT);
+  pinMode(KNOB_DIAL, INPUT);
+  digitalWrite(KNOB_PWR, HIGH);
+  pinMode(BUTTON_SINK, INPUT_PULLUP);
+
+  buttonPressedInterruptFired = false;
+  attachInterrupt(digitalPinToInterrupt(BUTTON_SINK), buttonISR, FALLING);
+
+  strip.begin();
+  strip.show(); // Initialize all pixels to 'off'
 }
 
 
 void loop() {
-
-  uint16_t rawValue = analogRead(KNOB_DIAL);
-  byte speedValue = (rawValue / KNOB_CONVERSION) & 255;
-  byte brightnessValue = (((KNOB_MAX - rawValue) / KNOB_CONVERSION) & 255);
-
-  /*Serial.print("Mode: "); Serial.println(lightMode);
-  Serial.print("BUTTON: "); Serial.println(digitalRead(BUTTON_SINK));
-  Serial.print("RAW: "); Serial.println(rawValue);
-  Serial.print("SPEED: "); Serial.println(speedValue);
-  Serial.print("BRIGHTNESS: "); Serial.println(brightnessValue);*/
-//delay(500);
-
-interruptFired = false;
-attachInterrupt(digitalPinToInterrupt(BUTTON_SINK), buttonISR, LOW);
+  boolean knobTurned = didTheKnobGetTurned();
+  boolean buttonPushed = buttonPressedInterruptFired;
+
+  if (buttonPressedInterruptFired) {
+    buttonPressedInterruptFired = false;
+    //attachInterrupt(digitalPinToInterrupt(BUTTON_SINK), buttonISR, LOW);
+  }
+
+  if (knobTurned) {
+    lastKnobValue = getLatestKnobValue();
+    speedValue = (lastKnobValue / KNOB_CONVERSION);
+    brightnessValue = (((KNOB_MAX - lastKnobValue) / KNOB_CONVERSION));
+
+    if (speedValue > 255) { speedValue = 255; }
+    if (speedValue < 1) { speedValue = 1; }
+    if (brightnessValue > 255) { brightnessValue = 255; }
+    if (brightnessValue < 1) { brightnessValue = 1; }
+  }
+
+  /*if (knobTurned || buttonPushed) {
+    Serial.print("Knob: "); Serial.println(knobTurned);
+    Serial.print("Button: "); Serial.println(buttonPushed);
+    Serial.print("Mode Changed to: "); Serial.println(lightMode);
+    Serial.print("Interrupts Fired: "); Serial.println(interruptsFired);
+    //Serial.print("BUTTON: "); Serial.println(digitalRead(BUTTON_SINK));
+    Serial.print("Knob Value: "); Serial.println(lastKnobValue);
+    Serial.print("Speed: "); Serial.println(speedValue);
+    Serial.print("Brightness: "); Serial.println(brightnessValue);
+  }*/
+
   // Check if the button was pressed and adjust the mode accordingly
   switch(lightMode) {
     case LIGHT_MODE_WIPE:
-      repeatWipe(speedValue);
+    //  Serial.println("Wipe Mode Enabled");
+      //repeatWipe(speedValue);
+      colorWipe(speedValue);
       break;
     case LIGHT_MODE_ALL_RED:
+      //Serial.println("All Red Mode Enabled");
       setLightsToSingleColor(brightnessValue, 0x00, 0x00);
       break;
     case LIGHT_MODE_ALL_GREEN:
+      //Serial.println("All Green Mode Enabled");
       setLightsToSingleColor(0x00, brightnessValue, 0x00);
       break;
     case LIGHT_MODE_ALL_BLUE:
+      //Serial.println("All Blue Mode Enabled");
       setLightsToSingleColor(0x00, 0x00, brightnessValue);
       break;
+    case LIGHT_MODE_ALL_PURPLE:
+      //Serial.println("All Purple Mode Enabled");
+      setLightsToSingleColor(brightnessValue, 0x00, brightnessValue);
+      break;
     case LIGHT_MODE_SOLID_RAINBOW:
-      rainbow(speedValue);
-      break;
+      //Serial.println("Rainbow Mode Enabled");
+      rainbow(speedValue, 0xFF);
+      break;
+    default:
     case LIGHT_MODE_CHASE:
+      //Serial.println("Chase Mode Enabled");
       chase(speedValue);
       break;
-    case LIGHT_MODE_FADE:
-      break;
-    case LIGHT_MODE_PULSE:
-      break;
-    default:
-    case LIGHT_MODE_MEDLEY:
-      break;
-  }
-
-  // Some example procedures showing how to display to the pixels:
-
-
-  //rainbow(analogRead(KNOB_DIAL_1) & 255);
-  //rainbowCycle(20);
+  }
+}
+
+boolean shouldInterruptRoutine() {
+  boolean knobTurned = didTheKnobGetTurned();
+  boolean interrupted =  knobTurned || buttonPressedInterruptFired;
+//  if (interrupted) { Serial.print("Interrupt: "); Serial.print(knobTurned); Serial.print(" : "); Serial.println(buttonPressedInterruptFired); }
+  return interrupted;
+}
+
+boolean didTheKnobGetTurned() {
+    boolean wasAChange = false;
+
+    int16_t knobValue = getLatestKnobValue();
+
+    if (abs(lastKnobValue - knobValue) > KNOB_DELTA_THRESHOLD) {
+        wasAChange = true;
+    }
+
+    return wasAChange;
+}
+
+uint16_t getLatestKnobValue() {
+  // Read the dial 3 times and find out what the average is to get rid of any outliers
+  uint16_t rawValue1 = analogRead(KNOB_DIAL);
+  //Serial.print(rawValue1); Serial.print("\t");
+  delay(KNOB_READ_DELAY);
+  uint16_t rawValue2 = analogRead(KNOB_DIAL);
+  //Serial.print(rawValue2); Serial.print("\t");
+  delay(KNOB_READ_DELAY);
+  uint16_t rawValue3 = analogRead(KNOB_DIAL);
+  //Serial.print(rawValue3); Serial.print("\t");
+
+  uint16_t average = (rawValue1 + rawValue2 + rawValue3) / 3;
+  //Serial.println(average);
+
+  return average;
+}
+
+COLOR getNextColor() {
+  COLOR color = colorArray[colorIndex];
+  colorIndex++;
+  if (colorIndex >= NUM_COLORS) { colorIndex = 0; }
+//      Serial.print("Color Index:  "); Serial.println(colorIndex);
+  return color;
 }
 
 void setLightsToSingleColor(byte r, byte g, byte b) {
+
+  uint16_t totalBrightness = (uint16_t)r + (uint16_t)g + (uint16_t)b;
+  uint16_t divisor = ((CURRENT_PER_PIXEL * NUMBER_PIXELS) / MAX_CURRENT);
+  uint16_t maxBrightness = 765;
+
+  if (divisor != 0) {
+    maxBrightness /= divisor;
+  }
+
+  //Serial.print("T: "); Serial.println(totalBrightness);
+  //Serial.print("M: "); Serial.println(maxBrightness);
+  if (totalBrightness > maxBrightness) {
+    uint8_t deltaPerPixel = (uint8_t)(((totalBrightness - maxBrightness) / 3) & 255);
+    //Serial.print("D: "); Serial.println(deltaPerPixel);
+    if (r > deltaPerPixel) { r -= deltaPerPixel; }
+    if (g > deltaPerPixel) { g -= deltaPerPixel; }
+    if (b > deltaPerPixel) { b -= deltaPerPixel; }
+  }
+
   for(uint16_t i = 0; i < strip.numPixels(); i++) {
-    strip.setPixelColor(i, r & MAX_BRIGHTNESS, g & MAX_BRIGHTNESS, b & MAX_BRIGHTNESS);
-
-    if (interruptFired) { return; }
+    strip.setPixelColor(i, r, g, b);
   }
   strip.show();
 }
 
-void repeatWipe(uint8_t wipeSpeed) {
-  colorWipe(strip.Color(MAX_BRIGHTNESS, 0, 0), wipeSpeed); // Red
-  colorWipe(strip.Color(0, MAX_BRIGHTNESS, 0), wipeSpeed); // Green
-  colorWipe(strip.Color(0, 0, MAX_BRIGHTNESS), wipeSpeed); // Blue
-}
+/*void repeatWipe(uint8_t wipeSpeed) {
+  colorWipe(strip.Color(0xFF, 0, 0), wipeSpeed); // Red
+  colorWipe(strip.Color(0, 0xFF, 0), wipeSpeed); // Green
+  colorWipe(strip.Color(0, 0, 0xFF), wipeSpeed); // Blue
+}*/
 
 // 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);
+void colorWipe(uint8_t wait) {
+  COLOR color = getNextColor();
+  for(uint8_t i=0; i<strip.numPixels(); i++) {
+      strip.setPixelColor(i, color.r, color.g, color.b);
       strip.show();
+      if (shouldInterruptRoutine()) { return; }
       delay(wait);
-      if (interruptFired) { return; }
-  }
-}
-
-void rainbow(uint8_t wait) {
-  wait /= 16;
-  Serial.print("Rainbow Wait: "); Serial.println(wait);
-  uint16_t i, j;
-
-  for(j=0; j<256; j++) {
-    for(i=0; i<strip.numPixels(); i++) {
-      strip.setPixelColor(i, Wheel((i+j) & 255, MAX_BRIGHTNESS));
+    //  if (shouldInterruptRoutine()) { return; }
+  }
+}
+
+void rainbow(uint8_t speed, uint8_t brightness) {
+
+
+//  for(j=0; j<256; j++) {
+    COLOR color = getNextColor();
+
+    for(uint16_t i = 0; i < strip.numPixels(); i++) {
+      strip.setPixelColor(i, color.r, color.b, color.g);
     }
     strip.show();
-    delay(wait);
-    if (interruptFired) { return; }
-  }
+    delay(speed * 4);
+    //if (shouldInterruptRoutine()) { return; }
+//  }
 }
 
 void chase(uint8_t speed) {
-  uint32_t colorToWipe = Wheel(random(0, 255), MAX_BRIGHTNESS);
-
-  for(int i=0; i<strip.numPixels(); i++) {
-    strip.setPixelColor(i, Wheel(i, MAX_BRIGHTNESS));
-  }
-  strip.show();
-
-  int directionModifier = 1;
-
-  for(int j=0; j<256; j++) {
-    colorToWipe = Wheel(random(0, 255), MAX_BRIGHTNESS);
-    Serial.print("COLOR: "); Serial.println(colorToWipe);
+  // Initialize the array to all one color
+  //COLOR color = getNextColor();
+/*  for(uint16_t i=0; i<strip.numPixels(); i++) {
+    strip.setPixelColor(i, color.r, color.g, color.b);
+  }
+  strip.show();*/
+
+
+
+
+  // for(uint16_t  = 0; j < 256; j++) {
+  //  colorIndex++;
+    //if (colorIndex >= NUM_COLORS) { colorIndex = 0; }
+
+    COLOR color = getNextColor();
+
     int pixelStart = directionModifier > 0 ? 0 : strip.numPixels() - 1;
-  //  int pixelStop = directionModifier > 0 ? strip.numPixels() : 0;
-
-    Serial.print("MOD: "); Serial.println(directionModifier);
-    Serial.print("pixelStart: "); Serial.println(pixelStart);
+
+  //  Serial.print("MOD: "); Serial.println(directionModifier);
+  //  Serial.print("pixelStart: "); Serial.println(pixelStart);
   //  Serial.print("pixelStop: "); Serial.println(pixelStop);
 
-    for(int i=pixelStart; (i >= 0 && i < strip.numPixels()); i += directionModifier) {
-
-
+    for(uint16_t pixelCounter = pixelStart; (pixelCounter >= 0 && pixelCounter < strip.numPixels()); pixelCounter += directionModifier) {
       for(int k = 0; k < CHASE_WINDOW; k++) {
-        int currentPixel = i + (k * directionModifier);
-        Serial.print("C: "); Serial.println(currentPixel);
-        if (currentPixel < NUMBER_OF_LEDS && currentPixel > 0) {
-          strip.setPixelColor(i + k, MAX_BRIGHTNESS, MAX_BRIGHTNESS, MAX_BRIGHTNESS);
+        int currentPixel = pixelCounter + (k * directionModifier);
+  //      Serial.print("C: "); Serial.println(currentPixel);
+        if (currentPixel < NUMBER_PIXELS && currentPixel >= 0) {
+          strip.setPixelColor(currentPixel, 0x7F, 0x7F, 0x7F);
         }
       }
 
-      int previousPixel = i - directionModifier;
-
-      Serial.print("P: "); Serial.println(previousPixel);
-      if (previousPixel < NUMBER_OF_LEDS && previousPixel > 0) {
-        strip.setPixelColor(previousPixel, colorToWipe);
+      int previousPixel = pixelCounter - directionModifier;
+
+  //    Serial.print("P: "); Serial.println(previousPixel);
+      if (previousPixel < NUMBER_PIXELS && previousPixel >= 0 /*&& i >= CHASE_WINDOW && i <= (NUMBER_PIXELS - CHASE_WINDOW)*/) {
+        strip.setPixelColor(previousPixel, color.r, color.g, color.b);
       }
 
       strip.show();
       delay(speed);
-      if (interruptFired) { return; }
+      if (shouldInterruptRoutine()) { return; }
     }
 
     directionModifier *= -1;
-  }
-}
-
-// Slightly different, this makes the rainbow equally distributed throughout
-/*void rainbowCycle(uint8_t wait) {
-  uint16_t i, j;
-
-  for(j=0; j<256*5; j++) { // 5 cycles of 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);
-  }
-}*/
-
-// 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, byte brightness) {
-  if(WheelPos < 85) {
-   return strip.Color((WheelPos * 3) & brightness, (255 - WheelPos * 3) & brightness, 0);
-  } else if(WheelPos < 170) {
-   WheelPos -= 85;
-   return strip.Color((255 - WheelPos * 3) & brightness, 0, (WheelPos * 3) & brightness);
-  } else {
-   WheelPos -= 170;
-   return strip.Color(0, (WheelPos * 3) & brightness, (255 - WheelPos * 3) & brightness);
-  }
-}
+  //}
+}

//   NEO_KHZ400  400 KHz (classic 'v1' (not v2) FLORA pixels, WS2811 drivers)
//   NEO_GRB     Pixels are wired for GRB bitstream (most NeoPixel products)
//   NEO_RGB     Pixels are wired for RGB bitstream (v1 FLORA pixels, not v2)
Adafruit_NeoPixel strip = Adafruit_NeoPixel(NUMBER_OF_LEDS, PIN, NEO_GRB + NEO_KHZ800);

void setup() {
  Serial.begin(9600);
  Serial.print("Maximum Brightness due to current limitations: "); Serial.println(MAX_BRIGHTNESS);

  randomSeed(analogRead(1));

  pinMode(KNOB_PWR,  OUTPUT);
  pinMode(KNOB_DIAL, INPUT);
  digitalWrite(KNOB_PWR, HIGH);

  pinMode(BUTTON_SINK, INPUT_PULLUP);
  //attachInterrupt(digitalPinToInterrupt(BUTTON_SINK), buttonISR, LOW);

  strip.begin();
  strip.show(); // Initialize all pixels to 'off'
}


enum LIGHT_MODE {
  LIGHT_MODE_START = 0,

#include <Adafruit_NeoPixel.h>

// Pin that is attached to the data line of the NeoPixel Array
#define PIN 12

// Number of Pixels in the Array
#define NUMBER_PIXELS 60
#define MAX_CURRENT 1800
#define CURRENT_PER_PIXEL 60


// Knob Control
#define KNOB_PWR  11
#define KNOB_DIAL A0
#define KNOB_READ_DELAY 10
#define KNOB_DELTA_THRESHOLD 20
#define KNOB_MAX 1024
#define KNOB_MIN 0
static float KNOB_RANGE = KNOB_MAX - KNOB_MIN;
static uint16_t KNOB_CONVERSION = round(KNOB_RANGE / 255);


// Button and ISR Configuration
#define DEBOUNCE_TIME 300
#define BUTTON_SINK   3

// How Big the Chase Mode Window is
#define CHASE_WINDOW 4

struct COLOR {
  byte r;
  byte g;
  byte b;
};

#define RED    {0x7F, 0x00, 0x00}
#define ORANGE {0x7F, 0x52, 0x00}
#define YELLOW {0x79, 0x7A, 0x05}
#define GREEN  {0x00, 0x7F, 0x00}
#define BLUE   {0x00, 0x00, 0x7F}
#define PURPLE {0x7E, 0x00, 0x7E}
#define INDIGO {0x20, 0x05, 0x7A}

#define NUM_COLORS (sizeof(colorArray) / sizeof(COLOR))
COLOR colorArray[] = { RED, ORANGE, YELLOW, GREEN, BLUE, INDIGO, PURPLE };



// Parameter 1 = number of pixels in strip
// Parameter 2 = pin number (most are valid)
