#include <SD.h>
#include <SPI.h>
#include <Wire.h>
#include <LiquidCrystal_I2C.h>

#define MAX_CANDIDATES 10
#define MAX_NAME_LENGTH 16
#define MAX_HOUSE_LENGTH 16
#define DATA_FILE "data.csv"

LiquidCrystal_I2C lcd(0x27, 16, 2);

enum buttons {LEFT=2, MID, RIGHT, RESULT};
enum output {BUZZER=7};
enum storage {CARD=10};

int numCandidates = 0;
int currentCandidateIndex = 0;

class Candidate {
  public:
    char name[MAX_NAME_LENGTH];
    char house[MAX_HOUSE_LENGTH];
    int votes;

    Candidate() {
      strcpy(name, "\0");
      strcpy(house, "\0");
      votes = 0;
    }

    Candidate(const char *cName, const char *cHouse, int cVotes = 0) {
      strcpy(name, cName);
      strcpy(house, cHouse);
      votes = cVotes;
    }

    void addVote() {
      votes++;
    }
};

Candidate candidates[MAX_CANDIDATES];

void setup() {
  Serial.begin(9600);

  lcd.init();
  lcd.backlight();
  lcd.print("-VOTING SYSTEM-");
  delay(2000);

  if (!SD.begin(CARD)) {
    Serial.println("SD Card not detected.");
    return;
  }
  Serial.println("SD Card inserted.");
  
  if (SD.exists(DATA_FILE)) {
    loadCandidatesFromCSV();
  } else {
    addDefaultCandidates();
  }

  pinMode(LEFT, INPUT);
  pinMode(MID, INPUT);
  pinMode(RIGHT, INPUT);
  pinMode(RESULT, INPUT);
  pinMode(BUZZER, OUTPUT);

  displayCurrentCandidate();
}

void loop() {

  if (digitalRead(LEFT))
    navigateCandidates(-1);

  if (digitalRead(MID))
    voteCurrentCandidate();

  if (digitalRead(RIGHT))
    navigateCandidates(1);
  
  if (digitalRead(RESULT))
    displayWinner();

}

void loadCandidatesFromCSV() {
  File file = SD.open(DATA_FILE, FILE_READ);
  if (!file) {
    Serial.println("File could not be opened.");
    return;
  }

  while (file.available()) {
    String line = file.readStringUntil('\n');
    line.trim();
    if (line.length() == 0) continue;
    
    int commaIndex1 = line.indexOf(',');
    int commaIndex2 = line.lastIndexOf(',');

    const char *name = line.substring(0, commaIndex1).c_str();
    const char *house = line.substring(commaIndex1 + 1, commaIndex2).c_str();
    int votes = line.substring(commaIndex2 + 1).toInt();

    if (numCandidates < MAX_CANDIDATES)
    {
      Candidate tempCandidate(name, house, votes);
      candidates[numCandidates++] = tempCandidate;
    }
  }

  file.close();
}

void addDefaultCandidates() {
  addCandidate("Aarav", "Red");
  addCandidate("Sunita", "Blue");
  addCandidate("Rajendra", "Green");
  addCandidate("Anjali", "Blue");
  addCandidate("Dinesh", "Red");
  addCandidate("Kritika", "Green");
  addCandidate("Bijay", "Red");
  addCandidate("Sweta", "Blue");
  addCandidate("Niraj", "Yellow");
  addCandidate("Priya", "Yellow");

  updateCSV();
}

void addCandidate(const char *name, const char *house) {
  if (numCandidates < MAX_CANDIDATES)
  {
    Candidate tempCandidate(name, house);
    candidates[numCandidates] = tempCandidate;
    numCandidates++;
  }
}

void navigateCandidates(int direction) {
  currentCandidateIndex += direction;

  if (currentCandidateIndex < 0)
    currentCandidateIndex = numCandidates - 1;

  if (currentCandidateIndex >= numCandidates)
    currentCandidateIndex = 0;

  displayCurrentCandidate();
  tone(BUZZER, 4000, 25);
  delay(300);
  noTone(BUZZER);
}

void updateCSV() {

  if (SD.exists(DATA_FILE))
    SD.remove(DATA_FILE);

  File file = SD.open(DATA_FILE, FILE_WRITE);
  if (!file)
  {
    Serial.println("Could not open or write to the file.");
    return;
  }
  else {
    Serial.println("Written!");
  }

  for (int i = 0; i < numCandidates; i++)
  {
    file.print(candidates[i].name);
    file.print(",");
    file.print(candidates[i].house);
    file.print(",");
    file.println(candidates[i].votes);
  }

  file.close();
}

void voteCurrentCandidate() {
  if (numCandidates > 0) {
    candidates[currentCandidateIndex].addVote();
    updateCSV();
    lcd.clear();
    lcd.print("Voted for");
    lcd.setCursor(0, 1);
    lcd.print(candidates[currentCandidateIndex].name);
    tone(BUZZER, 4000, 3000);
    delay(3000);
    noTone(BUZZER);
    lcd.clear();
    lcd.print("PLEASE LEAVE...");
    delay(4000);
    currentCandidateIndex = 0;
    displayCurrentCandidate();
  }
  delay(300);
}

void displayCurrentCandidate() {
  lcd.clear();
  if (numCandidates > 0) {
    lcd.print(candidates[currentCandidateIndex].name);
    lcd.setCursor(0, 1);
    lcd.print(candidates[currentCandidateIndex].house);
  }
  else
    lcd.print("No candidates");
}

void displayWinner() {
  if (numCandidates > 0)
  {
    int maxVotes = 0;
    int winnerIndex = 0;

    for (int i = 0; i < numCandidates; i++)
      if (candidates[i].votes > maxVotes)
      {
        maxVotes = candidates[i].votes;
        winnerIndex = i;
      }
    
    lcd.clear();
    lcd.print("Winner: ");
    lcd.print(candidates[winnerIndex].name);
    lcd.setCursor(0, 1);
    lcd.print("Votes: ");
    lcd.print(maxVotes);
  }
}
$abcdeabcde151015202530fghijfghij