#include <RTClib.h>
#include <LiquidCrystal.h>
#include <Eventually.h>
#include <EEPROM.h>

EvtManager mgr; // From Eventually
LiquidCrystal lcd(8, 9, 4, 5, 6, 7);  // From LiquidCrystal

TimeSpan StopwatchTime; // From RTCLib

#define BUTTONPIN   A0

#define btnRIGHT    5
#define btnUP       4
#define btnDOWN     3
#define btnLEFT     2
#define btnSELECT   1
#define btnNONE     (-1)

//Wokwi

#define SELECT_PIN  10
#define UP_PIN      12
#define DOWN_PIN    3
#define LEFT_PIN    2
#define RIGHT_PIN   11


#define BUTTONUP    0
#define BUTTONDOWN  1

#define NRLAPTIMES  10  // This allows us to easily change the number of laps we can handle (up to 99)

// EEPROM Layout
#define MODEAddress 0

#define MODE_HMS    0
#define MODE_Sec    1

TimeSpan LapTime[NRLAPTIMES];
int lapNr = 0;          // Stores the index of the next EMPTY LapTime
int lapToView = -1;     // Stores the currently displayed LapTime or -1 is none

int buttonState = BUTTONUP;
int click = btnNONE;

#define STOPPED   0
#define RUNNING   1

int stopwatchState = STOPPED;

void setup() {
  // wokwi
  pinMode(SELECT_PIN, INPUT_PULLUP);
  pinMode(UP_PIN, INPUT_PULLUP);
  pinMode(DOWN_PIN, INPUT_PULLUP);
  pinMode(LEFT_PIN, INPUT_PULLUP);
  pinMode(RIGHT_PIN, INPUT_PULLUP);

  // Serial.begin(115200);
  lcd.begin(16,2);
  
  // Cells never used are always set to 0xFF by default
  if(EEPROM.read(MODEAddress) == 0xFF)
    EEPROM.update(MODEAddress, MODE_HMS);   // Set to HMS by default
  // mgr.addListener(new EvtTimeListener(1000, true, (EvtAction)tick)); 
  mgr.addListener(new EvtTimeListener(50, true, (EvtAction)buttons));
  display();
}

void display() {
  char time[30];

  if(EEPROM.read(MODEAddress) == MODE_HMS)
      sprintf(time, "%02d:%02d:%02d",
      StopwatchTime.hours(),
      StopwatchTime.minutes(), 
      StopwatchTime.seconds());
    else
      sprintf(time, "%d", StopwatchTime.totalseconds());
  lcd.clear();
  lcd.print(time);
  if(lapToView >= 0)  // lapToView will be -1 if there are no laps stored
  {
    // Display lap number as lapToView + 1 because lap index starts at zero and us humans generally count from 1
    if(EEPROM.read(MODEAddress) == MODE_HMS)
          sprintf(time, "Lap %2d %02d:%02d:%02d", lapToView + 1, 
        LapTime[lapToView].hours(), 
        LapTime[lapToView].minutes(), 
        LapTime[lapToView].seconds());
        else
          sprintf(time, "Lap %2d %d", lapToView + 1,
		LapTime[lapToView].totalseconds());
    lcd.setCursor(0, 1);
    lcd.print(time);    
  }
}

bool tick() {
  if(stopwatchState == RUNNING)
  {
    StopwatchTime = StopwatchTime + TimeSpan(1);
    display();
  }
  return false; // Allow the event chain to continue
}

bool buttons()
{
  static int count = 0;

  if(++count == 20)
  {
    count = 0;
    tick();
  }

  int button = read_LCD_buttons();

  switch(buttonState)
  {
    case BUTTONUP:
      if(button != btnNONE)
      {
        click = button;
        buttonState = BUTTONDOWN;
      }
      break;
    case BUTTONDOWN:
      if(button == btnNONE)
      {
        click = btnNONE;      
        buttonState = BUTTONUP;
      }
      break;
  }

  switch(click)
  {
    case btnRIGHT:
      if(stopwatchState == STOPPED)
        stopwatchState = RUNNING;
      else
        stopwatchState = STOPPED;
      break;
    case btnLEFT:
      if(stopwatchState == STOPPED)
      {
        StopwatchTime = TimeSpan();
        lapNr = 0;        // Reset lapNr and lapToView
        lapToView = -1;
      }
      else
      {
        if(lapNr < NRLAPTIMES)
        {
          LapTime[lapNr] = StopwatchTime;
          lapToView = lapNr++;
        }
      }
      display();
      break;
    case btnUP :
      if(lapToView > 0)
      { // Greater than the first lap. Scroll up.
        lapToView--;
        display();                
      }          
      break;
    case btnDOWN :
      if(lapToView < (lapNr - 1))
      { // Less than the last lap. Scroll down.
        lapToView++;
        display();      
      }
      break;
    case btnSELECT :
          if(EEPROM.read(MODEAddress) == MODE_HMS)
            EEPROM.update(MODEAddress, MODE_Sec);
          else  
            EEPROM.update(MODEAddress, MODE_HMS);
          display();      
          break;
  }
  click = btnNONE;

  return false;
}

int read_LCD_buttons(){
  if(!buttonRead(RIGHT_PIN))
    return btnRIGHT;
  if(!buttonRead(UP_PIN))
    return btnUP;
  if(!buttonRead(DOWN_PIN))
    return btnDOWN;
  if(!buttonRead(LEFT_PIN))
    return btnLEFT;
  if(!buttonRead(SELECT_PIN))
    return btnSELECT;
  return btnNONE;
}

bool buttonRead(int fPin)
{
  int before = digitalRead(fPin);
  delay(5);
  if(digitalRead((fPin) == before))
    return before;
  return HIGH;
}
/*
int read_LCD_buttons(){
  int adc_key_in = 0;
  adc_key_in = analogRead(BUTTONPIN);
  delay(5);
  int k = (analogRead(BUTTONPIN) - adc_key_in);
  if (abs(k) > 5)
  return btnNONE;
  // Allow +- 50 tolerance
  if (adc_key_in > 1000) return btnNONE;
  if (adc_key_in < 50)   return btnRIGHT; 
  if (adc_key_in < 195)  return btnUP;
  if (adc_key_in < 380)  return btnDOWN;
  if (adc_key_in < 555)  return btnLEFT;
  if (adc_key_in < 790)  return btnSELECT;  
  return btnNONE;
}
*/
USE_EVENTUALLY_LOOP(mgr) // Use this instead of your loop() function.