
#define ABS(a)          ((a) > 0 ? (a) : -(a))

#define RED_COLOR       0
#define BLUE_COLOR      1
#define WHITE_COLOR     2

// Pins...
#define NO_PIN          -1
#define LED_PIN_BLUE    9
#define LED_PIN_RED     10
#define LED_PIN_WHITE   11

// Every 10 seconds, fade to white...
#define WHITE_PERIOD    10000

enum {
  MODE_USER = 0,
  MODE_SLOW,
  MODE_FAST,
  MODE_COUNT
};

enum {
  // Solid color states.
  STATE_R = 0,
  STATE_B,
  STATE_W,
  STATE_Bk,
  // Transition states
  STATE_RB,
  STATE_BR,
  STATE_BkW,
  STATE_WBk,
  STATE_RBk,
  STATE_BkR,
  STATE_COUNT
};

int state;
int usrMode;
int usrLED;



/////////////////////////////////////////////
// State actions

void fadeUpAndDown(int fadeUpPin, int fadeDownPin) {
  int i = 0;  
  for (i = 0; i < 256; ++i) {
    if (fadeUpPin >= 0)   analogWrite(fadeUpPin, i);
    if (fadeDownPin >= 0) analogWrite(fadeDownPin, 255 - i);
    delay(2);
  }
}


// Transition states.

void doStateRB ()         { fadeUpAndDown(LED_PIN_BLUE, LED_PIN_RED); state = STATE_B; }
void doStateBR ()         { fadeUpAndDown(LED_PIN_RED, LED_PIN_BLUE); state = STATE_R; }
void doStateBkR ()        { fadeUpAndDown(LED_PIN_RED, NO_PIN);       state = STATE_R; }
void doStateRBk ()        { fadeUpAndDown(NO_PIN, LED_PIN_RED);       state = STATE_BkW; }
void doStateBkW ()        { fadeUpAndDown(LED_PIN_WHITE, NO_PIN);     state = STATE_WBk; }
void doStateWBk ()        { fadeUpAndDown(NO_PIN, LED_PIN_WHITE);     state = STATE_BkR; }


// Solid states.

void doStateBlack() {
  analogWrite(LED_PIN_WHITE, 0);
  analogWrite(LED_PIN_BLUE,  0);
  analogWrite(LED_PIN_RED,   0);
  state = STATE_BkW; 
}


void doStateWhite () {
  analogWrite(LED_PIN_WHITE, 0);
  state = STATE_WBk;
}


void doStateR () {
  analogWrite(LED_PIN_BLUE, 0);
  analogWrite(LED_PIN_RED,  255);
  analogWrite(LED_PIN_WHITE, 0);
  state = STATE_R;
}


void doStateB () {
  analogWrite(LED_PIN_BLUE, 255);
  analogWrite(LED_PIN_RED,  0);
  analogWrite(LED_PIN_WHITE, 0);
  state = STATE_B;
}



/////////////////////////////////////////////
// Setup and loop....

void setup () {
  state   = STATE_R;
  usrMode = MODE_SLOW;

  pinMode( LED_PIN_WHITE,   OUTPUT);
  pinMode( LED_PIN_BLUE,    OUTPUT);
  pinMode( LED_PIN_RED,     OUTPUT);

  Serial.begin(9600);
}


void loop () {
  static long interval;
  static long previousMillis = 0;
  static long previousWhiteMillis = 0;

  if ((millis () - previousMillis) < interval) {
    return;
  }

  Serial.println ("Looping...");

  switch (usrMode) {
  case MODE_USER:
    if (usrLED == BLUE_COLOR) state = STATE_B;
    if (usrLED == RED_COLOR)  state = STATE_R;
    break;

  case MODE_SLOW:
    if ((state == STATE_R) && (ABS(millis() - previousWhiteMillis) > WHITE_PERIOD)) {
      state = STATE_RBk;
      previousWhiteMillis = millis();
    } else {
      if (state == STATE_R) state = STATE_RB;
      if (state == STATE_B) state = STATE_BR;
    }
    break;

  case MODE_FAST:
    if (state == STATE_R) {
      state = STATE_B;
    } 
    else {
      if (state == STATE_B) state = STATE_R;
    }
    break;
  }



  switch (state) {
  case STATE_R:   doStateR();   break;
  case STATE_RB:  doStateRB();  break; 
  case STATE_B:   doStateB();   break;    
  case STATE_BR:  doStateBR();  break;
  case STATE_RBk: doStateRBk(); break;
  case STATE_BkR: doStateBkR(); break;
  case STATE_BkW: doStateBkW(); break;
  case STATE_WBk: doStateWBk(); break;
  default:
    Serial.print("Unhandled state: ");
    Serial.println(state, DEC);
    break;
  }

  previousMillis = millis();
  interval = (usrMode == MODE_SLOW) ? 1000 : 100;

}
