#include <avr/sleep.h>

#include <TM1637Display.h>
#include <ClickEncoder.h>
#include <TimerOne.h>

#define SLEEP_INTERRUPT_1 2
#define SLEEP_INTERRUPT_2 3
#define SPEAKER 5
#define LIGHT 6
#define ENCODER_BUTTON 2
#define ENCODER_A 3
#define ENCODER_B 4

#define MAX_SECONDS 10 * 60
#define SLEEP_TIMEOUT 60 * 1000;

#define CLK 12
#define DIO 11
 


ClickEncoder *encoder;
TM1637Display display(CLK, DIO);

uint8_t data[] = {0, 0, 0, 0};
int16_t last, left, milli, sleepTimer;
bool running, showEnd, lightOn;

void timerIsr() {
  encoder->service();
  if(running) {
    milli -= 1;
  }
  sleepTimer -= 1;
}

void feedSleepTimer() {
  sleepTimer = SLEEP_TIMEOUT;
}

void goToSleep() {
  displayOff();

  Timer1.detachInterrupt();

  set_sleep_mode(SLEEP_MODE_PWR_DOWN);
  sleep_enable();
  attachInterrupt(digitalPinToInterrupt(SLEEP_INTERRUPT_1), wakeUp, CHANGE);
  attachInterrupt(digitalPinToInterrupt(SLEEP_INTERRUPT_2), wakeUp, CHANGE);
  sleep_mode();
  // Zzzzz. When we wake, we resume here...
  sleep_disable();
  detachInterrupt(digitalPinToInterrupt(SLEEP_INTERRUPT_2));
  detachInterrupt(digitalPinToInterrupt(SLEEP_INTERRUPT_1));

  // Run setup again, so everything is back to the initial state
  setup();
}

void wakeUp() {
  // This happens after we wake up.
  feedSleepTimer();
}

void displayOff() {
  // We may need to turn off the display using hardware...
  display.setBrightness(0, false);
  display.setSegments(data);
}

void displayOn() {
  display.setBrightness(7, true);
  display.setSegments(data);
}

void displayEnd() {
//
//      A
//     ---
//  F |   | B
//     -G-
//  E |   | C
//     ---
//      D
// XGFEDCBA
  data[0] = 0;
  data[1] = 0b01111001;
  data[2] = 0b01010100;
  data[3] = 0b01011110;
}

void displayTime(void) {
  int8_t minutes = left / 60;
  int8_t seconds = left % 60;

  uint8_t dot = (!running || milli < 500) ? 0x80 : 0x0;
    
  data[0] = display.encodeDigit(minutes / 10);
  data[1] = display.encodeDigit(minutes % 10) | dot;
  data[2] = display.encodeDigit(seconds / 10);
  data[3] = display.encodeDigit(seconds % 10);
}

void updateTime(void) {
  if(showEnd) {
    displayEnd();
  } else {  
    displayTime();
  }
  
  display.setSegments(data);
}
  


char BOUTON_WAVE       = 999;
char BOUTON_RESISTANCE = 999;


char RELAY_WAVE        = 999;
char RELAY_RESISTANCE  = 999;





void setup()
{
pinMode(BOUTON_WAVE,INPUT);
pinMode(BOUTON_RESISTANCE,INPUT);


pinMode(RELAY_WAVE,OUTPUT);
pinMode(RELAY_RESISTANCE,OUTPUT);

encoder = new ClickEncoder(ENCODER_A, ENCODER_B, ENCODER_BUTTON, 4, HIGH);
  encoder->setAccelerationEnabled(true);
    
  Timer1.initialize(1000);
  Timer1.attachInterrupt(timerIsr);

  pinMode(LIGHT, OUTPUT);

  running = false;
  showEnd = false;
  lightOn = false;
  
  left = 0;
  last = 0;
  milli = 1000;
  
  feedSleepTimer();
  displayOn();
  updateTime();

}

void checkButton() {
  ClickEncoder::Button b = encoder->getButton();
  if(b == ClickEncoder::Released) {
    running = !running;
  }
}

void checkLight() {
  if(running && !lightOn) {
    digitalWrite(LIGHT, HIGH);
    lightOn = true;
  } else if(!running && lightOn) {
    digitalWrite(LIGHT, LOW);
    lightOn = false;
  }
}

void done() {
  showEnd = true;
  tone(SPEAKER, 1500, 2000);
}


void loop() {
  // put your main code here, to run repeatedly:



checkButton();
  
  left += encoder->getValue();
  
  if(left > MAX_SECONDS) {
    left = MAX_SECONDS;
  }
  
  if(milli <= 0) {
    left--;
    milli = 1000;
  }

  if(left < 0) {
    left = 0;
  }

  // If the timer is running, the display needs to be updated every 500ms, otherwise it only needs updating if the encode value changes.
  if ((running && milli / 500 % 2 == 0) || left != last) {
    if(left != last) {
      last = left;
    }
    
    if(running && left == 0) {
      running = false;
      done();
    } else {
      showEnd = false;
    }
    
    updateTime();
    feedSleepTimer();
  }

  checkLight();

  if(sleepTimer == 0) {
    goToSleep();
  }



}
