class Timer {
  private:
    unsigned long nextChangeTime;
    unsigned long timeOn_;
    boolean overFlow;
  public:
    void TimerSet (unsigned long timeOn) {
      timeOn_ = timeOn;
      unsigned long currentTime = micros();
      nextChangeTime = currentTime + timeOn;
      if (nextChangeTime > currentTime) {
        overFlow = false;
      } else {
        overFlow = true;
      }
   }
    boolean TimerActive() {
      unsigned long currentTime = micros();
      boolean val = false;
      if (! overFlow) {
        if (currentTime < nextChangeTime) {
          val = true;
        }
      } else if ((currentTime + timeOn_)<(nextChangeTime + timeOn_)){
        val = true;
      }
      return val;
    }
};

#include <LiquidCrystal_I2C.h>  /*include LCD I2C Library*/

#define I2C_ADDR    0x27
#define LCD_COLUMNS 16
#define LCD_LINES   2
LiquidCrystal_I2C lcd(I2C_ADDR, LCD_COLUMNS, LCD_LINES);

#define LED 4

int BTN_MODE = 6;
int BTN_OK = 7;
int BTN_CANCEL = 8;
int BTN_UP = 9;
int BTN_DOWN = 10;

unsigned long debounceDuration = 50; //millis

enum currentStateType {
  ready,
  set,
  busy
}; 

enum currentSetupType {
  none,
  power,
  timer,
  temp
}; 

currentStateType currentState = ready;
currentSetupType currentSetup = none;
int currentMode = 0; //0 OFF; 1 VENT; 2 COOL; 3 WARM; 4 AUTO
int currentTemp = random(10, 25);
int setPower = 10; 
int setTimer = 0;
int setTemp = 10;
bool timerActive = false;

Timer TMR;

void setup() {
  lcd.init();  // LCD display initialized
  lcd.clear();     // Clear LCD Display
  lcd.backlight(); // Turn on LCD backlight
  lcd.print("UIT");

  pinMode(LED, OUTPUT);
  pinMode(BTN_MODE, INPUT_PULLUP);
  pinMode(BTN_OK, INPUT_PULLUP);
  pinMode(BTN_CANCEL, INPUT_PULLUP);
  pinMode(BTN_UP, INPUT_PULLUP);
  pinMode(BTN_DOWN, INPUT_PULLUP);
}

void loop() {
  byte buttonState_BTN_MODE = digitalRead(BTN_MODE);
  byte buttonState_BTN_OK = digitalRead(BTN_OK);
  byte buttonState_BTN_CANCEL = digitalRead(BTN_CANCEL);
  byte buttonState_BTN_UP = digitalRead(BTN_UP);
  byte buttonState_BTN_DOWN = digitalRead(BTN_DOWN);

  if (buttonState_BTN_MODE == HIGH && buttonState_BTN_OK == HIGH && buttonState_BTN_UP == HIGH && buttonState_BTN_DOWN == HIGH) {
    checkCancelButton();
  };

  if (currentState != busy) {
    digitalWrite(LED, LOW);

    if (currentState == ready) {
      if (buttonState_BTN_OK == HIGH && buttonState_BTN_CANCEL == HIGH){
        checkModeButton();
      }
    }
    if (currentState == set) {
      if (buttonState_BTN_OK == HIGH && buttonState_BTN_CANCEL == HIGH && buttonState_BTN_DOWN == HIGH){
        checkUpButton();
      }
      if (buttonState_BTN_OK == HIGH && buttonState_BTN_CANCEL == HIGH && buttonState_BTN_UP == HIGH){
        checkDownButton();
      }
    }
    
    if (buttonState_BTN_MODE == HIGH && buttonState_BTN_CANCEL == HIGH && buttonState_BTN_UP == HIGH  && buttonState_BTN_DOWN == HIGH) {
      checkOkButton();
    }
  } else {
    digitalWrite(LED, HIGH);

    if (currentMode == 4) {

      if (timerActive) {
        if(!TMR.TimerActive()){
          if (currentTemp > setTemp) {
            currentTemp --;
            displayHandler();
            TMR.TimerSet(random(1000000, 3000000));
          } else if (currentTemp < setTemp) {
            currentTemp ++;
            displayHandler();
            TMR.TimerSet(random(1000000, 3000000));
          } else {
            resetAll();
            displayHandler();
          }
        }
      }

    } else {

      if (setTimer == 0){
        if(!TMR.TimerActive()){
          displayHandler();
          TMR.TimerSet(1000000);
        }
      } else {
        if (timerActive) {
          if(!TMR.TimerActive()){
            setTimer --;
            if (setTimer > 0) {
              displayHandler();
              TMR.TimerSet(1000000);
            } else {
              resetAll();
              displayHandler();
            }
          }
        }
      }

    }
  }
};

//MODE Button
unsigned long lastTimeButtonStateChanged_BTN_MODE = 0;
byte lastButtonState_BTN_MODE = LOW;
void checkModeButton() {
  if (millis() - lastTimeButtonStateChanged_BTN_MODE > debounceDuration) {
    byte buttonState = digitalRead(BTN_MODE);
    if (buttonState != lastButtonState_BTN_MODE) {
      lastTimeButtonStateChanged_BTN_MODE = millis();
      lastButtonState_BTN_MODE = buttonState;
      if (buttonState == LOW) {
        modeHandler();
        displayHandler();
      } 
    }
  }
};

//OK Button
unsigned long lastTimeButtonStateChanged_BTN_OK = 0;
byte lastButtonState_BTN_OK = LOW;
void checkOkButton() {
  if (millis() - lastTimeButtonStateChanged_BTN_OK > debounceDuration) {
    byte buttonState = digitalRead(BTN_OK);
    if (buttonState != lastButtonState_BTN_OK) {
      lastTimeButtonStateChanged_BTN_OK = millis();
      lastButtonState_BTN_OK = buttonState;
      if (buttonState == LOW) {
        okHandler();
        displayHandler();

      }
    }
  }
};

