#include <EEPROM.h>
#include <LiquidCrystal_I2C.h>
#include "Bounce.h"
#include "Wire.h"

//#################Definicion pin arduino##################
#define ENTER	2
#define UP		3
#define DOWN	4
#define LEFT	5
#define RIGHT	6
#define START	7
#define CANCEL	8
#define RELE1	11
#define RELE2	10
#define LED 	13

//###################Definicion estados###################

#define E_INICIAL         0
#define E_CONFIGTIEMPO    1
#define E_CUENTA_ATRAS    2
#define E_MENU            3
#define E_MENU_GUARDAR    4
#define E_GUARDAR_MEMORIA 5
#define E_MENU_CARGAR     6
#define E_CARGAR_MEMORIA  7
#define E_MENU_CARA       8
#define E_CARA_GUARDAR    9
#define E_MENU_CANCEL     10

#define INTERVAL_MILLIS 5
#define REBOUNCE_TIME 250

//###################Declaracion objetos##################

Bounce buttonStart = Bounce(START, INTERVAL_MILLIS);
Bounce buttonCancel = Bounce(CANCEL, INTERVAL_MILLIS);
Bounce buttonUp = Bounce(UP, INTERVAL_MILLIS);
Bounce buttonDown = Bounce(DOWN, INTERVAL_MILLIS);
Bounce buttonLeft = Bounce(LEFT, INTERVAL_MILLIS);
Bounce buttonRight = Bounce(RIGHT, INTERVAL_MILLIS);
Bounce buttonEnter = Bounce(ENTER, INTERVAL_MILLIS);

LiquidCrystal_I2C lcd(0x38, 16, 2);

//##################Declaracion Variables#################
uint8_t state = E_INICIAL;

struct tiempo {
  uint8_t horas;
  uint8_t minutos;
  uint8_t segundos;
  uint8_t decimas;
} cuenta = {0, 0, 0, 0};

int posicionCursor = 9;

long milisegundos;

//Para añadir mas funciones al menu se deben añadir en el array y sumar
//mas uno la variable que contabiliza cuantas entradas hay en un menu
char* menuPrincipal[] = {"Guardar", "Cargar", "Doble Cara"};
char* menuMemoria[] = {"Memoria 1", "Memoria 2", "Memoria 3"};
char* menuDcara[] = {"Inferior", "Doble Cara", "Superior"};
int posMenu = 0;
int numMenuPrincipal = 2;
int numMenuMemoria = 2;
int numMenuDcara = 2;

//Rele 0 significa activar luz inferior
//Rele 1 significa activar ambas luces
//Rele 2 significa activar luz superior
int reles = 0;

