/*****************************************
*   Dosificador_LCD2004_4buttons         *
*   15/03/2021                           *
*   Versión 0.1.0                        *
*   koneko.mx                            *
*****************************************/

#include "Timer_lite.h"
#include <LiquidCrystal_I2C.h>

#define arr_len(x) (sizeof(x)/sizeof(*x))

/**************************************************
* PINOUT / MEMORY ADDRESS
**************************************************/
	#define LCD_I2C_ADDRESS		0x27
	#define RELAY_PIN			   4
	#define SETTINGS_BUTTONS_PIN  A3

/**************************************************
* BOTONES
**************************************************/
	#define SETTING_BUTTON_IN_VAL   16
	#define SETTING_BUTTON_OUT_VAL  91
	#define SETTING_BUTTON_UP_VAL   41
	#define SETTING_BUTTON_DOWN_VAL 69

	#define SETTING_BUTTON_TOLERANCE_READ				 8
	#define SETTING_BUTTON_CONFIRMATION_REPEATS_MILLIS	1
	#define SETTING_BUTTON_DELAY_MILLIS				   350

	// 4-button configuration 
	uint8_t  button_values[] = {SETTING_BUTTON_IN_VAL, SETTING_BUTTON_OUT_VAL, SETTING_BUTTON_UP_VAL, SETTING_BUTTON_DOWN_VAL};
	uint32_t _bt[sizeof(button_values)]; // millis
	uint32_t confirmation_repeats_millis;

/**************************************************
* MENÚ
**************************************************/
	#define ALL_SETTINGS 0

	#define VIEW   0
	#define UPDATE 1

	#define POSITION_ONE   0
	#define POSITION_TWO   1
	#define POSITION_THREE 2
	#define POSITION_FOUR  3
	#define POSITION_FIVE  4
	#define POSITION_SIX   5

	#define YEARS_DATETIME 2020

	#define AUTO_EXIT_MENU_MILLIS 15000

	// MENÚ
	#define OUT_OF_MENU  		   -1
	#define MENU_SALIR 				0
	#define MENU_T_ESPERA_INICIAL	1
	#define MENU_T_ARRANQUE			2
	#define MENU_T_ARRANQUE_ESPERA 	3
	#define MENU_T_ENCENDIDO 		4
	#define MENU_T_ENCENDIDO_ESPERA 5
	#define MENU_FECHA_Y_HORA       6
	#define MENU_DETENER_PROCESO 	7
	#define MENU_REANUDAR_PROCESO 	8
	#define MENU_REINICIAR_EQUIPO 	9
	#define MENU_VALORES_DE_FABRICA 10

	#define DETENER		0
	#define REANUDAR	1
	#define REINICIAR	2
	#define RESTAURAR	3
	#define CANCELAR	4
 
	// Menú navegación
	int8_t menu_section;
	uint8_t menu_edition;
	uint8_t menu_cursor_var;

	uint8_t setup_var[6];

	uint32_t last_change_menu_millis;

/**************************************************
* LCD
**************************************************/
	#define BLINK_MILLIS				1600
	#define LCD_PROCESS_REFRESH_MILLIS   250

	// LCD2004
	LiquidCrystal_I2C lcd(LCD_I2C_ADDRESS, 2, 1, 0, 4, 5, 6, 7, 3, POSITIVE);

	String menu[] = {
		"SALIR",
		"T. ESPERA INICIAL",
		"T. ARRANQUE",
		"T. ESPERA ARRANQUE",
		"T. ENCENDIDO",
		"T. ESPERA ENCENDIDO",
		"CAMBIAR FECHA Y HORA",
		"DETENER PROCESO",
		"REANUDAR PROCESO",
		"REINICIAR EQUIPO",
		"VALORES DE FABRICA",
	};

	String timer_text[] = {
		"DETENIDO",
		"INICIARA EN",
		"ARRANQUE",
		"ESPERA A.",
		"ENCENDIDO",
		"ESPERA E.",
	};

	String text_info[] = {
		"DETENER",
		"REANUDAR",
		"REINICIAR",
		"RESTAURAR",
		"CANCELAR",
	};

	boolean  refresh_lcd_menu;
	uint32_t blink_millis;