//CANCEL Button
unsigned long lastTimeButtonStateChanged_BTN_CANCEL = 0;
byte lastButtonState_BTN_CANCEL = LOW;
void checkCancelButton() {
  if (millis() - lastTimeButtonStateChanged_BTN_CANCEL > debounceDuration) {
      byte buttonState = digitalRead(BTN_CANCEL);
      if (buttonState != lastButtonState_BTN_CANCEL) {
        lastTimeButtonStateChanged_BTN_CANCEL = millis();
        lastButtonState_BTN_CANCEL = buttonState;
        if (buttonState == LOW) {

          resetAll();
          displayHandler();

        }
      }
    }
};

//UP Button
unsigned long lastTimeButtonStateChanged_BTN_UP = 0;
byte lastButtonState_BTN_UP = LOW;
void checkUpButton() {
  if (millis() - lastTimeButtonStateChanged_BTN_UP > debounceDuration) {
      byte buttonState = digitalRead(BTN_UP);
      if (buttonState != lastButtonState_BTN_UP) {
        lastTimeButtonStateChanged_BTN_UP = millis();
        lastButtonState_BTN_UP = buttonState;
        if (buttonState == LOW) {
          upHandler();
          displayHandler();
        }
      }
    }
};

//DOWN Button
unsigned long lastTimeButtonStateChanged_BTN_DOWN = 0;
byte lastButtonState_BTN_DOWN = LOW;
void checkDownButton() {
  if (millis() - lastTimeButtonStateChanged_BTN_DOWN > debounceDuration) {
      byte buttonState = digitalRead(BTN_DOWN);
      if (buttonState != lastButtonState_BTN_DOWN) {
        lastTimeButtonStateChanged_BTN_DOWN = millis();
        lastButtonState_BTN_DOWN = buttonState;
        if (buttonState == LOW) {
          downHandler();
          displayHandler();
        }
      }
    }
};

void modeHandler() {
  if (currentMode <= 3) {
    currentMode ++; 
  } else {
    currentMode = 0;
  }
}

void okHandler(){
  if (currentMode == 0) return;
  if (currentState == ready) {
    currentState = set;
  }

  if (currentState == set) {
    if (currentMode == 1 || currentMode == 2 || currentMode == 3) {
      if (currentSetup == none) {
        currentSetup = power;
        return;
      } else if (currentSetup == power) {
        currentSetup = timer;
        return;
      } else {
        currentState = busy;
        timerActive = true;
        return;
      }
    } else if (currentMode == 4) {
      if (currentSetup == none) {
        currentSetup = temp;
        return;
      } else if (currentSetup == temp) {
        currentSetup = power;
        return;
      } else {
        currentState = busy;
        timerActive = true;
        return;
      }
    }
  } else {
    return;
  }
};

void upHandler(){
  if (currentMode == 0) return;
  if (currentState == ready) return;
  if (currentSetup == none) return;

  if (currentState == set) {
    switch (currentSetup){
      case power:
        if (setPower <= 90) {
          setPower += 10;
        } else {
          setPower = 10;
        }
        break;

      case timer:
        if (setTimer <= 90) {
          setTimer += 10;
        } else {
          setTimer = 0;
        }
        break;
      case temp:
        if (setTemp <= 24) {
          setTemp += 1;
        } else {
          setTemp = 10;
        }
        break;
    }
  }
};

void downHandler(){
  if (currentMode == 0) return;
  if (currentState == ready) return;
  if (currentSetup == none) return;

  if (currentState == set) {
    switch (currentSetup){
      case power:
        if (setPower >= 20) {
          setPower -= 10;
        } else {
          setPower = 100;
        }
        break;
      case timer:
        if (setTimer >= 10) {
          setTimer -= 10;
        } else {
          setTimer = 100;
        }
      case temp:
        if (setTemp >= 11) {
          setTemp -= 1;
        } else {
          setTemp = 25;
        }
    }
  }
};

void displayHandler(){
  lcd.clear();

  lcd.setCursor(0,0);
  switch(currentMode){
   case 0:
      lcd.print("UIT");
      break;
    case 1:
      lcd.print("VENTILEREN");
      break;
    case 2:
      lcd.print("VERKOELEN");
      break;
    case 3:
      lcd.print("VERWARMEN");
      break;
    case 4:
      lcd.print("AUTO");
      break;
  }

  lcd.setCursor(0,1);
  if (currentState != busy){

    switch (currentSetup) {
      case power:
        lcd.print(String("Vermogen: ") + String(setPower) + String("%"));
        break;
      case timer:
        lcd.print(String("Timer: ") + String(setTimer) + String(" sec"));
        break;
      case temp:
        lcd.print(String("Temp: ") + String(setTemp) + String("C"));
        break;
      default:
        lcd.print("");
        break;
    }

  } else {

    if (currentMode == 4) {
       lcd.print(String("Temp ") + String(currentTemp) + String("C"));
    } else if (setTimer == 0) {
      lcd.print(String("BZZZZZzzzz"));
    } else {
      lcd.print(String("Nog ") + String(setTimer) + String(" sec"));
    }

  }
};

void resetAll() {
  lcd.clear();
  currentState = ready;
  currentSetup = none;
  currentMode = 0; //0 OFF; 1 VENT; 2 COOL; 3 WARM; 4 AUTO
  currentTemp = random(10, 25);
  setPower = 10; 
  setTimer = 0;
  setTemp = 10;
  timerActive = false;
}
$abcdeabcde151015202530354045505560fghijfghij