#include <Wire.h>
#include "RTClib.h"
#include <LiquidCrystal.h>

#define DOWN_BUTTON 3
#define UP_BUTTON 2
#define RIGHT_BUTTON 4
#define LEFT_BUTTON 5
int buttons[] = {DOWN_BUTTON, UP_BUTTON, RIGHT_BUTTON, LEFT_BUTTON};
int countdown = 0;
unsigned long milisBreakdown = 0;

RTC_DS1307 rtc;

LiquidCrystal lcd(12, 11, 10, 9, 8, 7);



void setup() {
  lcd.begin(16, 2);
  lcd.print("hello world");

  Serial.begin(9600);

  for (int btn : buttons) {
    pinMode(btn, INPUT_PULLUP);
  }

  if (! rtc.begin()) {
    Serial.println("Couldn't find RTC");
    Serial.flush();
    abort();
  }
}

int getButtonPress() {
  return getButtonPress(false);
}
int getButtonPress(bool onlyOnce) {
  return getButtonPress(onlyOnce, false);
}
int getButtonPress(bool onlyOnce, bool holdIn) {
  if (onlyOnce) {
    for (int btn : buttons) {
      if (digitalRead(btn) == LOW) {
        while (digitalRead(btn) == LOW && holdIn)delay(10);
        return btn;
      }
    }
    return 0;
  }
  while (true) {
    for (int btn : buttons) {
      if (digitalRead(btn) == LOW) {
        while (digitalRead(btn) == LOW && holdIn) {};
        return btn;
      }
    }
  }
}


char *menu(char *menus[255], int menu_length) {
  int selectedItems = 0;

  while (true) {
    lcd.clear();
    lcd.print(">");
    lcd.print(menus[selectedItems]);
    lcd.setCursor(0, 1);
    if (selectedItems != (menu_length - 1)) lcd.print(menus[selectedItems + 1]);
    else lcd.print(menus[0]);
    delay(400);

    switch (getButtonPress(false, true)) {
      case UP_BUTTON :
        // selectedItems = (selectedItems - 1) % menu_length;
        if (selectedItems <= 0) selectedItems = menu_length - 1;
        else selectedItems--;
        break;
      case DOWN_BUTTON :
        selectedItems = (selectedItems + 1) % menu_length;
        break;
      case RIGHT_BUTTON :
        lcd.clear();
        return menus[selectedItems];
      case LEFT_BUTTON :
        lcd.clear();
        return "";
    }

  }



}

// todo: give this proper name
int intSelector(int minimal, int maximal) {
  int value = minimal;

  while (true) {
    lcd.setCursor(0, 1);
    lcd.print("                ");
    lcd.setCursor(0, 1);
    lcd.print(value);

    delay(100);

    switch (getButtonPress()) {
      case UP_BUTTON :
        if (value != maximal) value++;
        break;
      case DOWN_BUTTON :
        if (value != minimal) value--;
        break;
      case RIGHT_BUTTON :
        lcd.setCursor(0, 1);
        lcd.print("                ");
        delay(400);
        return value;
      case LEFT_BUTTON :
        break;
    }
  }
}
void configRTC() {
  int year = 2000,
      month = 1,
      day = 1,
      hour = 0,
      miniute = 0,
      second = 0;

  lcd.print("RTC config..");
  delay(700);
  lcd.clear();

  lcd.print("tahun?");
  year = intSelector(2000, 4000);
  lcd.clear();

  lcd.print("bulan?");
  month = intSelector(1, 12);
  lcd.clear();

  lcd.print("tanggal?");
  int maxDay = 31;
  if (month == 4 || month == 6 || month == 9 || month == 11) maxDay = 30;
  else if (month == 2) {
    if ((year % 4 == 0 && year % 100 != 0) || (year % 400 == 0)) maxDay = 29;
    else maxDay = 28;
  }

  day = intSelector(1, maxDay);
  lcd.clear();

  lcd.print("jam berapa?");
  hour = intSelector(0, 24);
  lcd.clear();

  lcd.print("kalau menit?");
  miniute = intSelector(0, 60);
  lcd.clear();

  lcd.print("ok, detik?");
  second = intSelector(0, 60);

}

