#include <Encoder.h>                            // https://www.pjrc.com/teensy/td_libs_Encoder.html
#include <Wire.h>
#include <hd44780.h>                       // main hd44780 header
#include <hd44780ioClass/hd44780_I2Cexp.h> // i2c expander i/o class header
hd44780_I2Cexp lcd(0x27); // declare lcd object at address 0x27 (use i2c address zero to auto locate address)



const byte relaiPin     = 9;    // le relai
const byte buzPin       = 10;    // un buzzer pour le fun
const byte encoderCLKPin  = 2;    // encoder encoder CLK
const byte encoderDTPin   = 3;    // encoder encoder DT
const byte tickShift = 2; // nombre de décalages à faire sur la valeur (puissance de 2) ici 2**2 => 4 ticks pour 1 valeur

const long minBPM = 0;
const unsigned long dureeImpulsion = 100; // ms
const long maxBPM = 60000ul / dureeImpulsion; // pour que la pause soit toujours positive
long currentBPM = 60;
unsigned long chrono;

Encoder encoder(encoderDTPin, encoderCLKPin);

bool encoderChanged() {
  long newPosition = encoder.read() >> tickShift;
  if (newPosition != currentBPM) {
    if (newPosition < minBPM) {
      currentBPM = minBPM;
      encoder.write(currentBPM << tickShift);
    }
    else if (newPosition > maxBPM) {
      currentBPM = maxBPM;
      encoder.write(currentBPM << tickShift);
    }
    else currentBPM = newPosition;

    return true;
  }
  return false;
}

void tickEncoder() {
  lcd.setCursor(4, 0);
  lcd.print("    ");
  lcd.setCursor(4, 0);
  lcd.print(currentBPM);
}

void configuration() {
  pinMode(relaiPin, OUTPUT);
  pinMode(buzPin, OUTPUT);
  Serial.begin(115200);
  encoder.write(currentBPM << tickShift);
  int status = lcd.begin(16, 2);
  if (status != 0) hd44780::fatalError(status); // does not return
  lcd.print("BPM:");
  lcd.print(currentBPM);
  chrono = millis();
  Serial.println("Pret!");
}


void interface() {
  if (encoderChanged()) tickEncoder();
}

void animation() {
  static bool enImpulsion = false;
  if (currentBPM != 0) {
    if (enImpulsion) {
      if (millis() - chrono >= dureeImpulsion) {
        digitalWrite(relaiPin, LOW);
        enImpulsion = false;
        chrono = millis();
      }
    } else {
      unsigned long dureePause = (60000ul / currentBPM) - dureeImpulsion;
      if (millis() - chrono >= dureePause) {
        digitalWrite(relaiPin, HIGH);
        tone(buzPin, 1000, dureeImpulsion);
        enImpulsion = true;
        chrono = millis();
      }
    }
  }
}

void setup() {
  configuration();
}

void loop() {
  interface();
  animation();
}