/*
 * RGB LED Dalek Lamp Software
 *
 * Hareware consists of an Arduino Uno processor,  
 * a 10 LED section of an LPD8806 RGB LED strip and
 * a Radio Shack IR receiver module (276-640).
 *
 * Remote control is made possible using a Roku remote.
 * Other remotes could be used but the CODE_XXXX defines
 * would need to change.
 *
 * Hardware and Software by: Craig A. Lindley
 * Version: 1.0
 * Last Update: 07/10/2012
 */

#include <IRremote.h>
#include <setjmp.h>

// State Machine Defines

// Setup a jmp buffer for holding long jump state
jmp_buf env;

// All state machine states
enum LAMP_STATES {
  STATE_HOME, STATE_WAITING, STATE_EXECUTING, STATE_SHOWING
};

// Current state of lamp state machine
LAMP_STATES lampState;

// The index of the next display pattern (if selected)
int selectorIndex;

// Delay multiplier constants and variables
#define MAX_MULTIPLIER_INDEX +4
#define MIN_MULTIPLIER_INDEX -4

int multiplierIndex;

double delayMultiplier;

// Random pattern timeout
#define PATTERN_DURATION_SECONDS 60
boolean timeOutEnabled;
unsigned long timeOut;

// IR Remote Defines

// Pin IR receiver is connected to
int IR_RECV_PIN = 6;

// Declare instance of IR receiver
IRrecv irReceiver(IR_RECV_PIN);

// Result from decode operation
decode_results results;

// Keys on Roku remote control
#define CODE_NONE   0x00000000
#define CODE_HOME   0x7DF700FF
#define CODE_UP     0x7DF7807F
#define CODE_DOWN   0x7DF7A05F
#define CODE_LEFT   0x7DF740BF
#define CODE_RIGHT  0x7DF720DF
#define CODE_SELECT 0x7DF7C03F
#define CODE_REWIND 0x7DF7609F
#define CODE_PLAY   0x7DF7E01F
#define CODE_FF     0x7DF710EF

// LED Strip Definitions

// Define this value if, like me, you reversed the blue and green LED leads. Markings
// on the LPD8806 flexible PCB board are wrong.
#define BLUE_GREEN_REVERSED

struct RGB {
  uint8_t red;
  uint8_t green;
  uint8_t blue;
};

// Count of LEDs in lamp
#define NUMBER_OF_LEDS   10

// Arduino output pin assignments for driving LPD8806 RGB LED strip
#define LED_CLOCK_PIN    5
#define LED_DATA_PIN     4

// Masks and pointers to speed access
uint8_t clockPinMask, dataPinMask;
volatile uint8_t *clockPort, *dataPort;

// Logical to Physical LED mapping. Necessary to make LED ordering
// what I wanted.
uint8_t LEDMap [] = {
  0,1,2,3,4,5,
  8,7,6,9
};

// Array of LED data in GRB order. Component data is 7 bits wide.
uint32_t LEDData[NUMBER_OF_LEDS];

// Brightness correction data for LEDs.
// Used for dimming LEDs uniformly
// See SCurve.java for how this data was generated
uint8_t GAMMA [] = {
  0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,
  1,2,2,2,2,2,2,3,3,3,4,4,4,5,5,6,
  6,7,7,8,9,9,10,11,12,13,14,16,17,18,20,21,
  23,25,27,29,31,33,36,38,41,43,46,49,52,55,58,61,
  64,66,69,72,75,78,81,84,86,89,91,94,96,98,100,102,
  104,106,107,109,110,111,113,114,115,116,117,118,118,119,120,120,
  121,121,122,122,123,123,123,124,124,124,125,125,125,125,125,125,
  126,126,126,126,126,126,126,126,126,126,126,126,127,127,127,127
};

// Color definitions
#define MIN_LED_VALUE   0 
#define MAX_LED_VALUE 127

