// Download this in a .zip file.


/* Test the freetronics 16x2 LCD shield 
http://www.freetronics.com/pages/16x2-lcd-shield-quickstart-guide#.UBKMm7T9OK9

MTG Life Counter w/ hidden poison counter
by eturnerx@gmail.com 201208

*/
/*--------------------------------------------------------------------------------------
  Includes
--------------------------------------------------------------------------------------*/
#include    // include LCD library
/*--------------------------------------------------------------------------------------
  Defines
--------------------------------------------------------------------------------------*/
// Pins in use
#define BUTTON_ADC_PIN           A0  // A0 is the button ADC input
#define LCD_BACKLIGHT_PIN         3  // D3 controls LCD backlight
// ADC readings expected for the 5 buttons on the ADC input
#define RIGHT_10BIT_ADC           0  // right
#define UP_10BIT_ADC            145  // up
#define DOWN_10BIT_ADC          329  // down
#define LEFT_10BIT_ADC          505  // left
#define SELECT_10BIT_ADC        741  // right
#define BUTTONHYSTERESIS         10  // hysteresis for valid button sensing window
//return values for ReadButtons()
#define BUTTON_NONE               0  // 
#define BUTTON_RIGHT              1  // 
#define BUTTON_UP                 2  // 
#define BUTTON_DOWN               3  // 
#define BUTTON_LEFT               4  // 
#define BUTTON_SELECT             5  // 

// Define the state: it makes the code easier to read
#define STATE_LIFE1               0
#define STATE_LIFE2               1
#define STATE_POISON1             2
#define STATE_POISON2             3


/*--------------------------------------------------------------------------------------
  Variables
--------------------------------------------------------------------------------------*/
byte buttonJustPressed  = false;         //this will be true after a ReadButtons() call if triggered
byte buttonJustReleased = false;         //this will be true after a ReadButtons() call if triggered
byte buttonWas          = BUTTON_NONE;   //used by ReadButtons() for detection of button events

// Setup the variables that will be used
int life1 = 20;
int life2 = 20;
byte poison1 = 0;
byte poison2 = 0;
byte resetcounter = 0;
byte state = 0;


/*--------------------------------------------------------------------------------------
  Init the LCD library with the LCD pins to be used
--------------------------------------------------------------------------------------*/
LiquidCrystal lcd( 8, 9, 4, 5, 6, 7 );   //Pins for the freetronics 16x2 LCD shield. LCD: ( RS, E, LCD-D4, LCD-D5, LCD-D6, LCD-D7 )


/*--------------------------------------------------------------------------------------
  setup()
  Called by the Arduino framework once, before the main loop begins
--------------------------------------------------------------------------------------*/
void setup() {
  // Setup pins. 
  pinMode(BUTTON_ADC_PIN, INPUT );         // Analogue input for the LCD's buttons
  digitalWrite(BUTTON_ADC_PIN, LOW );
  pinMode(LCD_BACKLIGHT_PIN, OUTPUT );     // Backlight control (off/on)
  digitalWrite(LCD_BACKLIGHT_PIN, HIGH );  // Turn the backlight on

  lcd.begin( 16, 2 );                      // Tell LCD library this is 16x2 display
  lcd.setCursor( 0, 0 );
  //          1234567890123456
  lcd.print( "The Life Counter" );
  updateLifeCounts();
  
  digitalWrite(LCD_BACKLIGHT_PIN, LOW);  delay(150);  //PULSE the backlight a few times
  digitalWrite(LCD_BACKLIGHT_PIN, HIGH); delay(150);
  digitalWrite(LCD_BACKLIGHT_PIN, LOW);  delay(150);
  digitalWrite(LCD_BACKLIGHT_PIN, HIGH); delay(150);
  digitalWrite(LCD_BACKLIGHT_PIN, LOW);  delay(150);
  digitalWrite(LCD_BACKLIGHT_PIN, HIGH); 
}


/*--------------------------------------------------------------------------------------
  loop()
  Arduino main loop
--------------------------------------------------------------------------------------*/
void loop() {
  byte button;
   
  button = ReadButtons();
  if(button == BUTTON_NONE) {          // If no button is pressed then
    resetcounter = 0;                  //  reset timer for the rest function
  } else if(buttonJustPressed) {       // Otherwise, if a button has just been pressed
      switch(state) {                  //    Do different things depending on "state"
        case STATE_LIFE1:   do_state_life1(button);   break;
        case STATE_LIFE2:   do_state_life2(button);   break;
        case STATE_POISON1: do_state_poison1(button); break;
        case STATE_POISON2: do_state_poison2(button); break;
        default: break;
      }
      updateLifeCounts();              // Something was pressed so update the display
  } else if(button == BUTTON_SELECT) { // BUTTON_SELECT is pressed so do the reset sequence
    digitalWrite(LCD_BACKLIGHT_PIN, LOW );  delay( 150 );  // Backlight off
    digitalWrite(LCD_BACKLIGHT_PIN, HIGH ); delay( 150 );  // Backlight on
    resetcounter++;
    if(resetcounter > 10) {            // Life/Poison is reset after button is held for a time
      resetcounter = 0;
      life1 = 20;    life2 = 20;
      poison1 = 0;   poison2 = 0;
      state = 0;
      updateLifeCounts();
    }
  }
  //clear the buttonJustPressed & buttonJustReleased flags; they've already done their job
 if(buttonJustPressed)  buttonJustPressed  = false;
 if(buttonJustReleased) buttonJustReleased = false;
 delay(25);                           // Delay to stop buttons "jittering" false presses
}

