//******************************************************************************************
//  File: PS_Adafruit_MPR121.cpp
//  Authors: Luca Masera
//
//  Summary:  PS_Adafruit_MPR121 is a class that inherits from the st::InterruptSensor class.
//
//  Create an instance of this class in your sketch's global variable section. 
//  For Example:  st::PS_Adafruit_MPR121 sensor(F("button1"), 5, 0x10, 0x20, 1000);
//
//	st::PS_Adafruit_MPR121() constructor requires the following arguments
//  - String &name - REQUIRED - the name of the object, should be "touchButtons_Kitchen", "touchButtons_Desk", etc...
//  - int8_t activatedButtonId - REQUIRED - the id of the sensor that is used to activate the touching capability
//  - uint8_t sensibility5C = 0x10, together with sensibility5D sets the sensibility of the touch sensors
//  - uint8_t sensibility5D = 0x20,
//	- long reqNumMillisHeld = 1000 - milliseconds thesholdb between "Held" and "Pushed"
//
//  Change History:
//
//    Date        Who            What
//    2022-09-23  Luca Masera    Corrected bugs within inizialization and check of buttons status
//    2022-09-22  Luca Masera    Original creation
//
//******************************************************************************************

#include "PS_Adafruit_MPR121.h"

#include "Constants.h"
#include "Everything.h"

namespace st {
// private

// public
// constructor
PS_Adafruit_MPR121::PS_Adafruit_MPR121(const __FlashStringHelper *name,
                                       int8_t activateButtonId,
                                       uint8_t sensibility5C,
                                       uint8_t sensibility5D,
                                       long reqNumMillisHeld)
    : InterruptSensor(name, 2, false),  // use parent class' constructor
      m_sensibility5C(sensibility5C),
      m_sensibility5D(sensibility5D),
      m_lreqNumMillisHeld(reqNumMillisHeld),
      activateButtonId(activateButtonId){}

// destructor
PS_Adafruit_MPR121::~PS_Adafruit_MPR121() {}

void PS_Adafruit_MPR121::init() {
  // get current status of motion sensor by calling parent class's
  // init() routine - no need to duplicate it here!
  InterruptSensor::init();

  Serial.println("Adafruit MPR121 Capacitive Touch sensor");

  // Default address is 0x5A, if tied to 3.3V its 0x5B
  // If tied to SDA its 0x5C and if SCL then 0x5D
  if (!cap.begin(m_sensibility5C, m_sensibility5D, 0x5A)) {
    Serial.println(F("MPR121 not found, check wiring?"));

  } else {
    Serial.println(F("MPR121 found!"));
    refresh();
  }
}

// called periodically by Everything class to ensure ST Cloud is kept up yo
// date. HOWEVER, not useful for the IS_Button.
void PS_Adafruit_MPR121::refresh() {
  if (isBlockedStatus) {
    isBlockedStatus = false;
    Serial.println("Blocked status: trying to perform a soft reset to restart the sensor.");

    this->init();
  }

  // Send 'init' message to allow Parent Driver to set numberOfButtons
  // attribute automatically
  Everything::sendSmartString(getName() + F(" init"));
}

void PS_Adafruit_MPR121::update() {  // Get the currently touched pads
// get current time 
long currentTime = millis();

// get current touch array
curr_touched = cap.touched();

// had an over current or out of range?
isBlockedStatus = cap.hadOverCurrent(curr_touched) || cap.wentOutOfRange();

if (!isBlockedStatus) {
  
  // A) if not active...
  if (!isActive) {
    // B) if the touch is to activate the sensor (ACTIVATE_BUTTON)... 
    if (bitRead(curr_touched, activateButtonId) && !(bitRead(old_touched, activateButtonId))) {
      //    register activation time and ...
      Serial.println(F("activation touched"));
      activationStart = currentTime;
    
    } else if ((bitRead(old_touched, activateButtonId)) && bitRead(old_touched, activateButtonId)) {
      // C) if the activation time is bigger than the ACTIVATION_THRESOLD... 
      if (currentTime - activationStart > _ACTIVATION_THRESOLD) {
        // activate the sensor and register active time
        Serial.println(F("activated"));
        waitingActive = currentTime;
        isActive = true;
      }
    }

  // ... is active
  } else {
    // D) first check if still active, or at least hold
    if ((currentTime - waitingActive > _ACTIVE_TIMEOUT) && !isHold) {
      // if not, deactivate the sensor
      Serial.println(F("deactivated"));
      isActive = false;

    } else {
      // is active or hold, check what's going on with the buttons
      // for each button (we have 12), check the status of the bit in the touch array (ignore ACTIVATE_BUTTON)
      for (uint8_t currentButton = 0; currentButton < 12; currentButton++) {
        if (currentButton != activateButtonId) {
          // E) if positive and different from the old status then ...
          if (bitRead(curr_touched, currentButton) && !(bitRead(old_touched, currentButton))) {
            //... is a touch: register the TOUCH and store the touch time (for hold status)
            Serial.print("touched: ");
            Serial.println(currentButton);
            startTouch[currentButton] = currentTime;
            waitingActive = currentTime;
            Everything::sendSmartStringNow(getName() + String(currentButton) + _PUSHED);
            
            // F) if positive and equal to the old status then ...
            } else if (bitRead(curr_touched, currentButton) && (bitRead(old_touched, currentButton))) {
              // ... G) it could be a hold: if the HOLD_THRESOLD has been reached for the current button...
              if (!(bitRead(isHold, currentButton)) && (currentTime - startTouch[currentButton] > HOLD_THRESOLD)) {
                // ... register the HOLD and store a flag for the button
                Serial.print("held: ");
                Serial.println(currentButton);
                bitSet(isHold, currentButton);
                waitingActive = currentTime;
                Everything::sendSmartString(getName() + String(currentButton) + _HELD);
              }

            // H) if negative and different from the old status then ... 
            } else if (!(bitRead(curr_touched, currentButton)) && bitRead(old_touched, currentButton)) {
            // ... it means that there's no touch anymore 
            // I) if it was hold... 
            if (bitRead(isHold, currentButton)) {
            // ... reset the flag for the button
              Serial.print("stop held: ");
              Serial.println(currentButton);
              bitClear(isHold, currentButton);
            }
            // register the RELEASE for the button
            Serial.print("released: ");
            Serial.println(currentButton);
            Everything::sendSmartString(getName() + String(currentButton) + _RELEASED);
            }
          }
        }
      }
    }
  }
  
  old_touched = curr_touched;
}
  

void PS_Adafruit_MPR121::runInterrupt() {}

void PS_Adafruit_MPR121::runInterruptEnded() {}

}  // namespace st