/*
  SURAKSHA
  Smart Security Lock + Automatic Attendance
  OpenSource Section | SB.Chetak Innovation

  Reference implementation for authorised installations only.

  Hardware reference:
  - Arduino Mega 2560
  - MFRC522 RFID reader
  - Fingerprint sensor on Serial1
  - DS3231 RTC
  - SD card
  - I2C LCD 16x2
  - Low-voltage relay module controlling a 12 V solenoid lock
  - Siren / indicator output
  - Door sensor and exit push button

  No real credentials are included. Replace example RFID UIDs with cards
  you are authorised to enrol.
*/

#include <SPI.h>
#include <MFRC522.h>
#include <Wire.h>
#include <LiquidCrystal_I2C.h>
#include <RTClib.h>
#include <SD.h>
#include <Adafruit_Fingerprint.h>

const uint8_t RFID_SS = 53;
const uint8_t RFID_RST = 5;
const uint8_t SD_CS = 4;

const uint8_t RELAY_PIN = 7;
const uint8_t SIREN_PIN = 8;
const uint8_t LED_GREEN = 9;
const uint8_t LED_RED = 10;
const uint8_t DOOR_SENSOR = 6;
const uint8_t EXIT_BUTTON = 3;

MFRC522 rfid(RFID_SS, RFID_RST);
LiquidCrystal_I2C lcd(0x27, 16, 2);
RTC_DS3231 rtc;
Adafruit_Fingerprint finger = Adafruit_Fingerprint(&Serial1);

const char *AUTHORIZED_UIDS[] = {
  "AA BB CC DD",  // replace with an authorised card UID
  "11 22 33 44"   // replace with an authorised card UID
};
const size_t AUTH_UID_COUNT = sizeof(AUTHORIZED_UIDS) / sizeof(AUTHORIZED_UIDS[0]);

bool relayActiveHigh = true;

void setLockOpen(bool open) {
  digitalWrite(RELAY_PIN, relayActiveHigh ? open : !open);
}

String uidString() {
  String result = "";
  for (byte i = 0; i < rfid.uid.size; i++) {
    if (i) result += " ";
    if (rfid.uid.uidByte[i] < 0x10) result += "0";
    result += String(rfid.uid.uidByte[i], HEX);
  }
  result.toUpperCase();
  return result;
}

bool authorisedUid(const String &uid) {
  for (size_t i = 0; i < AUTH_UID_COUNT; i++) {
    if (uid == AUTHORIZED_UIDS[i]) return true;
  }
  return false;
}

void logEvent(const String &eventText) {
  File f = SD.open("attendance.csv", FILE_WRITE);
  if (!f) return;

  DateTime now = rtc.now();
  f.print(now.year()); f.print("-");
  if (now.month() < 10) f.print("0");
  f.print(now.month()); f.print("-");
  if (now.day() < 10) f.print("0");
  f.print(now.day()); f.print(",");
  if (now.hour() < 10) f.print("0");
  f.print(now.hour()); f.print(":");
  if (now.minute() < 10) f.print("0");
  f.print(now.minute()); f.print(":");
  if (now.second() < 10) f.print("0");
  f.print(now.second()); f.print(",");
  f.println(eventText);
  f.close();
}

void grantAccess(const String &who) {
  setLockOpen(true);
  digitalWrite(LED_GREEN, HIGH);

  lcd.clear();
  lcd.setCursor(0, 0);
  lcd.print("ACCESS GRANTED");
  lcd.setCursor(0, 1);
  lcd.print(who.substring(0, 16));

  logEvent("GRANTED:" + who);

  delay(2500);
  setLockOpen(false);
  digitalWrite(LED_GREEN, LOW);
}

void denyAccess(const String &reason) {
  digitalWrite(LED_RED, HIGH);
  digitalWrite(SIREN_PIN, HIGH);

  lcd.clear();
  lcd.setCursor(0, 0);
  lcd.print("ACCESS DENIED");
  lcd.setCursor(0, 1);
  lcd.print(reason.substring(0, 16));

  logEvent("DENIED:" + reason);

  delay(1200);
  digitalWrite(SIREN_PIN, LOW);
  digitalWrite(LED_RED, LOW);
}

bool fingerprintMatch() {
  if (finger.getImage() != FINGERPRINT_OK) return false;
  if (finger.image2Tz() != FINGERPRINT_OK) return false;
  if (finger.fingerFastSearch() != FINGERPRINT_OK) return false;
  return true;
}

void setup() {
  Serial.begin(115200);
  Serial1.begin(57600);

  pinMode(RELAY_PIN, OUTPUT);
  pinMode(SIREN_PIN, OUTPUT);
  pinMode(LED_GREEN, OUTPUT);
  pinMode(LED_RED, OUTPUT);
  pinMode(DOOR_SENSOR, INPUT_PULLUP);
  pinMode(EXIT_BUTTON, INPUT_PULLUP);

  setLockOpen(false);

  Wire.begin();
  lcd.init();
  lcd.backlight();

  SPI.begin();
  rfid.PCD_Init();

  rtc.begin();
  SD.begin(SD_CS);

  finger.begin(57600);
  finger.verifyPassword();

  lcd.setCursor(0, 0);
  lcd.print("SURAKSHA");
  lcd.setCursor(0, 1);
  lcd.print("Ready");
}

void loop() {
  // Authorised exit from inside.
  if (digitalRead(EXIT_BUTTON) == LOW) {
    grantAccess("EXIT BUTTON");
    while (digitalRead(EXIT_BUTTON) == LOW) delay(10);
  }

  // RFID authentication.
  if (rfid.PICC_IsNewCardPresent() && rfid.PICC_ReadCardSerial()) {
    String uid = uidString();

    if (authorisedUid(uid)) {
      grantAccess("RFID " + uid);
    } else {
      denyAccess("UNKNOWN CARD");
    }

    rfid.PICC_HaltA();
    rfid.PCD_StopCrypto1();
  }

  // Fingerprint authentication.
  if (fingerprintMatch()) {
    grantAccess("FINGER ID " + String(finger.fingerID));
    delay(500);
  }

  // Door status can be logged or shown locally.
  static bool previousDoorOpen = false;
  bool doorOpen = digitalRead(DOOR_SENSOR) == HIGH;
  if (doorOpen != previousDoorOpen) {
    logEvent(doorOpen ? "DOOR_OPEN" : "DOOR_CLOSED");
    previousDoorOpen = doorOpen;
  }
}