uint32_t COLOR_BLACK   = createPackedColor(MIN_LED_VALUE,MIN_LED_VALUE,MIN_LED_VALUE);
uint32_t COLOR_RED     = createPackedColor(MAX_LED_VALUE,MIN_LED_VALUE,MIN_LED_VALUE);
uint32_t COLOR_GREEN   = createPackedColor(MIN_LED_VALUE,MAX_LED_VALUE,MIN_LED_VALUE);
uint32_t COLOR_BLUE    = createPackedColor(MIN_LED_VALUE,MIN_LED_VALUE,MAX_LED_VALUE);
uint32_t COLOR_YELLOW  = createPackedColor(MAX_LED_VALUE,MAX_LED_VALUE,MIN_LED_VALUE);
uint32_t COLOR_MAGENTA = createPackedColor(MAX_LED_VALUE,MIN_LED_VALUE,MAX_LED_VALUE);
uint32_t COLOR_CYAN    = createPackedColor(MIN_LED_VALUE,MAX_LED_VALUE,MAX_LED_VALUE);
uint32_t COLOR_WHITE   = createPackedColor(MAX_LED_VALUE,MAX_LED_VALUE,MAX_LED_VALUE);

// A function pointer for executing display patterns from an array
typedef void (*pt2Function)();

/*******************************************************************/
/***                        IR Functions                         ***/
/*******************************************************************/

// Low level IR code reading function
// Function will return 0 if no IR code available
unsigned long _readIRCode() {

  results.value = 0;

  // Attempt to read an IR code ?
  if (irReceiver.decode(&results)) {

    delay(20);

    // Prepare to receive the next IR code
    irReceiver.resume();
  }
  return results.value;
}

// Read an IR code
// Function will return 0 if no IR code available
unsigned long readIRCode() {

  // Is there an IR code to read ?
  unsigned long code = _readIRCode();
  if (code == 0) {
    // No code so return 0
    return 0;
  }
  // Keep reading until code changes
  while (_readIRCode() == code) {
    ;
  }
  return code;
}

// Check for the home key being pressed or pattern timeout
void checkForInterrupt() {

  boolean timeOutCondition   = (timeOutEnabled && (millis() > timeOut));
  boolean interruptCondition = (readIRCode() == CODE_HOME);

  if (! timeOutCondition && ! interruptCondition) {
    return;
  }
  if (interruptCondition) {
    lampState = STATE_HOME;
  }

  if (timeOutCondition) {
    timeOutEnabled = false;
  }

  // Do the long jump back into top of main loop
  longjmp(env,1);
}

/*******************************************************************/
/***                  LPD 8806 LED strip functions               ***/
/*******************************************************************/

// Convert separate R,G,B into packed 32-bit GRB color
uint32_t createPackedColor(uint8_t r, uint8_t g, uint8_t b) {
  return 0x808080 | ((uint32_t) g << 16) | ((uint32_t) r << 8) | (uint32_t) b;
}

#ifdef BLUE_GREEN_REVERSED

// Blue and Green wiring reversed

// Set LED color from separate 7-bit R, G, B components
void setLEDColor(uint16_t ledNumber, uint8_t r, uint8_t g, uint8_t b) {
  uint32_t data = (b | 0x80);
  data <<= 8;
  data |= (r | 0x80);
  data <<= 8;
  data |= (g | 0x80);
  LEDData[LEDMap[ledNumber]] = data;
}

// Set LED color from packed RGB value
void setLEDColor(uint16_t ledNumber, uint32_t c) {
  uint32_t data = ((c & 0x7F) | 0x80);
  data <<= 8;
  data |= (((c>>8) & 0x7F) | 0x80);
  data <<= 8;
  data |= (((c>>16) & 0x7F) | 0x80);
  LEDData[LEDMap[ledNumber]] = data;
}

#else

// Blue and Green wiring not reversed

// Set LED color from separate 7-bit R, G, B components
void setLEDColor(uint16_t ledNumber, uint8_t r, uint8_t g, uint8_t b) {
  uint32_t data = (g | 0x80);
  data <<= 8;
  data |= (r | 0x80);
  data <<= 8;
  data |= (b | 0x80);
  LEDData[LEDMap[ledNumber]] = data;
}