int getTimeInput() {
  int state = 1;
  int add_input_value = 1;
  unsigned long input_value = 0;

  delay(500);

  while (true) {
    lcd.clear();

    int second = input_value;
    int hour = second / 3600;
    second = second % 3600;
    int miniute = second / 60;
    second = second % 60;

    lcd.setCursor(0, 0);
    lcd.print(hour);
    lcd.print(":");
    if (miniute < 10) {
      lcd.print("0");
    }
    lcd.print(miniute);
    lcd.print(":");
    if (second < 10) {
      lcd.print("0");
    }
    lcd.print(second);
    lcd.setCursor(0, 1);
    lcd.print("+/-");
    lcd.print(add_input_value);
    lcd.print("s");



    delay(400);

    switch (getButtonPress(false)) {
      case UP_BUTTON :
        input_value = input_value + add_input_value;
        break;
      case DOWN_BUTTON :
        if ( (input_value - add_input_value) >= 0) input_value = input_value - add_input_value;
        break;
      case RIGHT_BUTTON :
        lcd.clear();
        Serial.println(input_value);
        return input_value;
      case LEFT_BUTTON :
        state = (state + 1) % 6;
        switch (state) {
          case 1:
            add_input_value = 1;
            break;
          case 2:
            add_input_value = 10;
            break;
          case 3:
            add_input_value = 60;
            break;
          case 4:
            add_input_value = 600;
            break;
          case 5:
            add_input_value = 3600;
            break;
        }
        break;
    }
  }
}



void loop() {
  DateTime date = rtc.now();

  char *DateFormat = "DD/MM/YYYY ";
  char *TimeFormat = "hh:mm";

  // lcd.clear();
  lcd.setCursor(0, 0);
  lcd.print(date.toString(DateFormat));
  lcd.setCursor(11, 0);
  lcd.print(date.toString(TimeFormat));
  delay(100);

  if (countdown != 0) {
    int second = countdown - ((millis() - milisBreakdown) / 1000);

    if (second <= 0) {
      lcd.clear();
      lcd.print("HITUNG MUNDUR");
      lcd.setCursor(0, 1);
      lcd.println("SELESAI");
      getButtonPress(false);
      lcd.clear();
      countdown = 0;
      milisBreakdown = 0;
      delay(400);
      return;
    }
    int hour = second / 3600;
    second = second % 3600;
    int miniute = second / 60;
    second = second % 60;

    lcd.setCursor(0, 1);
    lcd.print(hour);
    lcd.print(":");
    if (miniute < 10) {
      lcd.print("0");
    }
    lcd.print(miniute);
    lcd.print(":");
    if (second < 10) {
      lcd.print("0");
    }
    lcd.print(second);
  } 

  if (getButtonPress(true, true)) {
    delay(400);
    char *menuItems[] = {"Setel Alarm", "Hitung Waktu", "Hitung Mundur", "config RTC", "credit"};
    char *respons = menu(menuItems, 5);

    if (respons == "config RTC") configRTC();
    else if (respons == "Hitung Mundur") {
      int input = getTimeInput();
      if (input){
        countdown = input;
        milisBreakdown = millis();
      }
    } else if (respons == "Hitung Waktu") {
      delay(400);
      lcd.clear();
      milisBreakdown = millis();
      while (true) {
        unsigned long time = (millis() - milisBreakdown);
        int second = time / 1000;
        int hour = second / 3600;
        second = second % 3600;
        int miniute = second / 60;
        second = second % 60;

        lcd.setCursor(0, 1);
        lcd.print(hour);
        lcd.print(":");
        if (miniute < 10) {
          lcd.print("0");
        }
        lcd.print(miniute);
        lcd.print(":");
        if (second < 10) {
          lcd.print("0");
        }
        lcd.print(second);
        lcd.print(".");
        lcd.print(time - ((time / 1000) * 1000));




        if (getButtonPress(true, false) == RIGHT_BUTTON) {
          delay(400);
          unsigned long jedaAwal = millis();
          bool wait = true;
          while (wait) {
            switch (getButtonPress(false, true)) {
              case UP_BUTTON :
              case DOWN_BUTTON :
                //
                break;
              case RIGHT_BUTTON :
                milisBreakdown += millis() - jedaAwal;
                wait = false;
                break;
              case LEFT_BUTTON :
                milisBreakdown = 0;
                return;
            }
          }
          delay(100);
        }
        // delay(400);
      }
    }
    else   lcd.print(respons);

    // delay(700);
    lcd.clear();
    // lcd.print(respons);
    delay(1000);
  }
}

GND5VSDASCLSQWRTCDS1307+