void do_state_life1(byte button) {
  switch(button) {
    case BUTTON_LEFT:  state = STATE_POISON1;     break;
    case BUTTON_RIGHT: state = STATE_LIFE2;       break;
    case BUTTON_UP:    life1++;                   break;
    case BUTTON_DOWN:  life1--;                   break;
    default: break; 
  }
}

void do_state_life2(byte button) {
  switch(button) {
    case BUTTON_LEFT:  state = STATE_LIFE1;       break;
    case BUTTON_RIGHT: state = STATE_POISON2;     break;
    case BUTTON_UP:    life2++;                   break;
    case BUTTON_DOWN:  life2--;                   break;
    default: break; 
  }
}

void do_state_poison1(byte button) {
  switch(button) {
    case BUTTON_LEFT:  state = STATE_LIFE1;       break;
    case BUTTON_RIGHT: state = STATE_LIFE1;       break;
    case BUTTON_UP:    poison1++;                 break;
    case BUTTON_DOWN:  if(poison1 > 0) poison1--; break;
    default: break; 
  }
}

void do_state_poison2(byte button) {
  switch(button) {
    case BUTTON_LEFT:  state = STATE_LIFE2;       break;
    case BUTTON_RIGHT: state = STATE_LIFE2;       break;
    case BUTTON_UP:    poison2++;                 break;
    case BUTTON_DOWN:  if(poison2 > 0) poison2--; break;
    default: break; 
  }
}

//          1234567890123456
//          The Life Counter      
//          LLssP      PssLL                
//          1234567890123456

void updateLifeCounts() {
  lcd.setCursor( 0, 1 );

  if(life1 <= 0 || poison1 >= 10) lcd.print("XXXXX      "); else {
    if(life1 > 99) lcd.print("99"); else {
      if(life1 < 10) lcd.print(' ');
      lcd.print(life1);
    }
    if(state == STATE_LIFE1) lcd.print("* ");
      else if(state == STATE_POISON1) lcd.print(" ^"); 
      else lcd.print("  ");
    if(state == STATE_POISON1 || poison1 > 0) lcd.print(poison1);
      else lcd.print(' ');
    lcd.print("      ");
  }

  if(life2 <= 0 || poison2 >= 10) lcd.print("XXXXX"); else {
    if(state == STATE_POISON2 || poison2 > 0) lcd.print(poison2);
      else lcd.print(' ');
    if(state == STATE_LIFE2) lcd.print(" *");
      else if(state == STATE_POISON2) lcd.print("^ "); 
      else lcd.print("  ");
    if(life1 > 99) lcd.print("99"); else {
      if(life2 < 10) lcd.print(' ');
      lcd.print(life2);
    }
  }
}


/*--------------------------------------------------------------------------------------
  ReadButtons()
  Detect the button pressed and return the value
  Uses global values buttonWas, buttonJustPressed, buttonJustReleased.
--------------------------------------------------------------------------------------*/
byte ReadButtons() {
   unsigned int buttonVoltage;
   byte button = BUTTON_NONE;   // return no button pressed if the below checks don't write to btn
   
   //read the button ADC pin voltage
   buttonVoltage = analogRead( BUTTON_ADC_PIN );
   //sense if the voltage falls within valid voltage windows
   if( buttonVoltage < ( RIGHT_10BIT_ADC + BUTTONHYSTERESIS ) )
   {
      button = BUTTON_RIGHT;
   }
   else if(   buttonVoltage >= ( UP_10BIT_ADC - BUTTONHYSTERESIS )
           && buttonVoltage <= ( UP_10BIT_ADC + BUTTONHYSTERESIS ) )
   {
      button = BUTTON_UP;
   }
   else if(   buttonVoltage >= ( DOWN_10BIT_ADC - BUTTONHYSTERESIS )
           && buttonVoltage <= ( DOWN_10BIT_ADC + BUTTONHYSTERESIS ) )
   {
      button = BUTTON_DOWN;
   }
   else if(   buttonVoltage >= ( LEFT_10BIT_ADC - BUTTONHYSTERESIS )
           && buttonVoltage <= ( LEFT_10BIT_ADC + BUTTONHYSTERESIS ) )
   {
      button = BUTTON_LEFT;
   }
   else if(   buttonVoltage >= ( SELECT_10BIT_ADC - BUTTONHYSTERESIS )
           && buttonVoltage <= ( SELECT_10BIT_ADC + BUTTONHYSTERESIS ) )
   {
      button = BUTTON_SELECT;
   }
   //handle button flags for just pressed and just released events
   if( ( buttonWas == BUTTON_NONE ) && ( button != BUTTON_NONE ) )
   {
      //the button was just pressed, set buttonJustPressed, this can optionally be used to trigger a once-off action for a button press event
      //it's the duty of the receiver to clear these flags if it wants to detect a new button change event
      buttonJustPressed  = true;
      buttonJustReleased = false;
   }
   if( ( buttonWas != BUTTON_NONE ) && ( button == BUTTON_NONE ) )
   {
      buttonJustPressed  = false;
      buttonJustReleased = true;
   }
   
   //save the latest button value, for change event detection next time round
   buttonWas = button;
   
   return( button );
}