#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
#include <EasyButton.h>
#include <EEPROM.h>

#define SCREEN_WIDTH 128
#define SCREEN_HEIGHT 64
#define OLED_RESET    -1
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);

#define BUTTON_UP_PIN    32
#define BUTTON_DOWN_PIN  33
#define BUTTON_STAR_PIN  25
#define BUTTON_HASH_PIN  26

EasyButton button_up(BUTTON_UP_PIN);
EasyButton button_down(BUTTON_DOWN_PIN);
EasyButton button_star(BUTTON_STAR_PIN);
EasyButton button_hash(BUTTON_HASH_PIN);

const int TOTAL_MENU = 4;
int current_menu = 1;
bool in_menu = false;
unsigned long last_button_press = 0;

int open_time = 10;
int open_unit = 1;

int close_time = 30;
int close_unit = 1;

bool start_with = false;
bool lock_work = true;

String get_unit(int unit) {
  switch(unit){
    case 0: return "SEC";
    case 1: return "MIN";
    case 2: return "HR";
    default: return "";
  }
}

void display_main_screen() {
  display.clearDisplay();
  display.setTextSize(1);
  display.setTextColor(SSD1306_WHITE);
  
  display.setCursor(0, 0);
  display.print("ON  : ");
  display.print(open_time);
  display.print(" ");
  display.print(get_unit(open_unit));
  
  display.setCursor(0, 12);
  display.print("OFF : ");
  display.print(close_time);
  display.print(" ");
  display.print(get_unit(close_unit));
  
  display.display();
}

void display_menu() {
  display.clearDisplay();
  display.setTextSize(1);
  display.setTextColor(SSD1306_WHITE);
  
  for(int i=1; i<=TOTAL_MENU; i++){
    display.setCursor(0, (i-1)*12);
    
    if(in_menu && i == current_menu){
      display.print("> ");
    }
    else{
      display.print("  ");
    }
    
    switch(i){
      case 1:
        display.print("1. ON    ");
        display.print(open_time);
        display.print(" ");
        display.print(get_unit(open_unit));
        break;
      case 2:
        display.print("2. OFF   ");
        display.print(close_time);
        display.print(" ");
        display.print(get_unit(close_unit));
        break;
      case 3:
        display.print("3. START ");
        display.print(start_with ? "ON" : "OFF");
        break;
      case 4:
        display.print("4. LOCK  ");
        display.print(lock_work ? "YES" : "NO");
        break;
    }
  }
  
  display.display();
}

void setup() {
  Serial.begin(115200);
  
  if(!display.begin(SSD1306_SWITCHCAPVCC, 0x3C)) {
    Serial.println(F("SSD1306 allocation failed"));
    for(;;);
  }
  display.clearDisplay();
  
  EEPROM.begin(512);
  
  open_time = (EEPROM.read(0) | (EEPROM.read(1) << 8)) <= 999 ? (EEPROM.read(0) | (EEPROM.read(1) << 8)) : 10;
  close_time = (EEPROM.read(2) | (EEPROM.read(3) << 8)) <= 999 ? (EEPROM.read(2) | (EEPROM.read(3) << 8)) : 30;
  start_with = EEPROM.read(4) <= 1 ? (bool)EEPROM.read(4) : false;
  lock_work = EEPROM.read(5) <= 1 ? (bool)EEPROM.read(5) : true;
  open_unit = (EEPROM.read(6) >= 0 && EEPROM.read(6) <= 2) ? EEPROM.read(6) : 1;
  close_unit = (EEPROM.read(7) >= 0 && EEPROM.read(7) <= 2) ? EEPROM.read(7) : 1;
  
  display_main_screen();
  
  pinMode(BUTTON_UP_PIN, INPUT_PULLUP);
  pinMode(BUTTON_DOWN_PIN, INPUT_PULLUP);
  pinMode(BUTTON_STAR_PIN, INPUT_PULLUP);
  pinMode(BUTTON_HASH_PIN, INPUT_PULLUP);
  
  button_up.begin();
  button_down.begin();
  button_star.begin();
  button_hash.begin();
  
  button_down.onPressed(handle_menu_navigation_down);
  button_up.onPressed(handle_menu_toggle);
  button_star.onPressed(handle_star_press);
  
  button_hash.onPressed([](){
    handle_hash_press_short();
  });
  
  button_hash.onPressedFor(1000, [](){
    handle_hash_press_long();
  });
}

void loop() {
  button_up.read();
  button_down.read();
  button_star.read();
  button_hash.read();
}

void handle_menu_navigation_down() {
  Serial.println("Button Down Pressed");
  if(!in_menu){
    in_menu = true;
    current_menu = 1;
  }
  else{
    current_menu++;
    if(current_menu > TOTAL_MENU) current_menu = 1;
  }
  display_menu();
  last_button_press = millis();
}

void handle_menu_toggle() {
  Serial.println("Button Up Pressed");
  if(in_menu){
    in_menu = false;
    display_main_screen();
    Serial.println("Switched to Main Screen");
    save_settings();
  }
  else{
    in_menu = true;
    current_menu = 1;
    display_menu();
    Serial.println("Switched to Menu");
  }
  last_button_press = millis();
}

void handle_star_press() {
  if(!in_menu) return;
  
  switch(current_menu){
    case 1:
    case 2:
      if(current_menu == 1){
        open_unit = (open_unit + 1) % 3;
      }
      else{
        close_unit = (close_unit + 1) % 3;
      }
      break;
    case 3:
      start_with = !start_with;
      break;
    case 4:
      lock_work = !lock_work;
      break;
  }
  display_menu();
  last_button_press = millis();
}

void handle_hash_press_short() {
  if(!in_menu) return;
  
  switch(current_menu){
    case 1:
      open_time += 1;
      if(open_time > 999) open_time = 0;
      break;
    case 2:
      close_time += 1;
      if(close_time > 999) close_time = 0;
      break;
  }
  display_menu();
  last_button_press = millis();
}

void handle_hash_press_long() {
  if(!in_menu) return;
  
  switch(current_menu){
    case 1:
      open_time = 0;
      break;
    case 2:
      close_time = 0;
      break;
  }
  display_menu();
  last_button_press = millis();
}

void save_settings() {
  bool need_save = false;
  
  if(EEPROM.read(0) != (open_time & 0xFF) || EEPROM.read(1) != ((open_time >> 8) & 0xFF)){
    EEPROM.write(0, open_time & 0xFF);
    EEPROM.write(1, (open_time >> 8) & 0xFF);
    need_save = true;
  }
  
  if(EEPROM.read(2) != (close_time & 0xFF) || EEPROM.read(3) != ((close_time >> 8) & 0xFF)){
    EEPROM.write(2, close_time & 0xFF);
    EEPROM.write(3, (close_time >> 8) & 0xFF);
    need_save = true;
  }
  
  if(EEPROM.read(4) != (start_with ? 1 : 0)){
    EEPROM.write(4, start_with ? 1 : 0);
    need_save = true;
  }
  
  if(EEPROM.read(5) != (lock_work ? 1 : 0)){
    EEPROM.write(5, lock_work ? 1 : 0);
    need_save = true;
  }
  
  if(EEPROM.read(6) != open_unit){
    EEPROM.write(6, open_unit);
    need_save = true;
  }
  
  if(EEPROM.read(7) != close_unit){
    EEPROM.write(7, close_unit);
    need_save = true;
  }
  
  if(need_save){
    EEPROM.commit();
    Serial.println("Settings saved to EEPROM");
  }
  else{
    Serial.println("No changes detected, EEPROM not written");
  }
}