#include "SPIFFS.h"
#include <MFRC522.h>

#define SS_PIN 21  // Define the pins used by the MFRC522
#define RST_PIN 22

MFRC522 mfrc522(SS_PIN, RST_PIN);  // Create MFRC522 instance

const char *RfidTagsPath = "/RfidTagsPath.txt";
String Stored_Tags;

void setup() {
  Serial.begin(115200);
  // Initialize SPIFFS
  initSPIFFS();
  // Initialize RFID reader
  RfidSet();
  // Read the RFID tags from SPIFFS
  Stored_Tags = readFile(SPIFFS, RfidTagsPath);
  Serial.println("Stored RFID tags:");
  Serial.println(Stored_Tags);
}

void loop() {
  if (Serial.available()) {
    String command = Serial.readStringUntil('\n');
    command.trim();  // Remove any leading/trailing whitespace
    handleCommand(command);
  }
}

// Initialize SPIFFS
void initSPIFFS() {
  if (!SPIFFS.begin(true)) {
    Serial.println("An error has occurred while mounting SPIFFS");
  }
  Serial.println("SPIFFS mounted successfully");
}

// RFID initialization
void RfidSet() {
  SPI.begin();
  mfrc522.PCD_Init();
}

void handleCommand(const String &command) {
  if (command.startsWith("ADD ")) {
    String tag = command.substring(4);
    scanAndStoreTag(tag);
  } else if (command.startsWith("DELETE ")) {
    String tag = command.substring(7);
    deleteTag(tag);
  } else if (command.startsWith("UPDATE ")) {
    int splitIndex = command.indexOf(' ', 7);
    if (splitIndex > 7) {
      String oldTag = command.substring(7, splitIndex);
      String newTag = command.substring(splitIndex + 1);
      updateTag(oldTag, newTag);
    } else {
      Serial.println("Invalid UPDATE command format. Use: UPDATE <oldTag> <newTag>");
    }
  } else if (command.startsWith("CHECK ")) {
    String tag = command.substring(6);
    checkRFIDTag(tag);
  } else {
    Serial.println("Unknown command. Available commands: ADD, DELETE, UPDATE, CHECK");
  }
}

// Check if RFID tag is already stored
bool isTagStored(const String &tag) {
  Serial.println("New Tag: " + tag);

  int pos = 0;
  String storedTag;
  while ((pos = Stored_Tags.indexOf('\n')) != -1) {
    storedTag = Stored_Tags.substring(0, pos);
    storedTag.trim();  // Trim any leading or trailing whitespace
    Stored_Tags.remove(0, pos + 1);

    if (storedTag == tag) {
      return true;
    }
  }

  // Also check the last line if there is no newline at the end of the file
  storedTag = Stored_Tags;
  storedTag.trim();
  if (storedTag == tag) {
    return true;
  }

  return false;
}

// Read File from SPIFFS
String readFile(fs::FS &fs, const char *path) {
  Serial.printf("Reading file: %s\r\n", path);

  File file = fs.open(path);
  if (!file || file.isDirectory()) {
    Serial.println("- failed to open file for reading");
    return String();
  }

  String fileContent;
  while (file.available()) {
    fileContent += file.readStringUntil('\n') + "\n";
  }
  file.close();
  return fileContent;
}

// Write file to SPIFFS (appending)
void appendFile(fs::FS &fs, const char *path, const char *message) {
  Serial.printf("Appending to file: %s\r\n", path);

  File file = fs.open(path, FILE_APPEND);
  if (!file) {
    Serial.println("- failed to open file for appending");
    return;
  }
  if (file.println(message)) {
    Serial.println("- file appended");
  } else {
    Serial.println("- append failed");
  }
  file.close();
}

// Overwrite file to SPIFFS (overwriting)
void OverwriteFile(fs::FS &fs, const char *path, const String &message) {
  Serial.printf("Writing file: %s\r\n", path);

  File file = fs.open(path, FILE_WRITE);
  if (!file) {
    Serial.println("- failed to open file for writing");
    return;
  }
  if (file.print(message)) {
    Serial.println("- file written");
  } else {
    Serial.println("- write failed");
  }
  file.close();
}

