#include <Arduino.h>
#include <U8g2lib.h>

#ifdef U8X8_HAVE_HW_SPI
#include <SPI.h>
#endif
#ifdef U8X8_HAVE_HW_I2C
#include <Wire.h>
#endif

// Variables to store the time
byte hours = 0;
byte minutes = 0;
byte seconds = 0;

// Constants for the button pins
const int PIN_BUTTON_MODE = 3;
const int PIN_BUTTON_SET = 2;

const int BUTTON_MODE_DEBOUNCE_TIME = 250;
const int BUTTON_SET_DEBOUNCE_TIME = 10;

const int MODE_SHOW_TIME = 0;
const int MODE_SET_SECONDS = 3;
const int MODE_SET_MINUTES = 2;
const int MODE_SET_HOURS = 1;

// Variables for the button state
// We are using the internal pull-up resistors via INPUT_PULLUP, so
// press is LOW and not pressed is HIGH
unsigned long elapsedButtonModeMillis = 0;
unsigned long previousButtonModeMillis = 0;

unsigned long elapsedButtonSetMillis = 0;
unsigned long previousButtonSetMillis = 0;

// Char array for the time being showed on the display
char timeString[9];

// Variables to store the time
unsigned long currentMillis = 0;

// Int is enough to store the elapsed time
int elapsedTimeUpdateMillis = 0;
unsigned long previousTimeUpdateMillis = 0;

float percentageOfSecondElapsed = 0;

byte currentMode = MODE_SHOW_TIME;

// A complete list of all displays is available at: https://github.com/olikraus/u8g2/wiki/u8g2setupcpp
U8G2_SSD1306_128X64_NONAME_1_HW_I2C u8g2(U8G2_R0, /* reset=*/ U8X8_PIN_NONE);

void setup(void) {

  u8g2.setFont(u8g2_font_logisoso28_tf);
  u8g2.begin();

  // Configure the pins of the buttons with the internal PULLUP resistor
  pinMode(PIN_BUTTON_MODE, INPUT_PULLUP);
  pinMode(PIN_BUTTON_SET, INPUT_PULLUP);

}

void loop(void) {

  // millis() itself takes 1.812 micro seconds that is 0.001812 milli seconds
  // https://arduino.stackexchange.com/questions/113/is-it-possible-to-find-the-time-taken-by-millis
  currentMillis = millis();
  
  if (digitalRead(PIN_BUTTON_MODE) == LOW) {
    buttonModeHandler();
  }

  if (digitalRead(PIN_BUTTON_SET) == LOW) {
    buttonSetHandler();
  }


  checkTime();

  if (currentMode == MODE_SHOW_TIME) {
    increaseSeconds();
  } else {
    previousTimeUpdateMillis = currentMillis;
  }

  drawScreen();

}

void checkTime() {
  // Check if a minutes has been elapsed
  if (seconds > 59) {
    seconds = 0;
    minutes++;
  }

  // Check if an hour has been elapsed
  if (minutes > 59) {
    minutes = 0;
    hours++;
  }

  // Check if a day has been elapsed
  if (hours > 12) {
    hours = 1;
  }
}

void buttonModeHandler() {
  elapsedButtonModeMillis = currentMillis - previousButtonModeMillis;
  if (elapsedButtonModeMillis > BUTTON_MODE_DEBOUNCE_TIME) {
    Serial.println("Mode Handler");
    previousButtonModeMillis = currentMillis;
    currentMode++;

    if (currentMode > 3) {
      currentMode = 0;
    }

  }
}

void buttonSetHandler() {
  elapsedButtonSetMillis = currentMillis - previousButtonSetMillis;
  Serial.println(elapsedButtonSetMillis);
  if (elapsedButtonSetMillis > BUTTON_SET_DEBOUNCE_TIME) {
    Serial.println("Set Handler");
    previousButtonSetMillis = currentMillis;

    if (currentMode == MODE_SET_SECONDS) {
      seconds = 0;
    }
    if (currentMode == MODE_SET_MINUTES) {
      minutes++;
    }
    if (currentMode == MODE_SET_HOURS) {
      hours++;
    }
  }
}

void increaseSeconds() {
  elapsedTimeUpdateMillis = currentMillis - previousTimeUpdateMillis;

  // Check if 1000ms, 1 second, has been elapsed
  if (elapsedTimeUpdateMillis > 1000) {
    seconds++;

    // It might be possible that more than 1000ms has been elapsed e.g. 1200ms 
    // Then there are already 200ms elapsed of the next second. We need to
    // substract these on the "last time". So the next second will be updated 200ms earlier. 
    // This reduces the amount of time drift.
    previousTimeUpdateMillis = currentMillis - (elapsedTimeUpdateMillis - 1000);
  }
}

void drawScreen() {
  u8g2.firstPage();

  do {

    if (currentMode != MODE_SHOW_TIME) {
      u8g2.drawTriangle((currentMode - 1) * 43 + 5, 0, currentMode * 43 - 5, 0, (currentMode - 1) * 43 + 21, 5);
    }

    drawAnimation();
    drawTime();

  } while (u8g2.nextPage());
}

void drawTime() {

  // Found at https://forum.arduino.cc/index.php?topic=371117.0
  // sprintf_P uses the Program Memory instead of RAM, more info at http://gammon.com.au/progmem
  // Here we format the minutes and seconds with a leading zero: e.g. 01, 02, 03 etc.
  sprintf_P(timeString, PSTR("%2d:%02d:%02d"), hours, minutes, seconds);

  // Draw the timeString
  u8g2.drawStr(0, 45, timeString);

}

void drawAnimation() {
  // Calculate the percentage elapsed of a second
  percentageOfSecondElapsed = elapsedTimeUpdateMillis / 1000.0;

  if (currentMode == MODE_SHOW_TIME) {
    // Draw the yellow lines
    u8g2.drawBox(0, 0, 127 - (127 * percentageOfSecondElapsed), 2);
    u8g2.drawBox(0, 3, (127 * percentageOfSecondElapsed), 2);
  }
}