/***************************************************
* Timer_lite
***************************************************/
	#define TIMER_FIRST_EEPROM_ADREESS		0

	#define TIEMPO_INICIO_ESPERA_DEFAULT	60
	#define TIEMPO_ARRRANQUE_DEFAULT		10
	#define TIEMPO_ARRRANQUE_ESPERA_DEFAULT	20
	#define TIEMPO_ENCENDIDO_DEFAULT		10
	#define TIEMPO_ENCENDIDO_ESPERA_DEFAULT	10
	#define PROCESO_DETENIDO_DEFAULT		0

	uint8_t Timer_lite::_instancias_num;
	RTC_DS3231 Timer_lite::_RTC;
	DateTime Timer_lite::_now;

	Timer_lite myTimer_lite(RELAY_PIN, TIMER_FIRST_EEPROM_ADREESS);

/**************************************************
* Define DEBUG_SERIAL_ENABLE to enable debug serial.
* Comment it to disable debug serial.
**************************************************/
	  #define DEBUG_SERIAL_ENABLE

    #define dbSerial Serial

    #ifdef DEBUG_SERIAL_ENABLE
        #define serialPrint(a)    dbSerial.print(a)
        #define serialPrintln(a)  dbSerial.println(a)
        #define serialBegin(a)    dbSerial.begin(a)
        #define serialBeginWhile(a) while(!dbSerial) delay(1)
    #else
        #define serialPrint(a)    do{}while(0)
        #define serialPrintln(a)  do{}while(0)
        #define serialBegin(a)    do{}while(0)
        #define serialBeginWhile(a) do{}while(0)
    #endif