// Delete a specific tag from SPIFFS
void deleteTag(const String &tag) {
  String newContent = "";

  int pos = 0;
  String storedTag;
  bool tagFound = false;

  while ((pos = Stored_Tags.indexOf('\n')) != -1) {
    storedTag = Stored_Tags.substring(0, pos);
    storedTag.trim();  // Trim any leading or trailing whitespace
    Stored_Tags.remove(0, pos + 1);

    if (storedTag == tag) {
      tagFound = true;
    } else {
      newContent += storedTag + "\n";
    }
  }

  // Also check the last line if there is no newline at the end of the file
  storedTag = Stored_Tags;
  storedTag.trim();
  if (storedTag == tag) {
    tagFound = true;
  } else if (!storedTag.isEmpty()) {
    newContent += storedTag + "\n";
  }

  if (tagFound) {
    OverwriteFile(SPIFFS, RfidTagsPath, newContent);
    Serial.println("Tag deleted");
  } else {
    Serial.println("Tag not found");
  }
}

// Update an existing RFID tag in SPIFFS
void updateTag(const String &oldTag, const String &newTag) {
  // Read the existing tags from the file
  String Stored_Tags = readFile(SPIFFS, RfidTagsPath);
  String newContent = "";

  int pos = 0;
  String storedTag;
  bool tagFound = false;

  // Check if the new tag is already stored to avoid duplicates
  if (isTagStored(newTag)) {
    Serial.println("Duplicate tag, update not performed");
    return;
  }

  // Iterate through stored tags to find the old tag and replace it
  while ((pos = Stored_Tags.indexOf('\n')) != -1) {
    storedTag = Stored_Tags.substring(0, pos);
    storedTag.trim();  // Trim any leading or trailing whitespace
    Stored_Tags.remove(0, pos + 1);

    if (storedTag == oldTag) {
      tagFound = true;
      newContent += newTag + "\n";  // Replace old tag with new tag
    } else {
      newContent += storedTag + "\n";
    }
  }

  // Check the last line if there is no newline at the end of the file
  storedTag = Stored_Tags;
  storedTag.trim();
  if (storedTag == oldTag) {
    tagFound = true;
    newContent += newTag + "\n";  // Replace old tag with new tag
  } else if (!storedTag.isEmpty()) {
    newContent += storedTag + "\n";
  }

  // Write the new content back to the file if the old tag was found and replaced
  if (tagFound) {
    OverwriteFile(SPIFFS, RfidTagsPath, newContent);
    Serial.println("Tag updated");
  } else {
    Serial.println("Tag not found");
  }
}

// Scan and store RFID tag
void scanAndStoreTag(const String &tag) {
  if (!isTagStored(tag)) {
    appendFile(SPIFFS, RfidTagsPath, tag.c_str());
    Serial.println("Tag stored");
  } else {
    Serial.println("Duplicate tag, not stored");
  }
}

// Check if scanned RFID tag matches stored tags
void checkRFIDTag(const String &scannedTag) {
  int pos = 0;
  String tag;
  bool match = false;
  while ((pos = Stored_Tags.indexOf('\n')) != -1) {
    tag = Stored_Tags.substring(0, pos);
    tag.trim();  // Trim the tag to remove any leading or trailing whitespace
    Stored_Tags.remove(0, pos + 1);

    if (tag == scannedTag) {
      match = true;
      break;
    }
  }

  // Also check the last line if there is no newline at the end of the file
  tag = Stored_Tags;
  tag.trim();
  if (tag == scannedTag) {
    match = true;
  }

  if (match) {
    Serial.println("Match found!");
    Success();
  } else {
    Serial.println("No match found.");
    Denied();
  }
}

void Success() {
  Serial.println("Access Granted!");
  // Add your success handling code here
}

void Denied() {
  Serial.println("Access Denied!");
  // Add your denied handling code here
}

void RFID_RUN() {
  if (!mfrc522.PICC_IsNewCardPresent()) {
    return;
  }
  if (!mfrc522.PICC_ReadCardSerial()) {
    return;
  }

  String content = "";
  for (byte i = 0; i < mfrc522.uid.size; i++) {
    content.concat(String(mfrc522.uid.uidByte[i] < 0x10 ? " 0" : " "));
    content.concat(String(mfrc522.uid.uidByte[i], HEX));
  }
  content.toUpperCase();

  Serial.print("UID tag: ");
  Serial.println(content);

  // Check if scanned RFID tag matches stored tags
  checkRFIDTag(content);
}