#include <SPI.h>
#include <Wire.h>
#include <SD.h>
#include "beerbox.h"
#include <LiquidCrystal_I2C.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <Adafruit_Fingerprint.h>

Adafruit_Fingerprint finger = Adafruit_Fingerprint(&Serial2);
LiquidCrystal_I2C lcd(0x38, 8, 2); // 0x38 for PCF***A on address 000




File myFile;
const int chipSelect = 53;

char toprint[255];
Beerbox *box;

int drinksMax = 1; // TODO add this to the box struct
int maxID = 0;
int lastButtonState = 1;

int currentDrink = 0;
int lastPersonIndex = -1;

Person* persons;
int personCount;
String rfID;



unsigned long oldTime = 0;
unsigned long ledTime = 0;


void setup() {
  //generall debugging
  Serial.begin(115200);

  //LCD init
  lcd.init();
  pinMode(2, INPUT_PULLUP);

  pinMode(3, OUTPUT);
  pinMode(led, OUTPUT);
  //tone(3,440,5000);

  //Fingerprint init
  finger.begin(57600);
  if (finger.verifyPassword()) {
    Serial.println("Found fingerprint sensor!");
  } else {
    Serial.println("Did not find fingerprint sensor :(");
    while (1);
  }

  // RFID Reader init
  Serial1.begin(9600);

  // BLE Com init
  Serial3.begin(115200);


  Serial.print("Initializing SD card...");
  // On the Ethernet Shield, CS is pin 4. It's set as an output by default.
  // Note that even if it's not used as the CS pin, the hardware SS pin
  // (10 on most Arduino boards, 53 on the Mega) must be left as an output
  // or the SD library functions will not work.
  pinMode(SS, OUTPUT);

  if (!SD.begin(chipSelect)) {
    Serial.println("initialization failed!");
    return;
  }
  Serial.println("initialization done.");

  //check_for_file("p001", "per");

  box = new Beerbox;
  box = read_beerbox(box);

  persons = readAllPersons();

  lcd.print("Drink: _");
  lcd.setCursor(0, 1);
  lcd.print(box->drinks[0].name);
  currentDrink = 0;
  box->waiting = false;

  //turnOn led
  digitalWrite(led,HIGH);
  ledTime = millis();

}


void loop() {

  checkFinger();
  checkRFID();
  checkButton();

  boxTimer();
  ledTimer();




  if (Serial3.available() > 0) {
    char incomingByte = Serial3.read();
    if (incomingByte == '!') { //2 createnew user
      incomingByte = Serial3.read();
      String personName;
      while (incomingByte != ';')
      {
        personName =  String(personName + incomingByte);
        incomingByte = Serial3.read();
      }
      Serial.println(personName);
      lcd.clear();
      lcd.print(personName);
      Serial.println(maxID);
      //lcd.setCursor(0,1);
      //lcd.print("Fin pls");
      int fingerReturn = -1;
      while (fingerReturn != 0)
      {
        fingerReturn = getFingerprintEnroll(maxID + 1);
      }
      lcd.clear();
      lcd.print(personName);
      lcd.setCursor(0, 1);
      lcd.print("RFID pls");

      zugang = "";
      while (zugang == "") {
        getRFIDEnroll();
      }
      Serial.println(zugang);
      maxID++;
      Person* newPerson = new Person;
      personName.toCharArray(newPerson->name, personName.length()+1);
      char tmp[FILE_NAME_LEN + 1];
      sprintf(tmp,"p%03d%s",maxID,FILE_EXTENSION);
      strcpy(newPerson->file_name, tmp);
      zugang.toCharArray(newPerson->rfid_uuid, zugang.length()+1);
      newPerson->finger_uuid = maxID;
      newPerson->credits_left = 1000;
      for (int i = 0; i < drinksMax; i++) {
       newPerson->drinks_taken[i]=0;
      }
      write_pers_file(newPerson);


      zugang = ""; // reset here
      lcd.clear();
      lcd.print(personName);
      lcd.setCursor(0, 1);
      lcd.print("Sucess!");
      delay(1000);
      //and revert to normal:
      lcd.clear();
      lcd.print("Drink:");
      lcd.setCursor(0, 1);
      lcd.print(box->drinks[currentDrink].name);

    }
  }

}//end loop