/**************************************************
* FRONT PANEL OF CONFIGURATIONS
**************************************************/
	// button methods and memory spaces
	void loop_panel_buttons()
	{
		// Leemos valor analogo del botón
		uint16_t button_value = analogRead(SETTINGS_BUTTONS_PIN);

		// Comprobamos el valor obtenido con un rago de tolerancia SETTING_BUTTON_TOLERANCE_READ
		for (uint8_t i = 0; i < sizeof button_values / sizeof button_values[0]; i++) {
			if(button_values[i] >= (button_value - SETTING_BUTTON_TOLERANCE_READ) && button_values[i] <= (button_value + SETTING_BUTTON_TOLERANCE_READ))
				button_value = button_values[i];
		}

    serialPrintln(button_value);

		//Valoramos si se presiono un botón y ejecutamos el metodo correspondiente
		switch(button_value) {
			case SETTING_BUTTON_IN_VAL:
				if(millis() >= _bt[0] + SETTING_BUTTON_DELAY_MILLIS) {
					if(confirmation_repeats_millis == 0)
						confirmation_repeats_millis = millis() + SETTING_BUTTON_CONFIRMATION_REPEATS_MILLIS;

					if(millis() > confirmation_repeats_millis){
						action_button(SETTING_BUTTON_IN_VAL);

						_bt[0] = millis();
					}
				}
				break;

			case SETTING_BUTTON_OUT_VAL:
				if(millis() >= _bt[1] + SETTING_BUTTON_DELAY_MILLIS) {
					if(confirmation_repeats_millis == 0)
						confirmation_repeats_millis = millis() + SETTING_BUTTON_CONFIRMATION_REPEATS_MILLIS;

					if(millis() > confirmation_repeats_millis){
						action_button(SETTING_BUTTON_OUT_VAL);

						_bt[1] = millis();
					}
				}
				break;

			case SETTING_BUTTON_UP_VAL:
				if(millis() >= _bt[2] + SETTING_BUTTON_DELAY_MILLIS) {
					if(confirmation_repeats_millis == 0)
						confirmation_repeats_millis = millis() + SETTING_BUTTON_CONFIRMATION_REPEATS_MILLIS;

					if(millis() > confirmation_repeats_millis){
						action_button(SETTING_BUTTON_UP_VAL);

						_bt[2] = millis();
					}
				}
				break;

			case SETTING_BUTTON_DOWN_VAL:
				if(millis() >= _bt[3] + SETTING_BUTTON_DELAY_MILLIS) {
					if(confirmation_repeats_millis == 0)
						confirmation_repeats_millis = millis() + SETTING_BUTTON_CONFIRMATION_REPEATS_MILLIS;

					if(millis() > confirmation_repeats_millis){
						action_button(SETTING_BUTTON_DOWN_VAL);

						_bt[3] = millis();
					}
				}
				break;

			default:
				confirmation_repeats_millis = 0;

				_bt[0] = 0;
				_bt[1] = 0;
				_bt[2] = 0;
				_bt[3] = 0;
		}

		// Verificamos y salimos del menú si a pasan AUTO_EXIT_MENU_MILLIS
		if((menu_section != OUT_OF_MENU) && (millis() >= last_change_menu_millis + AUTO_EXIT_MENU_MILLIS)){
			load_menu_variables(OUT_OF_MENU);

			refresh_lcd_menu = true;

			serialPrintln("AUTO_EXIT_MENU_MILLIS");
		}
	}

	void action_button(uint8_t button_value)
	{
		switch(button_value) {
			case SETTING_BUTTON_IN_VAL:
				switch(menu_section) {
					case OUT_OF_MENU:
						load_menu_variables(1);
						break;

					case MENU_DETENER_PROCESO:
					case MENU_REANUDAR_PROCESO:
					case MENU_REINICIAR_EQUIPO:
					case MENU_VALORES_DE_FABRICA:
						if(menu_edition == VIEW)
							menu_edition = UPDATE;

						else if(++menu_cursor_var > POSITION_ONE)
							save_setting(menu_section);
						break;


					case MENU_T_ESPERA_INICIAL:
						if(menu_edition == VIEW)
							menu_edition = UPDATE;

						else if(++menu_cursor_var > POSITION_TWO)
							save_setting(menu_section);
						break;


					case MENU_T_ARRANQUE_ESPERA:
					case MENU_T_ARRANQUE:
					case MENU_T_ENCENDIDO_ESPERA:
					case MENU_T_ENCENDIDO:
						if(menu_edition == VIEW)
							menu_edition = UPDATE;

						else if(++menu_cursor_var > POSITION_THREE)
							save_setting(menu_section);
						break;

					case MENU_FECHA_Y_HORA:
						if(menu_edition == VIEW)
							menu_edition = UPDATE;

						else if(++menu_cursor_var > POSITION_SIX)
							save_setting(menu_section);
						break;

					case MENU_SALIR:
						load_menu_variables(OUT_OF_MENU);
						break;
				}
				break;

			case SETTING_BUTTON_OUT_VAL:
				switch(menu_edition){
					case VIEW:
						load_menu_variables(OUT_OF_MENU);
						break;

					case UPDATE:
						if(menu_cursor_var == 0)
							load_menu_variables(menu_section);
						else
							menu_cursor_var--;
				}
				break;

			case SETTING_BUTTON_UP_VAL:
				switch(menu_section) {
					case OUT_OF_MENU:
						load_menu_variables(arr_len(menu) -1);
						break;

					case MENU_T_ESPERA_INICIAL:
						if(menu_edition == VIEW){
							load_menu_variables(MENU_SALIR);

						}else{// UPDATE
							if(menu_cursor_var == POSITION_ONE)
								setup_var[0] = setup_var[0] >= 60 ? 0 : ++setup_var[0]; // Minutos

							else if(menu_cursor_var == POSITION_TWO)
								setup_var[1] = setup_var[1] >= 59 ? 0 : ++setup_var[1]; // Segundos
						}
						break;

					case MENU_T_ARRANQUE_ESPERA:
					case MENU_T_ARRANQUE:
					case MENU_T_ENCENDIDO_ESPERA:
					case MENU_T_ENCENDIDO:
						if(menu_edition == VIEW){
							load_menu_variables(menu_section -1);

						}else{// UPDATE
							if(menu_cursor_var == POSITION_ONE)
								setup_var[0] = setup_var[0] >= 23 ? 0 : ++setup_var[0]; // Horas

							else if(menu_cursor_var == POSITION_TWO)
								setup_var[1] = setup_var[1] >= 59 ? 0 : ++setup_var[1]; // Minutos

							else if(menu_cursor_var == POSITION_THREE)
								setup_var[2] = setup_var[2] >= 59 ? 0 : ++setup_var[2]; // Segundos
						} 
						break;

					case MENU_FECHA_Y_HORA:
						if(menu_edition == VIEW){
							load_menu_variables(menu_section -1);

						}else{ // UPDATE
							if(menu_cursor_var == POSITION_ONE)
								setup_var[0] = setup_var[0] >= 23 ? 1 : ++setup_var[0]; // Horas

							else if(menu_cursor_var == POSITION_TWO)
								setup_var[1] = setup_var[1] >= 59 ? 0 : ++setup_var[1]; // Minutos

							else if(menu_cursor_var == POSITION_THREE)
								setup_var[2] = setup_var[2] >= 59 ? 0 : ++setup_var[2]; // Segundo

							else if(menu_cursor_var == POSITION_FOUR)
								setup_var[3] = setup_var[3] >= 31 ? 0 : ++setup_var[3]; // Día

							else if(menu_cursor_var == POSITION_FIVE)
								setup_var[4] = setup_var[4] >= 12 ? 0 : ++setup_var[4]; // Mes

							else if(menu_cursor_var == POSITION_SIX)
								setup_var[5] = setup_var[5] >= 10 ? 0 : ++setup_var[5]; // año
						}
						break;

					case MENU_DETENER_PROCESO:
						if(menu_edition == VIEW)
							load_menu_variables(menu_section -1);

						else // UPDATE
							setup_var[0] = setup_var[0] >= 1 ? 0 : ++setup_var[0];
						break;

					case MENU_REANUDAR_PROCESO:
						if(menu_edition == VIEW)
							load_menu_variables(menu_section -2);

						else // UPDATE
							setup_var[0] = setup_var[0] >= 1 ? 0 : ++setup_var[0];
						break;

					case MENU_REINICIAR_EQUIPO:
						if(menu_edition == VIEW)
							load_menu_variables(menu_section + (myTimer_lite.getTimerDetenido()? -1: -2));

						else // UPDATE
							setup_var[0] = setup_var[0] >= 1 ? 0 : ++setup_var[0];
						break;

					case MENU_VALORES_DE_FABRICA:
						if(menu_edition == VIEW)
							load_menu_variables(menu_section -1);

						else // UPDATE
							setup_var[0] = setup_var[0] >= 1 ? 0 : ++setup_var[0];
						break;

					case MENU_SALIR:
						load_menu_variables(arr_len(menu) -1);
						break;
				}
				break;

			case SETTING_BUTTON_DOWN_VAL:
				switch(menu_section) {
					case OUT_OF_MENU:
						load_menu_variables(1);
						break;

					case MENU_T_ESPERA_INICIAL:
						if(menu_edition == VIEW){
							load_menu_variables(menu_section +1);

						}else{ // UPDATE
							if(menu_cursor_var == POSITION_ONE)
								setup_var[0] = setup_var[0] <= 0 ? 60 : --setup_var[0]; // Minutos

							else if(menu_cursor_var == POSITION_TWO)
								setup_var[1] = setup_var[1] <= 0 ? 59 : --setup_var[1]; // Segundos
						}
						break;

					case MENU_T_ARRANQUE:
					case MENU_T_ARRANQUE_ESPERA:
					case MENU_T_ENCENDIDO:
					case MENU_T_ENCENDIDO_ESPERA:
						if(menu_edition == VIEW){
							load_menu_variables(menu_section +1);

						}else{ // UPDATE
							if(menu_cursor_var == POSITION_ONE)
								setup_var[0] = setup_var[0] <= 0 ? 23 : --setup_var[0]; // Horas

							else if(menu_cursor_var == POSITION_TWO)
								setup_var[1] = setup_var[1] <= 0 ? 59 : --setup_var[1]; // Minutos

							else if(menu_cursor_var == POSITION_THREE)
								setup_var[2] = setup_var[2] <= 0 ? 59 : --setup_var[2]; // Segundos
						}
						break;

					case MENU_FECHA_Y_HORA:
						if(menu_edition == VIEW){
							load_menu_variables(menu_section + (myTimer_lite.getTimerDetenido()? 2: 1));

						}else{ // UPDATE
							if(menu_cursor_var == POSITION_ONE)
								setup_var[0] = setup_var[0] <= 0 ? 23 : --setup_var[0]; // Horas

							else if(menu_cursor_var == POSITION_TWO)
								setup_var[1] = setup_var[1] <= 0 ? 59 : --setup_var[1]; // Minutos

							else if(menu_cursor_var == POSITION_THREE)
								setup_var[2] = setup_var[2] <= 0 ? 59 : --setup_var[2]; // Segundos

							else if(menu_cursor_var == POSITION_FOUR)
								setup_var[3] = setup_var[3] <= 0? 31 : --setup_var[3]; // Día

							else if(menu_cursor_var == POSITION_FIVE)
								setup_var[4] = setup_var[4] <= 0? 12 : --setup_var[4]; // Mes

							else if(menu_cursor_var == POSITION_SIX)
								setup_var[5] = setup_var[5] <= 0? 10 : --setup_var[5]; // año
						}
						break;

					case MENU_DETENER_PROCESO:
						if(menu_edition == VIEW)
							load_menu_variables(menu_section +2);

						else // UPDATE
							setup_var[0] = setup_var[0] <= 0 ? 1 : --setup_var[0];
						break;

					case MENU_REANUDAR_PROCESO:
						if(menu_edition == VIEW)
							load_menu_variables(menu_section +1);

						else // UPDATE
							setup_var[0] = setup_var[0] <= 0 ? 1 : --setup_var[0];
						break;

					case MENU_REINICIAR_EQUIPO:
						if(menu_edition == VIEW)
							load_menu_variables(menu_section +1);

						else // UPDATE
							setup_var[0] = setup_var[0] <= 0 ? 1 : --setup_var[0];
						break;

					case MENU_VALORES_DE_FABRICA:
						if(menu_edition == VIEW)
							load_menu_variables(MENU_SALIR);

						else // UPDATE
							setup_var[0] = setup_var[0] <= 0 ? 1 : --setup_var[0];
						break;

					case MENU_SALIR:
						load_menu_variables(1);
						break;
				}
				break;
		}

		refresh_lcd_menu = true;
		last_change_menu_millis = millis();

		serialPrint("menu_section: ");
		serialPrint(menu_section);
		serialPrint(", menu_edition: ");
		serialPrint(menu_edition);
		serialPrint(", menu_cursor_var: ");
		serialPrint(menu_cursor_var);
		serialPrint(", setup_var[0]: ");
		serialPrint(setup_var[0]);
		serialPrint(", setup_var[1]: ");
		serialPrint(setup_var[1]);
		serialPrint(", setup_var[2]: ");
		serialPrint(setup_var[2]);
		serialPrint(", setup_var[3]: ");
		serialPrint(setup_var[3]);
		serialPrint(", setup_var[4]: ");
		serialPrint(setup_var[4]);
		serialPrint(", setup_var[5]: ");
		serialPrintln(setup_var[5]);
	}

	void save_setting(uint8_t _setting)
	{
		menu_edition    = VIEW;
		menu_cursor_var = POSITION_ONE;

		switch(_setting){
			case MENU_T_ESPERA_INICIAL:
				myTimer_lite.setTiempoInicioEspera(setup_var[0], setup_var[1]);
				break;

			case MENU_T_ARRANQUE:
				myTimer_lite.setTiempoArranque(setup_var[0], setup_var[1], setup_var[2]);
				break;

			case MENU_T_ARRANQUE_ESPERA:
				myTimer_lite.setTiempoArranqueEspera(setup_var[0], setup_var[1], setup_var[2]);
				break;

			case MENU_T_ENCENDIDO:
				myTimer_lite.setTiempoEncendido(setup_var[0], setup_var[1], setup_var[2]);
				break;

			case MENU_T_ENCENDIDO_ESPERA:
				myTimer_lite.setTiempoEncendidoEspera(setup_var[0], setup_var[1], setup_var[2]);
				break;

			case MENU_FECHA_Y_HORA:
        		myTimer_lite.setFechaHora(YEARS_DATETIME + setup_var[5], setup_var[4], setup_var[3], setup_var[0], setup_var[1], setup_var[2]);
				break;

			case MENU_DETENER_PROCESO:
				if(setup_var[0]){
					myTimer_lite.detener();

					load_menu_variables(MENU_REANUDAR_PROCESO);
				}
				break;

			case MENU_REANUDAR_PROCESO:
				if(setup_var[0]){
					myTimer_lite.reanudar();

					load_menu_variables(MENU_DETENER_PROCESO);
				}
				break;

			case MENU_REINICIAR_EQUIPO:
				if(setup_var[0]){
					myTimer_lite.reiniciar();

					load_menu_variables(OUT_OF_MENU);
				}
				break;

			case MENU_VALORES_DE_FABRICA:
				if(setup_var[0]){
					myTimer_lite.setTiempoInicioEspera(TIEMPO_INICIO_ESPERA_DEFAULT);
					myTimer_lite.setTiempoArranque(TIEMPO_ARRRANQUE_DEFAULT);
					myTimer_lite.setTiempoArranqueEspera(TIEMPO_ARRRANQUE_ESPERA_DEFAULT);
					myTimer_lite.setTiempoEncendido(TIEMPO_ENCENDIDO_DEFAULT);
					myTimer_lite.setTiempoEncendidoEspera(TIEMPO_ENCENDIDO_ESPERA_DEFAULT);
					myTimer_lite.reanudar();
					myTimer_lite.reiniciar();

					load_menu_variables(1);
				}
				break;
		}
	}

	void load_menu_variables(int8_t _menu_section)
	{
		menu_section 	= _menu_section;
		menu_edition    = VIEW;
		menu_cursor_var = POSITION_ONE;

		setup_var[0] = 0;
		setup_var[1] = 0;
		setup_var[2] = 0;
		setup_var[3] = 0;
		setup_var[4] = 0;
		setup_var[5] = 0;

		uint32_t valor_guardado;

		switch(_menu_section){
			case MENU_T_ESPERA_INICIAL:
				valor_guardado = myTimer_lite.getTiempoInicioEspera();

				setup_var[0] = uint8_t(valor_guardado / 60); // Minutos
				setup_var[1] = uint8_t(valor_guardado - (setup_var[0] * 60)); // Segundos
				break;

			case int8_t(MENU_T_ARRANQUE):
				valor_guardado = myTimer_lite.getTiempoArranque();

				setup_var[0] = uint8_t(valor_guardado / 3600); // horas
				setup_var[1] = uint8_t((valor_guardado - (setup_var[0] * 3600)) / 60); // Minutos
				setup_var[2] = uint8_t(valor_guardado - (setup_var[0] * 3600) - (setup_var[1] * 60)); // Segundos
				break;

			case MENU_T_ARRANQUE_ESPERA:
				valor_guardado = myTimer_lite.getTiempoArranqueEspera();

				setup_var[0] = uint8_t(valor_guardado / 3600); // horas
				setup_var[1] = uint8_t((valor_guardado - (setup_var[0] * 3600)) / 60); // Minutos
				setup_var[2] = uint8_t(valor_guardado - (setup_var[0] * 3600) - (setup_var[1] * 60)); // Segundos
				break;

			case MENU_T_ENCENDIDO:
				valor_guardado = myTimer_lite.getTiempoEncendido();

				setup_var[0] = uint8_t(valor_guardado / 3600); // horas
				setup_var[1] = uint8_t((valor_guardado - (setup_var[0] * 3600)) / 60); // Minutos
				setup_var[2] = uint8_t(valor_guardado - (setup_var[0] * 3600) - (setup_var[1] * 60)); // Segundos
				break;

			case MENU_T_ENCENDIDO_ESPERA:
				valor_guardado = myTimer_lite.getTiempoEncendidoEspera();

				setup_var[0] = uint8_t(valor_guardado / 3600); // horas
				setup_var[1] = uint8_t((valor_guardado - (setup_var[0] * 3600)) / 60); // Minutos
				setup_var[2] = uint8_t(valor_guardado - (setup_var[0] * 3600) - (setup_var[1] * 60)); // Segundos
				break;

			case MENU_FECHA_Y_HORA:
				setup_var[0] = Timer_lite::_now.hour();
				setup_var[1] = Timer_lite::_now.minute();
				setup_var[2] = Timer_lite::_now.second();

				setup_var[3] = Timer_lite::_now.day();
				setup_var[4] = Timer_lite::_now.month();

				if(Timer_lite::_now.year() >= 2020)
					setup_var[5] = (Timer_lite::_now.year() - YEARS_DATETIME);
				break;
		}
	}


