/**
 * Name:     Arduino - Menu for Shield LCD
 * Autor:    Alberto Gil Tesa
 * Web:      http://giltesa.com
 * License:  CC BY-NC-SA 3.0
 * Date:     2016/10/22
 *
 * Arduino Uno, Pinout:
 *                   _______________
 *                  |      USB      |
 *         RELE A1  |13           12|
 *                  |3V3          11|
 *                  |AREF         10|
 *            APAD  |A0            9|  LCD
 *                  |A1            8|  LCD
 *                  |A2            7|  LCD
 *                  |A3            6|  LCD
 *                  |A4            5|  LCD
 *                  |A5            4|  LCD
 *                  |          3/SCL|
 *                  |          2/SDA|
 *                  |5V          GND|
 *                  |RST         RST|
 *                  |GND   1/INT2/RX|
 *                  |VIN   0/INT3/TX|
 *                  |MISO         SS|
 *                  |SCK        MOSI|
 *                  |_______________|
 *
*/



/*******************************/
/***** LIBRERIAS Y OBJETOS *****/
/*******************************/
//#include <Wire.h>                 //LCD I2C
//#include <LCD.h>                  //LCD I2C
//#include <LiquidCrystal_I2C.h>    //LCD I2C

#include <LiquidCrystal.h>          //SHIELD LCD

#include <EEPROM.h>

LiquidCrystal lcd(8, 9, 4, 5, 6, 7);
//LiquidCrystal_I2C lcd( 0x27, 2, 1, 0, 4, 5, 6, 7, 3, POSITIVE );



/*******************************/
/********* CONSTANTES **********/
/*******************************/
#define TYPE_RELEA   1
#define TYPE_RELEB   2
#define TYPE_BOOL    3
#define TYPE_INT     4



// Generador de iconos: http://mikeyancey.com/hamcalc/lcd_characters.php
byte ico0[8] = { B00000, B00010, B00010, B00010, B11111, B11111, B11111, B00000 }; // Emisora
byte ico1[8] = { B00000, B01110, B01010, B01110, B00100, B00100, B00100, B00000 }; // Microfono
byte ico2[8] = { B00001, B00011, B00101, B00001, B11101, B10100, B10100, B11100 }; // Digital
byte ico3[8] = { B00000, B00000, B11100, B11111, B10001, B11111, B11111, B00000 }; // Grabadora
byte ico4[8] = { B00000, B00010, B00110, B11110, B11110, B00110, B00010, B00000 }; // Llamador
byte ico5[8] = { B00000, B00000, B11111, B11111, B10101, B10001, B11111, B00000 }; // Mensaje
byte ico6[8] = { B00000, B00000, B10000, B11000, B11110, B11111, B00000, B00000 }; // Pedal
byte ico7[8] = { B00000, B00100, B00110, B11111, B00110, B00100, B00000, B00000 }; // Flecha
const byte icoEmisora=0, icoMicrofono=1, icoDigital=2, icoGrabadora=3, icoLlamador=4, icoMensaje=5, icoPedal=6, icoFlecha=7;



/* RELES A */
const byte  zRELE_A   = 5;
const char *nRELE_A[] = {
    "1. OPCION 1    ",
    "2. OPCION 2    ",
    "3. OPCION 3    ",
    "4. OPCION 4    ",
    "5. OPCION 5    "
};
const byte iRELE_A[] = {
    icoEmisora,
    icoEmisora,
    icoEmisora,
    icoEmisora,
    icoEmisora
};

/* RELES B */
const byte  zRELE_B   = 4;
const char *nRELE_B[] = {
    "1. MICROFONO    ",
    "2. MODOS DIGITAL",
    "3. GRABADORA    ",
    "4. LLAMADOR     "
};
const char iRELE_B[] = {
    icoMicrofono,
    icoDigital,
    icoGrabadora,
    icoLlamador
};

/* TEXTOS MENUS */
const char *nMENU[] = {
    "Rele GrupoA     ",
    "Rele GrupoB     ",
    "Rele Msg Es     ",
    "Rele Msg T.     ",
    "Rele Msg T.     ",
    "Rele Aux        ",
    "Rele Pedal      ",
    "Guardar salir   ",
    "No guardar salir",
    "Salir           "
};



/*******************************/
/** ESTRUCTURAS CONFIGURACION **/
/*******************************/
struct MYDATA{
    int initialized;
    int sGroupA;       // Estado rele grupo A          -1(OFF), 0(R1), 1(R2), 2(R3), 3(R4), 4(R5)
    int sGroupB;       // Estado rele grupo B          -1(OFF), 0(R1), 1(R2), 2(R3), 3(R4)
    int sMsg;          // Estado rele mensaje          0, 1
    int tMsgSleep;     // Tiempo pausa rele mensaje    0~32767 segundos
    int tMsgWork;      // Tiempo trabajo rele mensaje  0~32767 segundos
    int sAux;          // Estado rele auxiliar         0, 1
    int tPedalWork;    // Tiempo trabajo rele pedal    0~32767 segundos
};
union MEMORY{
   MYDATA d;
   byte b[sizeof(MYDATA)];
}
memory;