// Set LED color from packed RGB value
void setLEDColor(uint16_t ledNumber, uint32_t c) {
  uint32_t data = (((c>>16) & 0x7F) | 0x80);
  data <<= 8;
  data |= (((c>>8) & 0x7F) | 0x80);
  data <<= 8;
  data |= ((c & 0x7F) | 0x80);
  LEDData[LEDMap[ledNumber]] = data;
}

#endif

// Set LED color from RGB struct
void setLEDColor(uint16_t ledNumber, struct RGB color) {
  setLEDColor(ledNumber, color.red, color.green, color.blue);  
}

// Set all LEDs to specified color
void setAllLEDsToColor(uint32_t color) {
  for (int i = 0; i < NUMBER_OF_LEDS; i++) {
    setLEDColor(i, color);
  }
  show();
}

// Set all LEDs to black
void setAllLEDsToBlack() {
  setAllLEDsToColor(COLOR_BLACK);
}

// Set all LEDs to blue
void setAllLEDsToBlue() {
  setAllLEDsToColor(COLOR_BLUE);
}

// Set all LEDs to max intensity white
void setAllLEDsToWhite() {
  setAllLEDsToColor(COLOR_WHITE);
}

// Write a byte of LED data to LED strip
void writeByte(byte b) {

  for (uint8_t bit = 0x80; bit; bit >>= 1) {      
    if (b & bit) {
      *dataPort |=  dataPinMask;
    }  
    else {
      *dataPort &= ~dataPinMask;
    }
    *clockPort |=  clockPinMask;
    *clockPort &= ~clockPinMask;
  }
}

// Push LED data to LED strip
void show() {

  // Get the strip's attention
  writeByte(0);

  // Write 24 bits/LED
  for (int i = 0; i < NUMBER_OF_LEDS; i++) {
    uint32_t pixel = LEDData[i];
    writeByte(pixel >> 16 & 0xFF);
    writeByte(pixel >> 8  & 0xFF);
    writeByte(pixel       & 0xFF);
  }
  // Finish up
  writeByte(0);
}

// Color space conversion
// Input arguments
// hue in degrees (0 - 360.0)
// saturation in percent (0.0 - 1.0)
// value in percent (0.0 - 1.0)
// Output arguments
// red, green blue (0.0 - 1.0)
void _HSVtoRGB(double hue, double saturation, double value, double *red, double *green, double *blue) {

  int i;
  double f, p, q, t;
  if (saturation == 0) {
    // achromatic (grey)
    *red = *green = *blue = value;
    return;
  }
  hue /= 60;			// sector 0 to 5
  i = floor(hue);
  f = hue - i;			// factorial part of h
  p = value * (1 - saturation);
  q = value * (1 - saturation * f);
  t = value * (1 - saturation * (1 - f));
  switch(i) {
  case 0:
    *red = value;
    *green = t;
    *blue = p;
    break;
  case 1:
    *red = q;
    *green = value;
    *blue = p;
    break;
  case 2:
    *red = p;
    *green = value;
    *blue = t;
    break;
  case 3:
    *red = p;
    *green = q;
    *blue = value;
    break;
  case 4:
    *red = t;
    *green = p;
    *blue = value;
    break;
  default:
    *red = value;
    *green = p;
    *blue = q;
    break;
  }
}

// Create fully saturated color. Store in RGB structure
void createColor(float hue, float value, struct RGB *color) {
  double r, g, b;

  _HSVtoRGB(hue, 1.0, value, &r, &g, &b);
  (*color).red   = (uint8_t) (r * MAX_LED_VALUE);
  (*color).green = (uint8_t) (g * MAX_LED_VALUE);
  (*color).blue =  (uint8_t) (b * MAX_LED_VALUE);
}

// Create a fully saturated HSV color
void createHSVColor(byte divisions, byte index, RGB *color) {

  float hueAngle = (360.0 * index) / divisions;
  createColor(hueAngle, 1.0, color);
}

/*******************************************************************/
/***            LED Display Pattern Support Functions            ***/
/*******************************************************************/