void check_for_file(String filename, String extension) {
  String str = filename + '.' + extension;
  char* buf = NULL;
  str.toCharArray(buf, str.length() + 1);

  if (SD.exists("p001.per")) {
    Serial.println(str + " exists.");
  }
  else {
    Serial.println(str + " doesn't exist.");
  }
}

//BEYOND THIS POINT IS THE LEYER OF BEERBOX -- ENTER AT YOUR OWN RISK

char tmp_filename[FILE_NAME_LEN + FILE_EXTENSION_LEN + 2];

Beerbox* read_beerbox(Beerbox *box) {

  File read;
  int num_read = 0;
  int index;

  char tmp[(DRINK_NAME_MAX_LENGTH + 1) * NUM_OF_DRINKS];

  int i = 0;


  read = SD.open("bb.con", FILE_READ);
  if (read == false) {

    Serial.print("error while reading!\n");
    Serial.print("-ABORT!\n\n");
    while (1);

  }

  //get drinks
  for (i = 0 ; i < NUM_OF_DRINKS ; i++) {

    read_line_from_file(read, tmp, sizeof(tmp));

    if (strlen(tmp)) {
      sscanf(tmp, " drink number %d: %s", &index, &box->drinks[i].name);

      read_line_from_file(read, tmp, sizeof(tmp));
      sscanf(tmp, " %*s %d ", &box->drinks[i].price);

    } else {
      strcpy(box->drinks[i].name, "slot empty");
      box->drinks[i].price = -1;
    }

  }

  for (i = 0; (i < NUM_OF_DRINKS) && (box->drinks[i].price != -1); i++) {

    sprintf(toprint, "drink number %i: %s\nprice: %d\n", i , box->drinks[i].name, box->drinks[i].price);
    Serial.println(toprint);
  }

  if (i < NUM_OF_DRINKS) {
    sprintf(toprint, "Slots %d - %d are not in use!\n", i + 1, NUM_OF_DRINKS);
    drinksMax = i;
    Serial.println(toprint);
  }

  read.close();
  return box;

}

Person read_person(Beerbox *box, char *filename) {

  Person aperson;
  File read;

  char tmp[25];//longest line is rfid_uuid


  //giving the full filename here, since it makes stuff easier
  //Serial.print("The filename is: ");
  //Serial.println(filename);
  read = SD.open(filename, FILE_READ);
  if (read == false) {

    Serial.print("error while reading!\n");
    Serial.print("ABORT!\n\n");
    while (-1);

  }

  strcpy(aperson.file_name, filename);

  //reset momentary array
  for (int i = 0; i < NUM_OF_DRINKS; i++) {
    aperson.drinks_taken[i] = -1;
  }

  //First read all the metadata
  //name
  read_line_from_file(read, tmp, sizeof(tmp));
  strcpy(aperson.name, tmp);
  //rfid_uuid
  memset(aperson.rfid_uuid, 0, 13);
  read_line_from_file(read, tmp, sizeof(tmp));
  sscanf(tmp, "rfid_uuid: %12s", &aperson.rfid_uuid);
  Serial.println(aperson.rfid_uuid);
  read_line_from_file(read, tmp, sizeof(tmp));
  sscanf(tmp, "finger_uuid: %d", &aperson.finger_uuid);
  //Serial.println(aperson.finger_uuid);
  //credits_left
  read_line_from_file(read, tmp, sizeof(tmp));
  sscanf(tmp, "credits_left: %d", &aperson.credits_left);
  //Serial.println(aperson.credits_left);

  while (read_line_from_file(read, tmp, sizeof(tmp)), strlen(tmp) > 0) {
    int tmpCount = 0;
    int tmpDrink = 0;
    sscanf(tmp, "drink_Count_%d: %d", &tmpDrink, &tmpCount);
    if (strlen(tmp)) {
      //Serial.print("drink: ");
      //Serial.print(tmpDrink);
      //Serial.print(" count: ");
      //Serial.println(tmpCount);
      aperson.drinks_taken[tmpDrink] = tmpCount;
    }
  }


  print_person(box, &aperson);
  //comment back in once it is fixed
  //update_pers_file(box, &aperson);

  read.close();
  return aperson;
}