/*******************************/
/***** VARIABLES GLOBALES ******/
/*******************************/
unsigned long tNow            = 0;
unsigned long tPedalBefore    = 0;
unsigned long tMsgSleepBefore = 0;
unsigned long tMsgWorkBefore  = 0;
boolean updateStatus          = true;
boolean btnLeft               = false;
boolean btnRight              = false;
boolean btnCenter             = false;
boolean btnPedal              = false;
boolean bPedal                = false;
boolean exitMenu              = false;
byte menuPosition             = 0;
const byte menuOptions        = 10;     // Numero de opciones de configuracion del menu principal
const byte rowsLCD            = 2;      // Numero de filas del LCD
const byte columnsLCD         = 16;     // Numero de columnas del LCD



/**
 * Configura el Arduino y carga la ultima configuracion.
 */
void setup()
{
    // Carga la configuracion de la EEPROM, y la configura la primera vez:
    readConfiguration();

    // Inicia el LCD:
    lcd.begin(columnsLCD, rowsLCD);
    //lcd.backlight();
    lcd.createChar( icoEmisora,   ico0 );
    lcd.createChar( icoMicrofono, ico1 );
    lcd.createChar( icoDigital,   ico2 );
    lcd.createChar( icoGrabadora, ico3 );
    lcd.createChar( icoLlamador,  ico4 );
    lcd.createChar( icoMensaje,   ico5 );
    lcd.createChar( icoPedal,     ico6 );
    lcd.createChar( icoFlecha,    ico7 );

    // Imprime la informacion del proyecto:
    lcd.setCursor(0,0); lcd.print("Menu Shield LCD ");
    lcd.setCursor(0,1); lcd.print("  giltesa.com   ");
    delay(2000);
    lcd.clear();
}



/**
 *
 */
void loop()
{
    tNow = seconds();
    readButtons();


    if( btnPedal && !bPedal ){
        bPedal = true;
        tPedalBefore = tNow;
    }
    else if( btnPedal && bPedal ){
        bPedal = false;
    }
    if( btnCenter )
        openMenu();


    // Tareas que se ejecutan solo cada 1 segundo:
    if( millis()%990 == 0 )
    {
        // Actualiza LCD linea 1 y 2: Con estado de los reles del grupo A y B:
        lcd.setCursor(0,0);
        if( memory.d.sGroupA == -1 ){
            lcd.print(" OFF            ");
        }else{
            lcd.write(iRELE_A[memory.d.sGroupA]);
            lcd.print(nRELE_A[memory.d.sGroupA]);
        }
        lcd.setCursor(0,1);
        if( memory.d.sGroupB == -1 ){
            lcd.print(" OFF            ");
        }else{
            lcd.write(iRELE_B[memory.d.sGroupB]);
            lcd.print(nRELE_B[memory.d.sGroupB]);
        }
    }
}



/**
 * Muestra el Menu principal en el LCD.
 */
void openMenu()
{
    lcd.clear();

    while( !exitMenu )
    {
        readButtons();

        if( btnPedal )
        {
            exitMenu = true;
        }
        else if( btnLeft && menuPosition-1 >= 0 )
        {
            menuPosition--;
        }
        else if( btnRight && menuPosition+1 < menuOptions )
        {
            menuPosition++;
        }
        else if( btnCenter )
        {
            switch( menuPosition )
            {
                case 0: openSubMenu( 0, TYPE_RELEA, &memory.d.sGroupA,   -1, 4     ); break; // Rele Grupo A
                case 1: openSubMenu( 1, TYPE_RELEB, &memory.d.sGroupB,   -1, 3     ); break; // Rele Grupo B
                case 2: openSubMenu( 2, TYPE_BOOL,  &memory.d.sMsg,       0, 1     ); break; // Rele Msg Estado
                case 3: openSubMenu( 3, TYPE_INT,   &memory.d.tMsgSleep,  0, 32767 ); break; // Rele Msg T. reposo
                case 4: openSubMenu( 4, TYPE_INT,   &memory.d.tMsgWork,   0, 32767 ); break; // Rele Msg T. func.
                case 5: openSubMenu( 5, TYPE_BOOL,  &memory.d.sAux,       0, 1     ); break; // Rele Aux
                case 6: openSubMenu( 6, TYPE_INT,   &memory.d.tPedalWork, 0, 32767 ); break; // Rele Pedal T. apag
                case 7: writeConfiguration(); exitMenu = true;                        break; // Guarda y sale
                case 8: readConfiguration();  exitMenu = true;                        break; // Cancela los cambios y sale
                case 9:                       exitMenu = true;                        break; // Sale, pero mantiene los cambios temporalmente
            }
        }


        if( !exitMenu && millis()%500 == 0 )
        {
            if(  menuPosition % rowsLCD == 0 )
            {
                for( int i=menuPosition ; i<(menuPosition+rowsLCD) ; i++ )
                {
                    lcd.setCursor(1, i % rowsLCD);
                    lcd.print( i<menuOptions ? nMENU[i] : "                " );
                }
            }

            if( (menuPosition-(rowsLCD-1)) % rowsLCD == 0 )
            {
                for( int i=(menuPosition-(rowsLCD-1)) ; i<((menuPosition-(rowsLCD-1))+rowsLCD) ; i++ )
                {
                    lcd.setCursor(1, i % rowsLCD);
                    lcd.print( i<menuOptions ? nMENU[i] : "                " );
                }
            }


            for( int i=0 ; i<rowsLCD ; i++ )
            {
                lcd.setCursor(0,i);
                lcd.print(" ");
            }
            lcd.setCursor(0, menuPosition % rowsLCD );
            lcd.write(icoFlecha);
        }
    }

    exitMenu     = false;
    menuPosition = 0;
    updateStatus = true;
    lcd.clear();
}



