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

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

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


//######### RFID CONSTANTS ##########
const int startZeichen = 02; // Chip-Kennung beginnt mit 02
const int endeZeichen = 03; // ASCII CR bendet Code
const int kennungLaenge   = 12; // Laenge Chip-Code 10 ASCII-Zeichen
const int gesamtKennungLaenge = 14; // Code-Laenge + Start- und Ende-Byte
char code[kennungLaenge + 1];  // fuer den Code und abschliessende Null
int bytesGelesen = 0;
int zaehlerFehlerCode = 0;
String zugang = "000000000000"; // zugangscode zwischenspeicher, Standard
//#######################


File myFile;
const int chipSelect = 53;

char leckmich[255];
Beerbox *box;

int drinksMax = 1;
int lastButtonState = 1;
int currentDrink = 0;


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

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

  //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);


  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");
  readAllPersons();

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


  lcd.print("BOXready");
  lcd.setCursor(0, 1);


}


void loop() {

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

  //Person myPerson = read_person(box, "p001");
  //read_person(box, "p002");



}//end loop

void checkButton() {
  //one change per click to make seleting drinks easier for the drunken
  //if (digitalRead(2) != lastButtonState) {
  //delayMicroseconds(50); //debounce delay
  if (digitalRead(2) != lastButtonState) {
    //ok we have a change!
    if (digitalRead(2) == LOW) {
      //pushed
      if (currentDrink == drinksMax - 1) currentDrink = -1;
      currentDrink++;
      Serial.println(box->drinks[currentDrink].name);
      lcd.clear();
      lcd.print("Drink:");
      lcd.setCursor(0, 1);
      lcd.print(box->drinks[currentDrink].name);

    }
    else {//released
    }
    lastButtonState = digitalRead(2);
  }
  //}
}

bool checkFinger() {

  uint8_t p = finger.getImage();
  if (p != FINGERPRINT_OK)  return -1;

  p = finger.image2Tz();
  if (p != FINGERPRINT_OK)  return -1;

  p = finger.fingerFastSearch();
  if (p != FINGERPRINT_OK)  return -1;

  // found a match!
  Serial.print("Found ID #"); Serial.print(finger.fingerID);
  Serial.print(" with confidence of "); Serial.println(finger.confidence);


  //TODO @ALEX Do something with finger ID
  // finger.fingerID

  //int getFingerIndex(Person* persons, finger.fingerID)

}

void checkRFID() {
  Serial1.flush();
  if (Serial1.available() >=  gesamtKennungLaenge) // wenn genug Zeichen eingegangen ...
  {
    if (Serial1.read() == startZeichen) // und das Startzeichen erkannt wurde ...
    {
      //Serial.print("Start-Byte erkannt ... ");
      bytesGelesen = 0; // starten Lesen, also Zaehler auf 0
      while (bytesGelesen < kennungLaenge) // lese 12-stelligen Code
      {
        char zeichen = Serial1.read(); // lesen Zeichen
        //Serial.print(zeichen); // zur Kontrolle
        //Serial.print(" ");
        if (zeichen == endeZeichen) // suche nach Ende-Befehl
        {
          //Serial.println("CR erkannt ...");
          break;
        }
        code[bytesGelesen] = zeichen; // speicher Zeichen im Puffer
        bytesGelesen = bytesGelesen + 1; // ein Zeichen weiter
      }
      code[bytesGelesen] = 0; // beende Zeichenkette
      Serial.print("RFID-Code lautet: ");
      Serial.println(code);
      zugang = code; // Umwandlung Code in String
      //TODO @Alex
      // Do something with the 12 char string code
      zugang = "";
      delay(1000); // debounce time
      serial1_flush_buffer();
    }
  }
}