//##########################SETUP#########################
void setup() {
  pinMode(RELE1, OUTPUT);
  digitalWrite(RELE1, LOW);
  pinMode(RELE2, OUTPUT);
  digitalWrite(RELE2, LOW);

  for (int i = 2; i < 9; i++) {
    pinMode(i, INPUT_PULLUP);
  }

  lcd.init();
}
//##########################LOOP##########################
void loop() {
	switch(state){
    case E_INICIAL:{
      imprimirTiempo();
      lcd.cursor();
      lcd.setCursor(9,0);
      state=E_CONFIGTIEMPO;
    }break;
		case E_CONFIGTIEMPO:{
			configuraTiempo();
			if(ev_start()){
        limpiaLCD();
				activaReles();
				state=E_CUENTA_ATRAS;
			}
      if(ev_enter()){
        limpiaLCD();
        state=E_MENU;
      }
			if(ev_cancel()){
				resetTiempo();
        desactivaReles();
				state=E_INICIAL;
			}
		}break;
		case E_CUENTA_ATRAS:{
			if(!ev_fin_cuenta_atras() && !ev_cancel()){
				cuentaAtras();
			}else{
				desactivaReles();
				resetTiempo();
				state = E_INICIAL;
			}
		}break;
		case E_MENU:{
		  imprimirMenu(menuPrincipal);
			desplazarMenu(numMenuPrincipal);
			if(ev_enter()){
        state=interactuarMenu();
        limpiaMenu();
      }
			if(ev_cancel()){
        limpiaLCD();
        limpiaMenu();
				state=E_INICIAL;
			}
		}break;
    case E_MENU_GUARDAR:{
      imprimirMenu(menuMemoria);
      //desplazarMenu(sizeof(menuMemoria));
      desplazarMenu(numMenuMemoria);
      if(ev_enter()){
        limpiaLCD();
        state=E_GUARDAR_MEMORIA;
      }
      if(ev_cancel()){
        state=E_MENU_CANCEL;
      }
    }break;
    case E_GUARDAR_MEMORIA:{
      guardarMemoria();
      limpiaMenu();
      state=E_INICIAL;
    }break;
    case E_MENU_CARGAR:{
       imprimirMenu(menuMemoria);
       desplazarMenu(numMenuMemoria);
       if(ev_enter()){
        limpiaLCD();
        state=E_CARGAR_MEMORIA;
      }
      if(ev_cancel()){
		    state=E_MENU_CANCEL;
      }
    }break;
    case E_CARGAR_MEMORIA:{
      cargarMemoria();
      limpiaMenu();
      state=E_INICIAL;
    }break;
	  case E_MENU_CARA:{
		  imprimirMenu(menuDcara);
		  desplazarMenu(numMenuDcara);
		  if(ev_enter()){
			  limpiaLCD();
			  state=E_CARA_GUARDAR;
		  }
		  if(ev_cancel()){
	  		state=E_MENU_CANCEL;
		  }
    }break;
    case E_CARA_GUARDAR:{
		  caraGuardar();
		  limpiaMenu();
		  state=E_INICIAL;
		}break;
    case E_MENU_CANCEL:{
      limpiaLCD();
      limpiaMenu();
      state=E_MENU;
    }break;
  }
}

//#######################Funciones########################
void imprimirTiempo() {
  lcd.setCursor(3, 0);
  if (cuenta.horas < 10) {
    lcd.print("0");
  }
  lcd.print(":");
  if (cuenta.minutos < 10) {
    lcd.print("0");
  }
  lcd.print(cuenta.minutos);
  lcd.print(":");
  if (cuenta.segundos < 10) {
    lcd.print("0");
  }
  lcd.print(cuenta.segundos);
  lcd.print(":");
  lcd.print(cuenta.decimas);
}
void limpiaLCD() {
  lcd.clear();
  lcd.noCursor();
}
void configuraTiempo() {
  if (ev_left() && posicionCursor > 5) {
    if (posicionCursor == 8)posicionCursor--; //Me salto la posicion de los :
    posicionCursor--;
    lcd.setCursor(posicionCursor, 0);
  }
  if (ev_right() && posicionCursor < 9) {
    if (posicionCursor == 6)posicionCursor++; //Me salto la posicion de los :
    posicionCursor++;
    lcd.setCursor(posicionCursor, 0);
  }
  if (ev_up()) {
    if (posicionCursor == 9 && cuenta.segundos < 59) {
      cuenta.segundos++;
    }
    if (posicionCursor == 8 && cuenta.segundos < 50) {
      cuenta.segundos += 10;
    }
    if (posicionCursor == 6 && cuenta.minutos < 59) {
      cuenta.minutos++;
    }
    if (posicionCursor == 5 && cuenta.minutos < 50) {
      cuenta.minutos += 10;
    }
    imprimirTiempo();
    lcd.setCursor(posicionCursor, 0); //Necesario?
  }
  if (ev_down()) {
    if (posicionCursor == 9 && cuenta.segundos > 0) {
      cuenta.segundos--;
    }
    if (posicionCursor == 8 && cuenta.segundos > 9) {
      cuenta.segundos -= 10;
    }
    if (posicionCursor == 6 && cuenta.minutos > 0) {
      cuenta.minutos--;
    }
    if (posicionCursor == 5 && cuenta.minutos > 9) {
      cuenta.minutos -= 10;
    }
    imprimirTiempo();
    lcd.setCursor(posicionCursor, 0);
  }
}
void resetTiempo() {
  cuenta.horas = 0;
  cuenta.minutos = 0;
  cuenta.segundos = 0;
  cuenta.decimas = 0;
  posicionCursor = 9;
}
void cuentaAtras() {
  int decimasTotales = (((cuenta.minutos * 60) + cuenta.segundos) * 10) + cuenta.decimas;
  milisegundos = millis();

  if (milisegundos % 100 == 0) {
    decimasTotales--;
    cuenta.decimas = decimasTotales % 10;
    cuenta.segundos = ((decimasTotales - cuenta.decimas) / 10) % 60;
    cuenta.minutos = ((decimasTotales - cuenta.decimas) / 10) / 60;

    imprimirTiempo();
  }
}
void activaReles() {
  EEPROM.get(48,reles);

  switch (reles) {
    case 0: {
        digitalWrite(RELE1, HIGH);
      } break;
    case 1: {
        digitalWrite(RELE1, HIGH);
        digitalWrite(RELE2, HIGH);
      } break;
    case 2: {
        digitalWrite(RELE2, HIGH);
      } break;
  }
}
void desactivaReles() {
  digitalWrite(RELE1, LOW);
  digitalWrite(RELE2, LOW);
}

