#include <Keypad.h>
#include <textBuff.h>
#include <timeObj.h>
#include <resizeBuff.h>


#define PASSCODE        "314DAB"  // Set whatever you want.
#define UNLOCK_LED_PIN  10
#define BAD_STR_LED_PIN 12
#define ROWS             4
#define COLS             4
#define NUM_CHARS       80
#define ERROR_MS        500
#define PASS_MS         5000

#define BEEPER_PIN      11
#define CLICK_FREQ      600
#define CLICK_FREQ_MS   25
#define UNLOCK_FREQ     800
#define UNLOCK_FREQ_MS  100
#define ERROR_FREQ      100
#define ERROR_FREQ_MS   500


// Stuff for setting up the keypad to send chars.
char keys[ROWS][COLS] = {
  { '1', '2', '3', 'A' },
  { '4', '5', '6', 'B' },
  { '7', '8', '9', 'C' },
  { '*', '0', '#', 'D' }
};
uint8_t colPins[COLS] = { 5, 4, 3, 2 }; // Pins connected to C1, C2, C3, C4
uint8_t rowPins[ROWS] = { 9, 8, 7, 6 }; // Pins connected to R1, R2, R3, R4

// Our globals.
Keypad    keypad = Keypad(makeKeymap(keys), rowPins, colPins, ROWS, COLS);  // Our keypad
textBuff  ourMsg(NUM_CHARS);            // Our text buffer for keystrokes.
bool      locked;                       // Are we locked or not?
bool      badStr;                       // Are we showing error for bad try or not?
timeObj   badStrTimer(ERROR_MS,false);  // A timer for how long we show bad try error.
timeObj   lockTimer(PASS_MS,false);     // A timer for how long we are unlocked.


// Good 'ol setup..
void setup() {

  Serial.begin(9600);                 // Serial port would be nice..
  locked = true;                      // We are locked.
  badStr = false;                     // We're not showing a bad try error.
  pinMode(UNLOCK_LED_PIN,OUTPUT);     // LED that shows us being unlocked/
  pinMode(BAD_STR_LED_PIN,OUTPUT);    // LED that shows a bad password.
  Serial.println("Enter password followed by # to unlock");
  Serial.println("Hint, look at the top of the code.");
}


// Deal with grabbing the string out of the run buffer.
// Check to see if its the valid password. Take the appropiate action.
void checkStr(void) {
  
  char* passStr;

  passStr = ourMsg.readStr();           // Grab the blinkin' string.
  ourMsg.clear();                       // Just in case, we clear the ring buffer.
  if (!strcmp(PASSCODE,passStr)) {      // If it's what we are looking for..
    unlock();                           // We unlock the machine.
  } else {                              // Else, it wasn't what we were looking for..
    showError();                        // We show an error.
  }                                     
}


// Do what we need to do to lock up.
void lock(void) {
  
  locked = true;
  lockTimer.reset();
  Serial.println("Relocking.");
}


// Do what we need to do to unlock.
void unlock(void) {

  locked = false;
  lockTimer.start();
  Serial.println("Success!");
  tone(BEEPER_PIN, UNLOCK_FREQ, UNLOCK_FREQ_MS);
}


// Do what we need to do to show an error.
void showError(void) {

  badStr = true;
  badStrTimer.start();
  Serial.println("No.");
  tone(BEEPER_PIN, ERROR_FREQ, ERROR_FREQ_MS);
}


// Do what we need to do to clear an error.
void clearError(void) {

  badStr = false;
  badStrTimer.reset();
}


// And here's loop. Do what we need to do.. Forever!
void loop() {
  
  char  key;

  key = keypad.getKey();                          // Make a grab for a keystroke..
  if (key != NO_KEY) {                            // If we got a valid keystroke..
    tone(BEEPER_PIN, CLICK_FREQ, CLICK_FREQ_MS);  // Let the user know we saw a click.
    if (key=='#') {                               // If the key was #..
      Serial.println();                           // Linefeed the serial monitor.
      checkStr();                                 // Check. Is what we're looking for?
    } else {                                      // Else, not a #..
      Serial.print(key);                          // Show them what we're seeing.
      ourMsg.addChar(key);                        // Add the key to our ring buffer.
    }                                             //
  }                                               //
  if (!locked && lockTimer.ding()) {              // If we're unlocked and the timer has expired..
    lock();                                       // Relock the machine.
  }                                               //
  if (badStr && badStrTimer.ding()) {             // If we're showing an error and the time is up..
    clearError();                                 // Stop showing the error.
  }                                               //
  digitalWrite(UNLOCK_LED_PIN,!locked);           // Set the unlock LED.
  digitalWrite(BAD_STR_LED_PIN,badStr);           // Set the error LED.
}