#include "coinacceptor.h"

CoinAcceptor::CoinAcceptor(uint8_t pulsePin, uint8_t enablePin, const unsigned int* pulseValues, const size_t maxPulses, const uint8_t pulseWidthMs) : 
  pulsePin(pulsePin), enablePin(enablePin), pulseValues(pulseValues), maxPulses(maxPulses), pulseWidthMs(pulseWidthMs)  {}

// Initialisierung des Pins & Interrupts
void CoinAcceptor::begin() {
    pinMode(enablePin, OUTPUT);
    pinMode(pulsePin, INPUT_PULLUP);
    disable();
    if(digitalRead(pulsePin) == HIGH)
    {
      Serial.println("Attaching interrupt to falling edge of coin acceptor pulse pin.");
      attachInterruptArg(digitalPinToInterrupt(pulsePin), pulseIsrHandler, this, FALLING);
    }
    else
    {
      Serial.println("Attaching interrupt to rising edge of coin acceptor pulse pin.");
      attachInterruptArg(digitalPinToInterrupt(pulsePin), pulseIsrHandler, this, RISING);      
    }
}

void IRAM_ATTR CoinAcceptor::pulseIsrHandler(void* arg) {
  CoinAcceptor* self = static_cast<CoinAcceptor*>(arg);
  unsigned long currentMs = micros()/1000;
  if((self->lastPulseAtMs == 0) || ((currentMs - self->lastPulseAtMs) > 1.5*self->pulseWidthMs))
  {
    self->coinDetected = true;
    self->lastPulseAtMs = currentMs;
    self->coinPulses++;
  }
}

void CoinAcceptor::disable() {
  accState = ACC_DISABLED;
  digitalWrite(enablePin, accState);
}

void CoinAcceptor::enable() {
  accState = ACC_ENABLED;
  coinDetected = false;
  detectionError = 0;
  coinPulses = 0;
  coinValue = 0;
  lastPulseAtMs = 0;   
  digitalWrite(enablePin, accState);
}

uint8_t CoinAcceptor::state() {
  return accState;
}

bool CoinAcceptor::detectCoin() {
  if(true == coinDetected)
  {
    disable();
    long lastPulseBeforeMs = 0;
    long maxMsSinceLastPulse = 8*pulseWidthMs;
    do
    {
      lastPulseBeforeMs = millis() - lastPulseAtMs;
    } while ((lastPulseAtMs == 0) || (lastPulseBeforeMs < maxMsSinceLastPulse ));
    Serial.printf("DONE --- Pulses: %d, Last pulse before: %d ms (max allowed: %d). \n", coinPulses, lastPulseBeforeMs, maxMsSinceLastPulse);
    // there is one additial edge in the signal ... remove it
    coinPulses--;
    if((coinPulses > 1) && (coinPulses <= maxPulses))
    {
      coinValue = pulseValues[coinPulses-1]; // coin array index is running from 0 to maxPulses-1 ...
      Serial.printf(" --- Detected coin value: %d cents\n", coinValue);
    }
    else
    {
      Serial.printf(" ERROR --- Number of pulses doesn't match any teached coin value\n");
      detectionError++;
    }
    coinDetected = false;
    return true;
  }
  return false;
}

uint16_t CoinAcceptor::getCoinValue() {
  return coinValue;
}