/**
 * Muestra el SubMenu en el LCD.
 * @param nameID    Indice del array que contiene el titulo del submenu.
 * @param typeMenu  Segun el tipo, se representara el submenu de una forma u otra.
 * @param value     Puntero a la variable que almacena el dato, y que se modificara.
 * @param minValue  Valor minimo que puede tener la variable.
 * @param maxValue  Valor maximo que puede tener la variable.
 */
void openSubMenu( byte nameID, byte typeMenu, int *value, int minValue, int maxValue )
{
    lcd.clear();

    while( !exitMenu )
    {
        readButtons();

        if( btnCenter || btnPedal )
        {
            exitMenu = true;
        }
        else if( btnLeft  && (*value)-1 >= minValue )
        {
            (*value)--;
        }
        else if( btnRight && (*value)+1 <= maxValue )
        {
            (*value)++;
        }


        if( !exitMenu && millis()%250 == 0 )
        {
            lcd.setCursor(0,0);
            lcd.print(nMENU[nameID]);

            lcd.setCursor(0,1);
            lcd.print("<");
            lcd.setCursor(columnsLCD-1,1);
            lcd.print(">");

            if( typeMenu == TYPE_RELEA )
            {
                lcd.setCursor(1,1);
                lcd.print(*value > minValue ? nRELE_A[*value] : "OFF             " );
            }
            else if( typeMenu == TYPE_RELEB )
            {
                lcd.setCursor(1,1);
                lcd.print(*value > minValue ? nRELE_B[*value] : "OFF             " );
            }
            else if( typeMenu == TYPE_BOOL )
            {
                lcd.setCursor(columnsLCD/2-2,1);
                lcd.print(*value == 0 ? "OFF" : "ON ");
            }
            else if( typeMenu == TYPE_INT )
            {
                lcd.setCursor(columnsLCD/2-4,1);
                lcd.print(*value);
                lcd.print(" seg    ");
            }

        }
    }

    exitMenu     = false;
    menuPosition = 0;
    lcd.clear();
}



/**
 *
 */
void readConfiguration()
{
    for( int i=0 ; i < sizeof(memory.d) ; i++  )
        memory.b[i] = EEPROM.read(i);

    if( !memory.d.initialized )
    {
        memory.d.initialized = true;
        memory.d.sGroupA     = -1;
        memory.d.sGroupB     = -1;
        memory.d.sMsg        = false;
        memory.d.tMsgSleep   = 0;
        memory.d.tMsgWork    = 0;
        memory.d.sAux        = false;
        memory.d.tPedalWork  = 0;
    }
}



/**
 *
 */
void writeConfiguration()
{
    for( int i=0 ; i<sizeof(memory.d) ; i++  )
        EEPROM.write( i, memory.b[i] );
}



/**
 *
 */
void readButtons()
{
    int val   = analogRead(A0);
    btnLeft   = false;
    btnRight  = false;
    btnCenter = false;
    btnPedal  = false;

    if( val < 50 )
        btnRight  = true;
    else if( val < 250 )
        btnCenter = true;
    else if( val < 450 )
        btnPedal  = true;
    else if( val < 650 )
        btnLeft   = true;

    if( btnLeft || btnRight || btnCenter || btnPedal ){
        delay(200);
    }
}



/**
 * @return Segundos que lleva el Arduino encendido.
 */
unsigned long seconds()
{
    return millis()/1000;
}