#include "wiring_constants.h"
#ifndef Buttons_h
#define Buttons_h

#include "Arduino.h"
#include "Adafruit_SH110X.h"
#include "Adafruit_GFX.h"
extern Adafruit_SH1106G display;

#define DURATION_SHORT_PRESS 800
#define DURATION_VERY_LONG_PRESS 2000





class Button {
  int pin;
  bool has_been_pressed;
  unsigned long press_start;
  unsigned long release_start;
  std::function<void()> onPressFunction;
  std::function<void()> onHoldFunction;
  std::function<void()> onLongHoldFunction;
  std::function<void()> onVeryLongHoldFunction;
  std::function<void()> onLongPressFunction;
  std::function<void()> onVeryLongPressFunction;
  std::function<void()> onReleasedFunction;

  public:
    Button(int pin);
    void init();
    void read();
    unsigned long pressed_since();
    unsigned long released_since();

    void onPress(std::function<void()> f);
    void onHold(std::function<void()> f);
    void onLongHold(std::function<void()> f);
    void onVeryLongHold(std::function<void()> f);
    void onLongPress(std::function<void()> f);
    void onVeryLongPress(std::function<void()> f);
    void onReleased(std::function<void()> f);

    void reset();
};

Button::Button(int pin) {
  this->pin = pin;
}

void Button::init() {
  pinMode(this->pin, INPUT_PULLUP);
  this->has_been_pressed = false;
  this->press_start = 0;
  this->release_start = 0;
}

void Button::read() {
  int is_pressed = !digitalRead(this->pin);
  
  if (is_pressed && this->press_start == 0) {
    this->press_start = millis();
  }
  if (!is_pressed && this->has_been_pressed && this->release_start == 0) {
    this->release_start = millis();
  }

  unsigned long pressed_since = this->pressed_since();
  unsigned long released_since = this->released_since();
  
  if (is_pressed) {
    // Fire the callback function all the time while this is being pressed
    if (this->onHoldFunction) { this->onHoldFunction(); }

    if (this->pressed_since() > 1000) {
      if (this->onLongHoldFunction) { this->onLongHoldFunction(); }
    }
    if (this->pressed_since() > 5000) {
      if (this->onVeryLongHoldFunction) { this->onVeryLongHoldFunction(); }
    }
    // Serial.print("Pressed since ");
    // Serial.println(pressed_since);
    if ( released_since > 100) {
      this->has_been_pressed = false;
    }
  } else {
    // Not pressed.
    if (!this->has_been_pressed) {
      if (pressed_since > 0 && pressed_since < DURATION_SHORT_PRESS) {
        if (this->onPressFunction) { this->onPressFunction(); }
        // Serial.print("Short Press (released after ");
        // Serial.print(pressed_since);
        // Serial.print(", released since ");
        // Serial.print(released_since);
      } else if (pressed_since > 0 &&  pressed_since < DURATION_VERY_LONG_PRESS) {
        if (this->onLongPressFunction) { this->onLongPressFunction(); }
        // Serial.print("Long Press (released after ");
        // Serial.print(pressed_since);
        // Serial.println(")");
      } else if (pressed_since > 0 && pressed_since >= DURATION_VERY_LONG_PRESS) {
        if (this->onVeryLongPressFunction) { this->onVeryLongPressFunction(); }
        // Serial.print("Very Long Press (released after ");
        // Serial.print(pressed_since);
        // Serial.println(")");
      }
      this->press_start = 0;
      this->has_been_pressed = true;
      this->release_start = millis();
      if (this->onReleasedFunction) { this->onReleasedFunction(); }
    }
  }
}

unsigned long Button::pressed_since() {
  if ( this->press_start == 0) {
    return 0;
  }
  return millis() - this->press_start;
}

unsigned long Button::released_since() {
  if ( this->release_start == 0) {
    return 0;
  }
  return millis() - this->release_start;
}

void Button::onPress(std::function<void()> f) {
  this->onPressFunction = f;
}

void Button::onHold(std::function<void()> f) {
  this->onHoldFunction = f;
}

void Button::onLongHold(std::function<void()> f) {
  this->onLongHoldFunction = f;
}

void Button::onVeryLongHold(std::function<void()> f) {
  this->onVeryLongHoldFunction = f;
}

void Button::onLongPress(std::function<void()> f) {
  this->onLongPressFunction = f;
}

void Button::onVeryLongPress(std::function<void()> f) {
  this->onVeryLongPressFunction = f;
}

void Button::onReleased(std::function<void()> f) {
  this->onReleasedFunction = f;
}

void Button::reset() {
  this->onPressFunction = NULL;
  this->onHoldFunction = NULL;
  this->onLongPressFunction = NULL;
  this->onVeryLongPressFunction = NULL;
}


#endif