#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(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;
}