/**************************************************
* LCD2004 rendering
**************************************************/
	// Helpers
	String fill_string_ws(String _text_info, uint8_t _length, uint8_t _first_spaces = 0, String _char = " ")
	{
		if(_text_info.length() >= _length)
			return _text_info.substring(0, _length);

		for(uint8_t i = 0; _first_spaces > i; i++)
			if(_text_info.length() < _length)
				_text_info = _char + _text_info;

		while(_text_info.length() < _length)
			_text_info = _text_info + _char;
	
		return _text_info;
	}

	String min_seg_to_string(uint8_t _min, uint8_t _seg)
	{
		String min = String(_min < 10? "0": "") + String(_min);
		String seg = String(_seg < 10? "0": "") + String(_seg);

		return min  + ":" + seg;
	}

	String hrs_min_seg_to_string(uint8_t _hrs, uint8_t _min, uint8_t _seg)
	{
		String hrs = String(_hrs < 10? "0": "") + String(_hrs);
		String min = String(_min < 10? "0": "") + String(_min);
		String seg = String(_seg < 10? "0": "") + String(_seg);

		return hrs  + ":" + min  + ":" + seg;
	}

	String hrs_min_seg_dia_mes_ano_to_string(uint8_t _hrs, uint8_t _min, uint8_t _seg, uint8_t _dia, uint8_t _mes, uint8_t _ano)
	{
		String hrs = String(_hrs < 10? "0": "") + String(_hrs);
		String min = String(_min < 10? "0": "") + String(_min);
		String seg = String(_seg < 10? "0": "") + String(_seg);

		String dia = String(_dia < 10? "0": "") + String(_dia);
		String mes = String(_mes < 10? "0": "") + String(_mes);
		String ano = String(YEARS_DATETIME + _ano);

		return hrs  + ":" + min  + ":" + seg + "  " + dia + "/" + mes + "/" + ano;
	}

	String seg_to_string(uint32_t _seg)
	{
		uint32_t hrs = _seg / 3600;
		uint16_t min = (_seg - (hrs * 3600)) / 60;
		uint8_t  seg = _seg - (hrs * 3600) - (min * 60);

		String hrs_s = hrs >= 1? String(hrs) + ":": "";
		String min_s = min >= 1? (hrs >= 1 && min <= 9? "0": "") + String(min) + (min >= 1? ":": ""): "";
		String seg_s = (min >= 1 && seg <= 9? "0": "") + String(seg);

		return hrs_s + min_s + seg_s;
	}



	// Methods
	void loop_screen_rendering()
	{
		if(refresh_lcd_menu){
			print_lcd_menu();

			refresh_lcd_menu = false;
		}

		if(millis() % LCD_PROCESS_REFRESH_MILLIS == 0){
			print_lcd_datetime();
			print_lcd_process();
			setCursor_lcd_menu();
		}
	}

	void print_lcd_menu()
	{
		lcd.noCursor();

	    // Menú de configuraciones
	    lcd.setCursor(0, 1);

	    menu_section == OUT_OF_MENU?
			lcd.print(fill_string_ws("", 20)):
			lcd.print(fill_string_ws(menu[menu_section], 20));

		// Detalles de configuraciones
		lcd.setCursor(0, 2);

		uint8_t text_info_idx;

		switch(menu_section){
			case OUT_OF_MENU:
			case MENU_SALIR:
				lcd.print(fill_string_ws("", 20));
				break;

			case MENU_T_ESPERA_INICIAL:
				lcd.print(fill_string_ws(min_seg_to_string(setup_var[0], setup_var[1]), 20, 6));
				break;

			case MENU_T_ARRANQUE:
			case MENU_T_ARRANQUE_ESPERA:
			case MENU_T_ENCENDIDO:
			case MENU_T_ENCENDIDO_ESPERA:
				lcd.print(fill_string_ws(hrs_min_seg_to_string(setup_var[0], setup_var[1], setup_var[2]), 20, 3));
				break;

			case MENU_FECHA_Y_HORA:
				if(menu_edition == UPDATE){
					text_info_idx = setup_var[0]? DETENER: CANCELAR;

					lcd.print(hrs_min_seg_dia_mes_ano_to_string(setup_var[0], setup_var[1], setup_var[2], setup_var[3], setup_var[4], setup_var[5]));

				}else
					lcd.print(fill_string_ws("", 20));
				break;

			case MENU_DETENER_PROCESO:
				if(menu_edition == UPDATE){
					text_info_idx = setup_var[0]? DETENER: CANCELAR;

					lcd.print(fill_string_ws(text_info[text_info_idx], 20, 3));

				}else
					lcd.print(fill_string_ws("", 20));
				break;

			case MENU_REANUDAR_PROCESO:
				if(menu_edition == UPDATE){
					text_info_idx = setup_var[0]? REANUDAR: CANCELAR;

					lcd.print(fill_string_ws(text_info[text_info_idx], 20, 3));

				}else
					lcd.print(fill_string_ws("", 20));
				break;

			case MENU_REINICIAR_EQUIPO:
				if(menu_edition == UPDATE){
					text_info_idx = setup_var[0]? REINICIAR: CANCELAR;

					lcd.print(fill_string_ws(text_info[text_info_idx], 20, 3));

				}else
					lcd.print(fill_string_ws("", 20));
				break;

			case MENU_VALORES_DE_FABRICA:
				if(menu_edition == UPDATE){
					text_info_idx = setup_var[0]? RESTAURAR: CANCELAR;

					lcd.print(fill_string_ws(text_info[text_info_idx], 20, 3));
				}else
					lcd.print(fill_string_ws("", 20));
				break;
		}

		/*
		// Limpiamos línea de proceso
		if(menu_edition == UPDATE){
			lcd.setCursor(0, 2);
			lcd.print(fill_string_ws("", 20));

			lcd.setCursor(0, 3);
			lcd.print(fill_string_ws("", 20));
		}
		*/
	}

	void print_lcd_datetime()
	{
		/*
		lcd.setCursor(0, 0);
		lcd.print(myTimer_lite.getHora());
		lcd.setCursor(10, 0);
		lcd.print(myTimer_lite.getFecha());
		*/
	}

	void print_lcd_process()
	{
		lcd.setCursor(0, 3);

		if(myTimer_lite.getTimerDetenido() != true){
			// Estado del Temporizador
			lcd.print(fill_string_ws(timer_text[myTimer_lite.getPeriodo()], 12));

			// Tiempo del periodo del temporizador
			lcd.print(fill_string_ws(seg_to_string(myTimer_lite.getRestantePeriodoSeg()), 8));

		}else{
			if(millis() >= blink_millis)
				blink_millis = millis() + (BLINK_MILLIS * 2);

			if(blink_millis - millis() >= BLINK_MILLIS)
				lcd.print(fill_string_ws(timer_text[0], 11));
		}
	}

	void setCursor_lcd_menu()
	{
		if(menu_edition == UPDATE){
			switch(menu_section){
				case MENU_T_ESPERA_INICIAL:
					// Posicionamos el cursos, para la edición
					if(menu_cursor_var == POSITION_ONE)
						lcd.setCursor(6, 2);

					else if(menu_cursor_var == POSITION_TWO)
						lcd.setCursor(9, 2);
					break;

				case MENU_T_ARRANQUE:
				case MENU_T_ARRANQUE_ESPERA:
				case MENU_T_ENCENDIDO:
				case MENU_T_ENCENDIDO_ESPERA:
					// Posicionamos el cursos, para la edición
					if(menu_cursor_var == POSITION_ONE)
						lcd.setCursor(3, 2);

					else if(menu_cursor_var == POSITION_TWO)
						lcd.setCursor(6, 2);

					else if(menu_cursor_var == POSITION_THREE)
						lcd.setCursor(9, 2);
					break;

				case MENU_FECHA_Y_HORA:
						// Posicionamos el cursos, para la edición
						if(menu_cursor_var == POSITION_ONE)
							lcd.setCursor(0, 2);

						else if(menu_cursor_var == POSITION_TWO)
							lcd.setCursor(3, 2);

						else if(menu_cursor_var == POSITION_THREE)
							lcd.setCursor(6, 2);

						else if(menu_cursor_var == POSITION_FOUR)
							lcd.setCursor(10, 2);

						else if(menu_cursor_var == POSITION_FIVE)
							lcd.setCursor(13, 2);

						else if(menu_cursor_var == POSITION_SIX)
							lcd.setCursor(16, 2);
					break;

				case MENU_DETENER_PROCESO:
				case MENU_REANUDAR_PROCESO:
				case MENU_REINICIAR_EQUIPO:
				case MENU_VALORES_DE_FABRICA:
					lcd.setCursor(3, 2);
					break;
			}

			lcd.cursor();
		}
	}

/**************************************************
* Setup / Loop
**************************************************/
	void setup() {
		pinMode(SETTINGS_BUTTONS_PIN, INPUT_PULLUP);

		// Salida Serial
		serialBegin(9600);
		serialBeginWhile();

		// Iniciamos la pantalla
		lcd.begin(20, 4);

		lcd.setCursor(0, 0);
		lcd.print("DIKEN INTERNATIONAL");

		myTimer_lite.initialize();
		//myTimer_lite.setFechaHoraSistema();
	}

	void loop() {
		myTimer_lite.process_loop();

		loop_panel_buttons();
		loop_screen_rendering();
	}