// Calculate the delay multiplier which will control the speed of
// the display pattern
void calculateDelayMultiplier(int theMultiplierIndex) {
  switch (theMultiplierIndex) {
  case 4:
    delayMultiplier = 16.0;
    break;
  case 3:
    delayMultiplier = 8.0;
    break;
  case 2:
    delayMultiplier = 4.0;
    break;
  case 1:
    delayMultiplier = 2.0;
    break;
  case 0:
    delayMultiplier = 1.0;
    break;
  case -1:
    delayMultiplier = 0.5;
    break;
  case -2:
    delayMultiplier = 0.25;
    break;
  case -3:
    delayMultiplier = 0.125;
    break;
  case -4:
    delayMultiplier = 0.0625;
    break;
  }
}

// Flash the whole lamp the specified number of times
void flashLamp(int times) {

  // Delay to get your finger off the button and your eyes looking at the lamp
  delay(1000);

  for (int i = 0; i < times; i++) {
    setAllLEDsToBlue();
    delay(250);
    setAllLEDsToBlack();
    delay(250);
  }
}

/*******************************************************************/
/***                    LED Display Patterns                     ***/
/*******************************************************************/

// Set all LEDs to black like lamp is off
void lampOffPattern() {

  // Turn off all LEDs
  setAllLEDsToBlack();

  while (true) {
    checkForInterrupt();
    delay(250);
  }
}

// Random colors random LEDs
void randomColorsRandomLEDPattern() {

  int led, r, g, b;

  // Repeat the pattern until interrupted
  while (true) {
    led = random(0, NUMBER_OF_LEDS);
    r = random(0, MAX_LED_VALUE);
    g = random(0, MAX_LED_VALUE);
    b = random(0, MAX_LED_VALUE);
    setLEDColor(led, r, g, b);
    show();
    delay(250 * delayMultiplier);
    checkForInterrupt();
  }
}

// Rotating color palette pattern
void rotatorPattern() {

  RGB color;
  double hue;

  // Hue increment
  double hueInc = 360.0 / NUMBER_OF_LEDS;

  // Repeat the pattern until interrupted
  while (true) {
    // Pattern rotates NUMBER_OF_LEDS times so each color has been in each position
    for (int s = 0; s < NUMBER_OF_LEDS; s++) {
      for (int led = 0; led < NUMBER_OF_LEDS; led++) {
        hue = ((int)((led + s) * hueInc)) % 360;
        createColor(hue, 0.5, &color);
        setLEDColor(led, color);
      }
      show();
      delay(250 * delayMultiplier);
      checkForInterrupt();
    }
  }
}

#define BRIGHTNESS_LEVELS 128
#define FADE_DELAY         20

// Fade in fade out display pattern
void fadeInFadeOutPattern(float hue) {

  RGB color;
  double value;

  while (true) {
    // Gradually increase color's value
    for (int b = 0; b < BRIGHTNESS_LEVELS; b++) {
      value = GAMMA[b] / 127.0;

      for (int led = 0; led < NUMBER_OF_LEDS; led++) {
        createColor(hue, value, &color);
        setLEDColor(led, color);
      }
      show();    
      delay(FADE_DELAY * delayMultiplier);
      checkForInterrupt();
    } 

    // Gradually decrease color's value
    for (int b = BRIGHTNESS_LEVELS - 1 ; b >=0 ; b--) {
      value = GAMMA[b] / 127.0;

      for (int led = 0; led < NUMBER_OF_LEDS; led++) {
        createColor(hue, value, &color);
        setLEDColor(led, color);
      }
      show();    
      delay(FADE_DELAY * delayMultiplier);
      checkForInterrupt();
    } 
  }
} 

// Red fade in fade out pattern
void redFadeInFadeOutPattern() {
  fadeInFadeOutPattern(0);
}

// Green fade in fade out pattern
void greenFadeInFadeOutPattern() {
  fadeInFadeOutPattern(120);
}

// Blue fade in fade out pattern
void blueFadeInFadeOutPattern() {
  fadeInFadeOutPattern(240);
}

// Random color fade in fade out pattern
void randomFadeInFadeOutPattern() {  
  int hue = random(360);
  fadeInFadeOutPattern(hue);
}