void serial1_flush_buffer()
{
  while (Serial1.read() >= 0)
    ; // do nothing
}


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 personcount Basically not needed...
//  char tmpStr[19];
//  read_line_from_file(read, tmpStr, sizeof(tmpStr));
//  if (strlen(tmpStr)) {
//    sscanf(tmpStr, "personCount: %d", &box->personCount);
//  }
//
//  sprintf(leckmich, "%i persons known!\n", box->personCount );
//  Serial.print(leckmich);

  //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;
    }

  }
  Serial.print(i);
  Serial.println(" drinks read!\n");

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


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

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

  read.close();
  return box;

}

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

  Person aperson;

  File read;

  int num_read = 0;
  int index;
  int tmp_int = 0;

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

  int i = 0;


  //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);

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


  read_line_from_file(read, tmp, sizeof(tmp));
  strcpy(aperson.name, tmp);

  Serial.println("before memset");
  memset(aperson.rfid_uuid, 0, 13);
  read_line_from_file(read, tmp, sizeof(tmp));
  sscanf(tmp, " %*s %12s", aperson.rfid_uuid);
  //Serial.print(aperson.rfid_uuid);

  read_line_from_file(read, tmp, sizeof(tmp));
  sscanf(tmp, " %*s %d", &aperson.finger_uuid);
  //Serial.println(aperson.finger_uuid);

  while (read_line_from_file(read, tmp, sizeof(tmp)), strlen(tmp) > 0) {

    i = 0;
    sscanf(tmp, " %*s %s", tmp_drink_name);

    for (i = 0; (i < NUM_OF_DRINKS) && (strcmp(box->drinks[i].name, tmp_drink_name)); i++) {
      Serial.print("It´s not drink No :");
      Serial.print(i+1);
      Serial.println(box->drinks[i].name);

    }
    Serial.println("in");
    if (i == NUM_OF_DRINKS) {
      //Is it in?
      //Serial.print("Drink \"%s\" does not exist in box!\n", tmp_drink_name);
    } else {

      read_line_from_file(read, tmp, sizeof(tmp));
      sscanf(tmp, " %*s %d", &aperson.drinks_taken[i]);
      //Serial.print(">>Quantity: %d\n", aperson.drinks_taken[i].quantity);
      read_line_from_file(read, tmp, sizeof(tmp));
      //Serial.print(">>Total: %d\n\n", aperson.drinks_taken[i].total);
    }

  }

  //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(leckmich, "\nPerson is called \"%s\" and has the filename \"%s\".\n", aperson->name, aperson->file_name);
  Serial.write(leckmich);
  //sprintf(leckmich, "rfid_uuid:   %s\n", aperson->rfid_uuid);
  //Serial.write(leckmich);
  sprintf(leckmich, "finger_uuid: %u\n", aperson->finger_uuid);
  Serial.print(leckmich);


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

    if (aperson->drinks_taken[i] != -1) {

      sprintf(leckmich, "\nDrink: %s\n", box->drinks[i].name);
      Serial.write(leckmich);
      sprintf(leckmich, "Consumed: %d\n", aperson->drinks_taken[i]);
      Serial.write(leckmich);
      sprintf(leckmich, "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(leckmich);

    }

  }


}


int update_pers_file(Beerbox *box, Person *aperson) {
  File write;
  char *tmp = NULL;

  int i = 0;

  write = SD.open(aperson->file_name, FILE_WRITE);
  write.seek(0);

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

  }

  sprintf(leckmich, " %s\n", aperson->name);
  write.write(tmp, strlen(tmp));
  sprintf(leckmich, "rfid_uuid:   %s\n", aperson->rfid_uuid);
  write.write(tmp, strlen(tmp));
  sprintf(leckmich, "finger_uuid: %u\n", aperson->finger_uuid);
  write.write(tmp, strlen(tmp));

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

    if (aperson->drinks_taken[i] != -1) {

      sprintf(leckmich, "\nDrink: %s\n", box->drinks[i].name);
      write.write(tmp, strlen(tmp));
      sprintf(leckmich,  "Consumed: %d\n", aperson->drinks_taken[i]);
      write.write(tmp, strlen(tmp));
      sprintf(leckmich, "Total: %d\n\n", aperson->drinks_taken[i]*box->drinks[i].price);
      write.write(tmp, strlen(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;
}
//
//LB
//Funktion schreiben, welche ein Array vom struct Person (Beliebige Groesse) sowie eine rfid-kennung uebernimmt und den Index der entsprechenden Person im array (beginnend bei 0)zurueckgiebt.
int getRFIDIndex(Person* persons, char* rfid) {

  for (int i = 0; i < sizeof(persons) / sizeof(Person); i++) {
    if (!strcmp(persons[i].rfid_uuid, rfid)) {
      return i;
    }
  }

}

int getFingerIndex(Person* persons, uint8_t fingerID) {

  for (int i = 0; i < sizeof(persons) / sizeof(Person); i++) {
    if (persons[i].finger_uuid == fingerID) {
      return i;
    }
  }
}


void readAllPersons () {

  File dir = SD.open("/");
  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());
        //print_person(box, &thisPerson);
        
        
      }
    }
    entry.close();
  }

  Serial.println("Read all!");
}



