/* -------------------------------------------------------------
   Arduino lock controller – fixed PIN (27549)
   • No EEPROM usage
   • No buzzer
   • Dial input on D2, lock control on D4
   ------------------------------------------------------------- */

#include <LiquidCrystal_I2C.h>

// ---------- Configuration ----------
const uint8_t ROTARY_PIN   = 2;   // pulse line from rotary dial
const uint8_t CLEAR_PIN    = 3;   // clear / cancel button
const uint8_t LOCK_PIN     = 4;   // drives MOSFET / relay for lock

const unsigned long DIAL_FINISH_MS = 800;   // pause after last pulse
const unsigned long DEBOUNCE_MS    = 15;    // rotary debounce
const unsigned long BTN_DEBOUNCE_MS= 50;    // clear button debounce
const unsigned long UNLOCK_TIME_MS = 3000; // how long lock stays open

// ----- Fixed password (you wanted 2‑7‑5‑4‑9) -----
const char FIXED_PIN[] = "27549";   // 5‑digit PIN, null‑terminated

// ---------- Runtime state ----------
LiquidCrystal_I2C lcd(0x27, 16, 2);

int  needToPrint = 0;
int  count = 0;
int  lastState = HIGH;   // using INPUT_PULLUP → idle = HIGH
int  trueState = HIGH;
unsigned long lastStateChangeTime = 0;

int  buttonLastState = HIGH;
unsigned long buttonLastChange = 0;
bool clearRequested = false;

char enteredCode[9] = "";   // max 8 chars + terminator
byte enteredLen = 0;

// -------------------------------------------------------------
void setup() {
  lcd.init();
  lcd.backlight();
  lcd.clear();
  lcd.setCursor(0, 0);
  lcd.print("Enter code:");

  Serial.begin(9600);

  pinMode(ROTARY_PIN, INPUT_PULLUP);
  pinMode(CLEAR_PIN,  INPUT_PULLUP);
  pinMode(LOCK_PIN,   OUTPUT);
  digitalWrite(LOCK_PIN, LOW);   // lock closed (inactive)
}

// -------------------------------------------------------------
void loop() {
  /* ----------- Clear button handling ----------- */
  int btnReading = digitalRead(CLEAR_PIN);
  if (btnReading != buttonLastState) {
    buttonLastChange = millis();               // start debounce timer
  }
  if ((millis() - buttonLastChange) > BTN_DEBOUNCE_MS) {
    if (btnReading == LOW && buttonLastState == HIGH) {
      clearRequested = true;
    }
  }
  buttonLastState = btnReading;

  /* ----------- Rotary‑dial handling ----------- */
  int reading = digitalRead(ROTARY_PIN);

  // Detect a pause that signals "digit finished"
  if ((millis() - lastStateChangeTime) > DIAL_FINISH_MS) {
    if (needToPrint) {
      // Store the digit (0‑9) in the buffer
      char digitChar = (count % 10) + '0';
      if (enteredLen < sizeof(enteredCode) - 1) {
        enteredCode[enteredLen++] = digitChar;
        enteredCode[enteredLen] = '\0';   // null‑terminate
        lcd.print(digitChar);             // echo on LCD
      }
      Serial.print(digitChar);
      needToPrint = 0;
      count = 0;
    }
  }

  // Edge detection for raw rotary signal
  if (reading != lastState) {
    lastStateChangeTime = millis();   // restart pause timer
  }

  // Debounce the rotary pulses
  if ((millis() - lastStateChangeTime) > DEBOUNCE_MS) {
    if (reading != trueState) {
      trueState = reading;
      if (trueState == HIGH) {   // count pulse on rising edge
        ++count;
        needToPrint = 1;
      }
    }
  }
  lastState = reading;

  /* ----------- Process clear request ----------- */
  if (clearRequested) {
    lcd.clear();
    lcd.setCursor(0, 0);
    lcd.print("Enter code:");
    enteredLen = 0;
    enteredCode[0] = '\0';
    needToPrint = 0;
    count = 0;
    clearRequested = false;
    Serial.println(F("[CLEARED]"));
  }

  /* ----------- Detect end‑of‑entry ----------- */
  static unsigned long lastDigitTime = 0;
  if (needToPrint) {
    lastDigitTime = millis();   // a new digit arrived
  }

  // If we have at least one digit and a long pause (>2×DIAL_FINISH_MS),
  // treat it as the user finishing the entry.
  if (enteredLen > 0 &&
      (millis() - lastDigitTime) > (2 * DIAL_FINISH_MS)) {

    // ----- Compare entered code with the fixed PIN -----
    if (strcmp(enteredCode, FIXED_PIN) == 0) {
      grantAccess();
    } else {
      denyAccess();
    }

    // Reset for next attempt
    enteredLen = 0;
    enteredCode[0] = '\0';
    lcd.clear();
    lcd.setCursor(0, 0);
    lcd.print("Enter code:");
    delay(500);   // tiny pause before accepting next entry
  }
}

// -------------------------------------------------------------
void grantAccess() {
  lcd.clear();
  lcd.setCursor(0, 0);
  lcd.print("Access granted");
  Serial.println(F("[GRANTED]"));

  // Open the lock
  digitalWrite(LOCK_PIN, HIGH);   // energize solenoid/relay
  delay(UNLOCK_TIME_MS);          // keep unlocked for defined period
  digitalWrite(LOCK_PIN, LOW);    // lock again

  // Optional visual cue – flash LCD backlight briefly
  lcd.noBacklight();
  delay(100);
  lcd.backlight();
}

// -------------------------------------------------------------
void denyAccess() {
  lcd.clear();
  lcd.setCursor(0, 0);
  lcd.print("Access denied");
  Serial.println(F("[DENIED]"));
}