// Fade in fade out display pattern
void fadeInFadeOutAltPattern(float hue) {

  RGB color;
  double value1, value2;

  while (true) {
    // Gradually increase and decrease color's value
    for (int b = 0; b < BRIGHTNESS_LEVELS; b++) {
      value1 = GAMMA[b] / 127.0;
      value2 = GAMMA[BRIGHTNESS_LEVELS - 1 - b] / 127.0;

      for (int led = 0; led < 6; led++) {
        createColor(hue, value1, &color);
        setLEDColor(led, color);
      }
      for (int led = 6; led < 10; led++) {
        createColor(hue, value2, &color);
        setLEDColor(led, color);
      }
      show();    
      delay(FADE_DELAY * delayMultiplier);
      checkForInterrupt();
    } 

    // Gradually decrease and increase color's value
    for (int b = BRIGHTNESS_LEVELS - 1 ; b >=0 ; b--) {
      value1 = GAMMA[b] / 127.0;
      value2 = GAMMA[BRIGHTNESS_LEVELS - 1 - b] / 127.0;

      for (int led = 0; led < 6; led++) {
        createColor(hue, value1, &color);
        setLEDColor(led, color);
      }
      for (int led = 6; led < 10; led++) {
        createColor(hue, value2, &color);
        setLEDColor(led, color);
      }
      show();    
      delay(FADE_DELAY * delayMultiplier);
      checkForInterrupt();
    } 
  }
} 

// Red fade in fade out pattern
void redFadeInFadeOutAltPattern() {
  fadeInFadeOutAltPattern(0);
}

// Green fade in fade out pattern
void greenFadeInFadeOutAltPattern() {
  fadeInFadeOutAltPattern(120);
}

// Blue fade in fade out pattern
void blueFadeInFadeOutAltPattern() {
  fadeInFadeOutAltPattern(240);
}

// Random color fade in fade out pattern
void randomFadeInFadeOutAltPattern() {  
  int hue = random(360);
  fadeInFadeOutAltPattern(hue);
}

// Simple sequential pattern in specified color
void sequentialPattern(uint32_t color) {

  // Repeat the pattern until interrupted
  while (true) {
    for (int i = 0; i < NUMBER_OF_LEDS; i++) {
      setLEDColor(i, color);
      show();
      delay(250 * delayMultiplier);
      checkForInterrupt();
      setLEDColor(i, COLOR_BLACK);
      show();
      delay(250 * delayMultiplier);
    }

    for (int i = NUMBER_OF_LEDS - 1; i >= 0; i--) {
      setLEDColor(i, color);
      show();
      delay(250 * delayMultiplier);
      checkForInterrupt();
      setLEDColor(i, COLOR_BLACK);
      show();
      delay(250 * delayMultiplier);
    }
  }
}

void sequentialYellowPattern() {
  sequentialPattern(COLOR_YELLOW);
}

void sequentialCyanPattern() {
  sequentialPattern(COLOR_CYAN);
}

void sequentialMagentaPattern() {
  sequentialPattern(COLOR_MAGENTA);
}

// Rainbow color pattern
void rainbow1Pattern() {

  uint8_t index, colorIndex = 0;
  RGB color;

  const uint8_t numberOfColors = 100;

  while (true) {
    index = colorIndex;
    for (byte led = 0; led < NUMBER_OF_LEDS; led++) {
      createHSVColor(numberOfColors, index, &color);
      setLEDColor(led, color);
      show();
      if (++index > numberOfColors - 1) {
        index = 0;
      }
    }
    checkForInterrupt();
    delay(200 * delayMultiplier);
    if (++colorIndex > numberOfColors - 1) {
      colorIndex = 0;
    }
  }
}

// Rainbow color pattern
void rainbow2Pattern() {

  uint8_t index, colorIndex = 0;
  RGB color;

  const uint8_t numberOfColors = 100;

  // Light the lower LEDs separately
  for (byte led = 6; led < NUMBER_OF_LEDS; led++) {
    setLEDColor(led, COLOR_WHITE);
  }

  while (true) {
    index = colorIndex;
    createHSVColor(numberOfColors, index, &color);
    for (byte led = 0; led < 6; led++) {
      setLEDColor(led, color);
    }
    show();
    if (++index > numberOfColors - 1) {
      index = 0;
    }
    checkForInterrupt();
    delay(200 * delayMultiplier);
    if (++colorIndex > numberOfColors - 1) {
      colorIndex = 0;
    }
  }
}

