#include <Keypad.h>
#include <LiquidCrystal.h>


const char keys[][4] =
{
  { '1', '2', '3', 'A' },
  { '4', '5', '6', 'B' },
  { '7', '8', '9', 'C' },
  { '*', '0', '#', 'D' }
};

Keypad keypad( makeKeymap( keys ), (uint8_t []){ 12, 11, 10, 9 }, (uint8_t []){ 8, 7, 6, 5 }, 4, 4 );
LiquidCrystal lcd( A5, A4, A3, A2, A1, A0 );

const uint8_t pin_led    = 2;
const uint16_t min_distance =    1;
const uint16_t max_distance = 9999;


void setup()
{
  Serial.begin( 115200 );
  lcd.begin( 16, 2 );
  pinMode( pin_led, OUTPUT );
}


void loop()
{
  const  uint32_t timer       = millis();
  static uint32_t timer_led   = 0;
  static uint32_t timer_error = 0;
  static uint32_t timer_key   = 0;
  static uint16_t distance    = 0;
  static uint8_t  digits      = 0;

  enum STATE : const uint8_t
  {
    RESET,
    READ_KEYS,
    TIMER_ERROR,
    TIMER_LED
  };
  static STATE state = RESET;


  if ( state == RESET )
  {
    lcd.setCursor( 0, 0 );
    lcd.print( F( "DISTANCE :  0000" ) );
    lcd.setCursor( 0, 1 );
    lcd.print( F( "* UNDO      OK #" ) );

    distance = 0;
    digits = 0;
    state = READ_KEYS;
  }

  else if ( state == READ_KEYS )
  {
    const char key = keypad.getKey();
    //static char previous_key = NO_KEY;
    static char key_pressed = NO_KEY;

    if ( key != NO_KEY )
    {
      key_pressed = key;
      //previous_key = key;
      //Serial.println( key );

      if ( digits == 0 && key == '0' )
      {
      }
      else if ( digits < 4 && key >= '0' && key <= '9' )
      {
        distance *= 10;
        distance += key - '0';
        lcd.setCursor( 16 - ++digits, 0 );
        lcd.print( distance );
      }
      else if ( digits != 0 )
      {
        if ( key == '*' )
        {
          //if ( timer_key == 0 )
          //{
            timer_key = millis();
            distance /= 10;
            lcd.setCursor( 15 - --digits, 0 );
            lcd.print( '0' );
            if ( distance > 0 )
            {
              lcd.print( distance );
            }
          //}
          /*else if ( millis() - timer_key >= 1500 )
          {
            timer_key = 0;
            
            state = RESET;
          }
          Serial.println( timer_key );*/
        }
        else if ( key == '#' )
        {
          Serial.println( distance );

          if ( distance >= min_distance && distance <= max_distance )
          {
            lcd.setCursor( 0, 1 );
            lcd.print( F( "   LED IS ON    " ) );
            digitalWrite( pin_led, HIGH );
            timer_led = timer;
            state = TIMER_LED;
          }
          else
          {
            timer_error = 0;
            state = TIMER_ERROR;
          }
        }
      }
    }
    else
    {
      if ( key_pressed != NO_KEY )
      {
        Serial.println( "key released" );
        key_pressed = NO_KEY;
        timer_key = 0;
      }
    }

    if ( timer_key != 0 && millis() - timer_key >= 1500 )
    {
      timer_key = 0;
      state = RESET;
    }
  }

  else if ( state == TIMER_LED )
  {
    if ( timer - timer_led >= 1000 )
    {
      timer_led = timer;

      static uint8_t counter = 0;
      ++counter;

      if ( counter == 5 )
      {
        lcd.setCursor( 0, 1 );
        lcd.print( F( "   LED IS OFF   " ) );
        digitalWrite( pin_led, LOW );
      }
      else if ( counter == 7 )
      {
        counter = 0;
        state = RESET;
      }
    }
  }

  else if ( state == TIMER_ERROR )
  {
    if ( timer_error == 0 || timer - timer_error >= 500 )
    {
      timer_error = timer;
      lcd.setCursor( 0, 1 );

      static bool show = false;
      show = !show;
        
      if ( show == true )
      {
        lcd.print( F( "  OUT OF RANGE  " ) );
      }
      else
      {
        static uint8_t counter = 0;

        if ( ++counter < 4 )
        {
          lcd.print( F( "                " ) );
        }
        else
        {
          counter = 0;
          state = RESET;
        }
      }
    }
  }
}