#include <digitalWriteFast.h>
#include <Streaming.h>
Print &cout {Serial};

constexpr uint8_t MEASURE_PIN {4};
//
// Class for measuring the time interval between two edge changes
// Initialized with HIGH -> from low to high - measurement - high to low).
// Initialized with LOW -> from HIGH to Low - measurement - Low to HIGH).
// HIGH is default.
//
class SptMeasurement {  // spt -> signal propagation time
public:
  SptMeasurement(uint8_t pin_, uint8_t activeState_ = HIGH) : pin {pin_}, activeState {activeState_} {}
  void begin() {
    pinMode(pin, INPUT);
    lastPinState = actPinState = !activeState;
  }
  uint32_t doTheMeasurment();
  void setDebounceTime(uint16_t dbt) { debounceTime = dbt; }
  void clearResult() { duration = 0; }

private:
  bool checkSignalChange();

private:
  uint8_t pin;
  uint8_t activeState;
  uint32_t timeStampDebounce {0};
  uint16_t debounceTime {5};
  uint8_t actPinState;
  uint8_t lastPinState;
  uint32_t timeStampStart {0};
  uint32_t duration {0};
};

bool SptMeasurement::checkSignalChange() {
  bool signalHasChanged = false;
  uint8_t pinState = digitalReadFast(pin);
  if (pinState != lastPinState) {   // Restart the debounce timer with every edge change
    lastPinState = pinState;
    timeStampDebounce = millis();
  } else if (pinState != actPinState && (millis() - timeStampDebounce >= debounceTime)) {
    // No more edge changes within the debounce time but the pinState is permanently unequal to the
    // previous one before the test.
    actPinState = pinState;
    signalHasChanged = true;
  }
  return signalHasChanged;
}

uint32_t SptMeasurement::doTheMeasurment() {
  if (checkSignalChange() == true) {   // Signal has been changed from low to high or vice versa
    if (actPinState == activeState) {
      timeStampStart = millis();
      duration = 0;
      cout << F("Start\n");
    } else {
      duration = millis() - timeStampStart;
    }
  }
  return duration;
}
// Class definition end //////////////////////////////////////////////////////////////

SptMeasurement spt {MEASURE_PIN};   // Create new Object

void setup() {
  Serial.begin(115200);
  spt.begin();   // init Object
}

void loop() {
  uint32_t duration = spt.doTheMeasurment();
  if (duration != 0) {
    cout << F("Stop: ") << duration << " ms: ";
    switch (duration) {
      case 0 ... 1100: cout << F("Error 1\n"); break;
      case 1101 ... 2100: cout << F("Error 2\n"); break;
      case 2101 ... 3100: cout << F("Error 3\n"); break;
      case 3101 ... 4100: cout << F("Error 4\n"); break;
      case 4101 ... 5100: cout << F("Error 5\n"); break;
      case 5101 ... 6100: cout << F("Error 6\n"); break;
      default: cout << F("Zeitspanne > 6,1 Sekunden!"); break;
    }
    spt.clearResult();
  }
}