// Rainbow color pattern
void rainbow3Pattern() {

  uint8_t index, colorIndex = 0;
  RGB color;

  const uint8_t numberOfColors = 64;

  // Light the upper LEDs separately
  for (byte led = 0; led < 6; led++) {
    setLEDColor(led, COLOR_WHITE);
  }

  while (true) {
    index = colorIndex;
    for (byte led = 0; led < 4; led++) {
      createHSVColor(numberOfColors, index, &color);
      setLEDColor(led + 6, color);
      show();
      if (++index > numberOfColors - 1) {
        index = 0;
      }
    }
    checkForInterrupt();
    delay(200 * delayMultiplier);
    if (++colorIndex > numberOfColors - 1) {
      colorIndex = 0;
    }
  }
}

// Lightning like pattern in specified color
void lightningPattern(uint32_t color) {

  int onTime, offTime;

  while (true) {
    // Randomly select LED
    uint8_t led = random(NUMBER_OF_LEDS);
    setLEDColor(led, color);
    show();
    onTime = random(20, 250) * delayMultiplier;
    delay(onTime);

    checkForInterrupt();

    setLEDColor(led, COLOR_BLACK);
    show();
    offTime = random(50, 300) * delayMultiplier;
    delay(offTime);
  } 
}

void redLightningPattern() {
  lightningPattern(COLOR_RED);
}

void greenLightningPattern() {
  lightningPattern(COLOR_GREEN);
}

void blueLightningPattern() {
  lightningPattern(COLOR_BLUE);
}

void whiteLightningPattern() {
  lightningPattern(COLOR_WHITE);
}


void chaserPattern(int hue) {

  struct RGB color1, color2, color3;

  // Calculate colors at the various percentages of brightness
  createColor(hue, 1.00, &color1);
  createColor(hue, 0.15, &color2);
  createColor(hue, 0.05, &color3);

  for (int i = 0; i < 13; i++) {
    switch (i) {
    case 0:
      setLEDColor(i, color1);
      break;
    case 1:
      setLEDColor(i,     color1);
      setLEDColor(i - 1, color2);
      break;
    case 2:
      setLEDColor(i,     color1);
      setLEDColor(i - 1, color2);
      setLEDColor(i - 2, color3);
      break;
    case 3:
    case 4:
    case 5:
    case 6:
    case 7:
    case 8:
    case 9:
      setLEDColor(i,     color1);
      setLEDColor(i - 1, color2);
      setLEDColor(i - 2, color3);
      setLEDColor(i - 3, COLOR_BLACK);
      break;
    case 10:
      setLEDColor(i - 1, color2);
      setLEDColor(i - 2, color3);
      setLEDColor(i - 3, COLOR_BLACK);
      break;
    case 11:
      setLEDColor(i - 2, color3);
      setLEDColor(i - 3, COLOR_BLACK);
      break;
    case 12:
      setLEDColor(i - 3, COLOR_BLACK);
      break;
    }
    show();
    delay(200 * delayMultiplier);
    checkForInterrupt();
  }

  for (int i = 12; i >= 0 ; i--) {
    switch (i) {
    case 12:
      setLEDColor(i - 3, color1);
      break;
    case 11:
      setLEDColor(i - 2, color2);
      setLEDColor(i - 3, color1);
      break;
    case 10:
      setLEDColor(i - 1, color3);
      setLEDColor(i - 2, color2);
      setLEDColor(i - 3, color1);
      break;
    case 9:
    case 8:
    case 7:
    case 6:
    case 5:
    case 4:
    case 3:
      setLEDColor(i,     COLOR_BLACK);
      setLEDColor(i - 1, color3);
      setLEDColor(i - 2, color2);
      setLEDColor(i - 3, color1);
      break;
    case 2:
      setLEDColor(i,     COLOR_BLACK);
      setLEDColor(i - 1, color3);
      setLEDColor(i - 2, color2);
      break;
    case 1:
      setLEDColor(i,     COLOR_BLACK);
      setLEDColor(i - 1, color3);
      break;
    case 0:
      setLEDColor(i,     COLOR_BLACK);
      break;
    }
    show();
    delay(200 * delayMultiplier);
    checkForInterrupt();
  }
}

