//
// Based on: millis_and_finite_state_machine.ino
//           https://wokwi.com/arduino/projects/314526434594914882
//


// An 'enum' is the best way to for a list of the states.
enum
{
  STATE_INITIAL_MESSAGE,
  STATE_IDLE,
  STATE_FIRST_PRESS,
  STATE_RELEASE_IN_BETWEEN,
  STATE_SECOND_PRESS,
  STATE_FAILED,
} state;

const int buttonPin = 2;
int last_button_value = HIGH;      // pin 2 is HIGH when button is not pressed

unsigned long previousMillis;      // a single 'previousMillis' used for everything

unsigned long elapsedMillisFirstPress;   // how long was the button pressed the first time
const unsigned long pressMinimum = 500;  // minimum time to press
const unsigned long pressMaximum = 5000; // maximum time to press
const unsigned long releaseMaximum = 3000;  // maximum time release in the middle


void setup()
{
  Serial.begin( 115200);
  Serial.println( "The sketch has started.");
  pinMode( buttonPin, INPUT_PULLUP);
}


void loop()
{
  unsigned long currentMillis = millis();
  
  
  // ---------------------------------------------------
  // BUTTON
  // ---------------------------------------------------
  // Detect if the button changes, and pass
  // that information on. In this case to the finite state machine.
  // Only the event of the change is passed on, because
  // this sketch did not need to know the state
  // of the button, only a change.
  // ---------------------------------------------------
  bool buttonPressed = false;
  bool buttonReleased = false;

  int button_value = digitalRead( buttonPin);

  if( button_value != last_button_value)
  {
    if( button_value == LOW)            // low is button pressed
    {
      buttonPressed = true;
    }
    else
    {
      buttonReleased = true;
    }
    last_button_value = button_value;
  }
  

  // ---------------------------------------------------
  // FINITE STATE MACHINE
  // ---------------------------------------------------
  // The final state machine uses information to
  // make decisions.
  // ---------------------------------------------------
  
  switch( state)
  {
    case STATE_INITIAL_MESSAGE:
      // Before going to the actual "idle" state, a message is printed.
      Serial.println( "------------------------------------------");
      Serial.println( "Press the button twice for a second or so.");
      state = STATE_IDLE;
      break;
    case STATE_IDLE:
      // This "idle" state is when there is nothing to do.
      // This state is executed until a button is pressed.
      // It "waits" until a button is pressed, but it does
      // not really wait, since it is run over and over again.
      if( buttonPressed)
      {
        previousMillis = currentMillis;
        state = STATE_FIRST_PRESS;
      }
      break;
    case STATE_FIRST_PRESS:
      // The button is pressed at the moment.
      // Check the time when it is released.
      if( buttonReleased)
      {
        elapsedMillisFirstPress = currentMillis - previousMillis;
        if( elapsedMillisFirstPress >= pressMinimum and elapsedMillisFirstPress <= pressMaximum)
        {
          previousMillis = currentMillis;
          state = STATE_RELEASE_IN_BETWEEN;
        }
        else
        {
          state = STATE_FAILED;
        }
      }
      break;
    case STATE_RELEASE_IN_BETWEEN:
      if( currentMillis - previousMillis > releaseMaximum)
      {
        state = STATE_FAILED;
      }
      else if( buttonPressed)         // button pressed within time ?
      {
        previousMillis = currentMillis;
        state = STATE_SECOND_PRESS;
      }
      break;
    case STATE_SECOND_PRESS:
      // The button is pressed at the moment for the second time
      // Check the time when it is released.
      if( buttonReleased)
      {
        unsigned long elapsedMillisSecondPress = currentMillis - previousMillis;
        if( elapsedMillisSecondPress >= pressMinimum and elapsedMillisSecondPress <= pressMaximum)
        {
          Serial.println( "Success.");
          if( elapsedMillisSecondPress > elapsedMillisFirstPress)
          {
            Serial.print( "The second press was longer by ");
            Serial.print( elapsedMillisSecondPress - elapsedMillisFirstPress);
            Serial.println( " milliseconds.");
          }
          else
          {
            Serial.println( "The second press was shorter.");
          }
          state = STATE_INITIAL_MESSAGE;
        }
        else
        {
          state = STATE_FAILED;
        }
      }
      break;
    case STATE_FAILED:
      Serial.println( "Not accepted !");
      state = STATE_INITIAL_MESSAGE;
      break;
  }
  
  delay( 10);  // a delay as a simple way to debounce the button
}