void imprimirMenu(char* menu[]) {
  if (posMenu == 0) {
    lcd.setCursor(0, 0);
    lcd.print("*");
    lcd.print(menu[posMenu]);
    lcd.setCursor(0, 1);
    lcd.print(" ");
    lcd.print(menu[posMenu + 1]);
  } else {
    lcd.setCursor(1, 0);
    lcd.print(menu[posMenu - 1]);
    lcd.setCursor(1, 1);
    lcd.print(menu[posMenu]);
  }
}
void desplazarMenu(int numMenu) {
  if (ev_down() && posMenu < numMenu) {
    lcd.clear();
    lcd.setCursor(0, 1);
    lcd.print("*");
    posMenu++;
  }
  if (ev_up() && posMenu > 0) {
    lcd.clear();
    lcd.setCursor(0, 1);
    lcd.print("*");
    posMenu--;
  }
}
int interactuarMenu() {
  switch (posMenu){
    case 0:{
      return E_MENU_GUARDAR;
    }break;
    case 1:{
      return E_MENU_CARGAR;
    }break;
    case 2:{
      return E_MENU_CARA;
    }break;
  }
}
void limpiaMenu() {
  posMenu = 0;
}
void guardarMemoria() {
  int segundosTotales = ((cuenta.minutos * 60) + cuenta.segundos);

  EEPROM.put(posMenu*16,segundosTotales);
}
void cargarMemoria() {
  int segundosTotales;

  EEPROM.get(posMenu*16, segundosTotales);

  cuenta.segundos = segundosTotales % 60;
  cuenta.minutos = segundosTotales / 60;
}
void caraGuardar(){
  //8 bits que tiene un byte
  //2 bytes para guardar la variable segundos que es tipo int
  //3 variables segundos a guardar correspondientes a Memoria 1, Memoria 2 y Memoria 3
  //EEPROM.put(8*2*3, reles);
	EEPROM.put(48,posMenu);
}
//###################Funciones eventos####################
boolean ev_start() {
  if (buttonStart.update()) {
    if (buttonStart.read() == LOW) {
      return true;
    }
  }
  return false;
}
boolean ev_cancel() {
  if (buttonCancel.update()) {
    if (buttonCancel.read() == LOW) {
      return true;
    }
  }
  return false;
}
boolean ev_enter() {
  if (buttonEnter.update()) {
    if (buttonEnter.read() == LOW) {
      return true;
    }
  }
  return false;
}
boolean ev_up() {
  if (buttonUp.update()) {
    if (buttonUp.read() == LOW) {
      return true;
    }
  }
  return false;
}
boolean ev_down() {
  if (buttonDown.update()) {
    if (buttonDown.read() == LOW) {
      return true;
    }
  }
  return false;
}
boolean ev_left() {
  if (buttonLeft.update()) {
    if (buttonLeft.read() == LOW) {
      return true;
    }
  }
  return false;
}
boolean ev_right() {
  if (buttonRight.update()) {
    if (buttonRight.read() == LOW) {
      return true;
    }
  }
  return false;
}
boolean ev_fin_cuenta_atras() {
  if (cuenta.minutos == 0 && cuenta.segundos == 0 && cuenta.decimas == 0) {
    return true;
  }
  return false;
}