// Random color chaser pattern
void randomColorChaserPattern() {

  while (true) {
    int hue = random(360);
    chaserPattern(hue);
  }
}

// Array of pointers to pattern display functions
pt2Function patternFunctions [] = {  

  lampOffPattern,
  randomColorsRandomLEDPattern,
  rotatorPattern,
  redFadeInFadeOutPattern,
  greenFadeInFadeOutPattern,
  blueFadeInFadeOutPattern,
  randomFadeInFadeOutPattern,
  redFadeInFadeOutAltPattern,
  greenFadeInFadeOutAltPattern,
  blueFadeInFadeOutAltPattern,
  randomFadeInFadeOutAltPattern,
  sequentialYellowPattern,
  sequentialCyanPattern,
  sequentialMagentaPattern,
  rainbow1Pattern,
  rainbow2Pattern,
  rainbow3Pattern,
  redLightningPattern,
  greenLightningPattern,
  blueLightningPattern,
  whiteLightningPattern,
  randomColorChaserPattern,
};

// Determine the number of display patterns from the entries in the array
#define NUMBER_OF_PATTERNS (sizeof(patternFunctions) / sizeof(pt2Function))

// Allow manual adjustment of color and brightness
void manualAdjustmentColorAndBrightnessDisplay() {

  struct RGB color;
  unsigned long code;

  int hueAngle = 180;
  const int hueIncrement = 5;

  const double valueIncrement = 0.1;
  double value = 0.5;

  LAMP_STATES state = STATE_EXECUTING;

  while (true) {

    switch (state) {
    case STATE_WAITING:
      {
        // Read the remote
        code = readIRCode();

        // Process the code
        switch (code) {

        case CODE_NONE:
        case CODE_SELECT:
        case CODE_REWIND: 
        case CODE_PLAY: 
        case CODE_FF:
          delay(200); 
          break;

        case CODE_HOME:
          // Set next state of main state machine
          lampState = STATE_HOME;
          longjmp(env,1);
          break;

        case CODE_LEFT:
          {
            hueAngle -= hueIncrement;
            if (hueAngle < 0) {
              hueAngle = 0;
            }
            state = STATE_EXECUTING;
          }
          break;

        case CODE_RIGHT:
          {
            hueAngle += hueIncrement;
            if (hueAngle >= 360) {
              hueAngle = 359;
            }
            state = STATE_EXECUTING;
          }
          break;

        case CODE_UP:
          {
            value += valueIncrement;
            if (value > 1.0) {
              value = 1.0;
            }
            state = STATE_EXECUTING;
          }
          break;

        case CODE_DOWN:
          {
            value -= valueIncrement;
            if (value < 0.0) {
              value = 0.0;
            }
            state = STATE_EXECUTING;
          }
          break;
        }
      }
      break;

    case STATE_EXECUTING:
      {
        createColor(hueAngle, value, &color);
        for (int i = 0; i < NUMBER_OF_LEDS; i++) {
          setLEDColor(i, color);
        }
        show();

        // Set next state
        state = STATE_WAITING; 
      }
      break;
    }
  }
}

/*******************************************************************/
/***                        Setup Code                           ***/
/*******************************************************************/