void print_person(Beerbox *box, Person *aperson) {

  int i = 0;
  sprintf(toprint, "\nPerson is called \"%s\" and has the filename \"%s\".\n", aperson->name, aperson->file_name);
  Serial.write(toprint);
  sprintf(toprint, "rfid_uuid: %s\n", aperson->rfid_uuid);
  Serial.write(toprint);
  sprintf(toprint, "finger_uuid: %u\n", aperson->finger_uuid);
  if (aperson->finger_uuid > maxID) maxID = aperson->finger_uuid;
  Serial.print(toprint);
  sprintf(toprint, "credits_left: %u\n", aperson->credits_left);
  Serial.print(toprint);

  for (i = 0; i < drinksMax; i++) {

    if (aperson->drinks_taken[i] != -1) {
      sprintf(toprint, "\nDrink: %s\n", box->drinks[i].name);
      Serial.write(toprint);
      sprintf(toprint, "Drink %d: %d\n", i, aperson->drinks_taken[i]);
      Serial.write(toprint);
      sprintf(toprint, "Total: %d.%02d Euro\n", aperson->drinks_taken[i]*box->drinks[i].price / 100, (aperson->drinks_taken[i]*box->drinks[i].price) % 100);
      Serial.write(toprint);
    }

  }


}


int update_pers_file() {
  Person thisPerson = persons[lastPersonIndex];
  File write;
  char tmp[25];

  int i = 0;
  //Serial.println(thisPerson.file_name);
  write = SD.open(thisPerson.file_name, FILE_WRITE);

  if (write == NULL) {
    Serial.print("error while reading!\n");
    Serial.print("ABORT!\n\n");
    while (-1);
  }
  write.seek(0);


  sprintf(tmp, "%s\n", thisPerson.name);
  write.write(tmp, strlen(tmp));
  sprintf(tmp, "rfid_uuid: %s\n", thisPerson.rfid_uuid);
  write.write(tmp, strlen(tmp));
  sprintf(tmp, "finger_uuid: %u\n", thisPerson.finger_uuid);
  write.write(tmp, strlen(tmp));
  sprintf(tmp, "credits_left: %u\n", thisPerson.credits_left);
  write.write(tmp, strlen(tmp));

  for (i = 0; i < drinksMax; i++) {

    if (thisPerson.drinks_taken[i] != -1) {
      sprintf(tmp, "\ndrink_Count_%d: %d\n", i, thisPerson.drinks_taken[i]);
      write.write(tmp, strlen(tmp));
      Serial.println(tmp);
    }

  }


  write.close();

  return 0;

}


char* read_from_file_until(File stream, char *str, int max_len, char until) {

  int i = 0;
  int tmp_int = 0;

  memset(str, 0, max_len);

  for (i = 0; (tmp_int != until) && (i < (max_len - 1)) && (tmp_int != -1); i++) {
    tmp_int = stream.read();
    //Serial.print("%c", tmp_int);
    str[i] = tmp_int;
  }


  if (tmp_int == -1) {

    //May occur more often as the function tries to read a certain number of drinks.
    //Serial.print("END OF FILE REACHED!\nABORT!!\n\n");

    str[0] = 0;
    return str;
  }

  if (str[0] != '\n')str[i - 1] = 0;


  return str;
}

char *read_line_from_file(File stream, char *str, int max_len) {

  do {
    str = read_from_file_until(stream, str, max_len, '\n');
  } while ((str[0] == '\n'));


  return str;
}



Person* readAllPersons () {

  File dir = SD.open("/");
  dir.rewindDirectory();

  personCount = 0;
  //count the valid persons:
  while (true) {
    File entry =  dir.openNextFile();
    if (! entry) {// no more files
      break;
    }
    if (!entry.isDirectory()) {
      if (strcmp(strlwr(entry.name() + (strlen(entry.name()) - 4)), ".per")==0) 
      {
        personCount++; //Yeah it is a person!
      }
      
      
    }
    entry.close();
  }
  //now store them
  //Serial.print("count:");
  //Serial.print(personCount);
  Person* readPersons = new Person[personCount];
  int counter = 0;
  dir.rewindDirectory();
  while (true) {
    File entry =  dir.openNextFile();
    if (! entry) {// no more files
      break;
    }
    if (!entry.isDirectory()) {
      int8_t len = strlen(entry.name());
      if (strstr(strlwr(entry.name() + (len - 4)), ".per"))
      {
        //Yeah it is a person!
        //Serial.print("Person: ");
        //Serial.println(entry.name());
        Person thisPerson = read_person(box, entry.name());
        readPersons[counter] = thisPerson;
        counter++;
      }
    }
    entry.close();
  }
  dir.close();
  Serial.println("Read all!");
  return readPersons;
}