// Do setup before program actually runs 
void setup() {

  // Setup serial interface for debugging
  // Serial.begin(115200);

  // As analog input pin 0 is unconnected, random analog
  // noise will cause the call to randomSeed() to generate
  // different seed numbers each time the sketch runs.
  // randomSeed() will then shuffle the random function.
  randomSeed(analogRead(0));

  // Start the IR receiver
  irReceiver.enableIRIn();

  // Setup LPD8806 clock and data pins as output
  pinMode(LED_CLOCK_PIN, OUTPUT);
  digitalWrite(LED_CLOCK_PIN, LOW);
  pinMode(LED_DATA_PIN, OUTPUT);
  digitalWrite(LED_DATA_PIN, LOW);

  // Establish masks for quick access to LED strip
  clockPort = portOutputRegister(digitalPinToPort(LED_CLOCK_PIN));
  clockPinMask = digitalPinToBitMask(LED_CLOCK_PIN);
  dataPort = portOutputRegister(digitalPinToPort(LED_DATA_PIN));
  dataPinMask = digitalPinToBitMask(LED_DATA_PIN);

  // Turn off all LEDs
  setAllLEDsToBlack();

  // Set initial state for state machine
  lampState = STATE_HOME;

  selectorIndex = 0;

  timeOutEnabled = false;
  timeOut = 0;
}

/*******************************************************************/
/***                     Main Program Loop                       ***/
/*******************************************************************/

// Infinite loop
void loop() {

  // Setup the jmp buffer so control always returns to top of this loop  
  setjmp(env);

  // Loop forever
  while (true) {

    // Examine the state machine state
    switch (lampState) {

    case STATE_HOME:
      {
        setAllLEDsToWhite();
        timeOutEnabled = false;
        timeOut = 0;
        selectorIndex = 0;
        multiplierIndex = 0;
        calculateDelayMultiplier(multiplierIndex);

        // Set next state of state machine
        lampState = STATE_WAITING;
      }
      break;

    case STATE_EXECUTING:
      {
        setAllLEDsToBlack();

        // Start the pattern by its index
        (*patternFunctions[selectorIndex])();
      }
      break;

    case STATE_SHOWING:
      {
        // Turn off all LEDs
        setAllLEDsToBlack();
        delay(300);

        // Randomly select display pattern and speed
        selectorIndex = random(0, NUMBER_OF_PATTERNS);
        multiplierIndex = random(MIN_MULTIPLIER_INDEX, MAX_MULTIPLIER_INDEX + 1);
        calculateDelayMultiplier(multiplierIndex);

        // Calculate future time to switch pattern
        timeOut = millis() + (1000L * PATTERN_DURATION_SECONDS);

        // Enable time outs
        timeOutEnabled = true;

        // Start up the selected pattern
        (*patternFunctions[selectorIndex])();
      }
      break;

    case STATE_WAITING:
      {
        unsigned long code = readIRCode();

        // Process the code
        switch (code) {

        case CODE_NONE:
          break;

        case CODE_HOME:
          break;

        case CODE_LEFT:
          {
            selectorIndex--;
            if (selectorIndex < 0) {
              selectorIndex = NUMBER_OF_PATTERNS - 1;
            }
          }
          break;

        case CODE_RIGHT:
          {
            selectorIndex++;
            if (selectorIndex > NUMBER_OF_PATTERNS - 1) {
              selectorIndex = 0;
            }
          }
          break;

        case CODE_SELECT:
          {
            lampState = STATE_EXECUTING;
          }
          break;

        case CODE_UP:
          {
            multiplierIndex--;
            if (multiplierIndex < MIN_MULTIPLIER_INDEX) {
              multiplierIndex = MIN_MULTIPLIER_INDEX;
            }
            calculateDelayMultiplier(multiplierIndex);
          }
          break;

        case CODE_DOWN:
          {
            multiplierIndex++;
            if (multiplierIndex > MAX_MULTIPLIER_INDEX) {
              multiplierIndex = MAX_MULTIPLIER_INDEX;
            }
            calculateDelayMultiplier(multiplierIndex);
          }
          break;

        case CODE_REWIND: 
          {
            manualAdjustmentColorAndBrightnessDisplay();
          }
          break;

        case CODE_PLAY: 
          {
            lampState = STATE_SHOWING;
          }
          break;

        case CODE_FF: 
          {
            flashLamp(NUMBER_OF_PATTERNS);
            lampState = STATE_HOME;
          }
          break;
        }
        break;
      }
    }
  }
}


































