#ifndef MQ2GASSENSOR_H
#define MQ2GASSENSOR_H

#include <Arduino.h>
#include <Wire.h>
#include "common.h"
#include "LCDHelper.h"

/**
 * This class encapsulates
 */
class MQ2GasSensorNonBlocking {
private:
  byte loadRes;
  byte sensorAnalogPIN;
  byte sensorDigitalPIN;
  float airFactor;
  const float* smokeCurve;
  byte sensorState;
  word sensorValue;
  float baselineResistance;   //resistance of sensor after calibration
  float currResistance;
  LCDHelper* lcdHelper;
  unsigned long sensorWarmupTime;
  byte readSamples;
  unsigned short readInterval;

  int currADCValue;

  //variables for non blocking resistance reading
  bool measuringResistance;
  byte currSample;
  byte currSamplesCount;
  word currInterval;
  float measuredResistance;
  unsigned long lastResistanceMeasurementTime;

  // Fan RPM monitoring variables
  static MQ2GasSensorNonBlocking* instance; // Static instance pointer for interrupt handling
  bool getSensorResistance(byte samples, word interval, float* resistance);
  /**
   * Function does the initial sensor calibration. It performs initial resistance reading.
   *
   * @return When calibration is finished, it will return true. Otherwise, it returns false.
   */
  bool calibrateSensor();

  static void digitalInputTriggered(); // Static interrupt handler
  void (*onDigitalInputTriggeredCallback)();  // Function pointer for the callback
public:
    static constexpr byte DEFAULT_LOAD_RES = (byte)10;         // Load resistance value in kΩ
    static constexpr float DEFAULT_AIR_FACTOR = 9.83;     // Air factor for calibration (clean air reference resistance)
    static const float SMOKE_CURVE[];
    static constexpr byte CALIBRATION_SAMPLES_COUNT = 50;
    static constexpr unsigned short CALIBRATION_SAMPLE_INTERVAL = 500;
    static constexpr byte SAMPLES_COUNT = 5;
    static constexpr byte SAMPLE_INTERVAL = 50;

    /**
     * This is minimum sensing resistance of the sensor according to datasheet in kOhms
     */
    static constexpr byte MIN_SENSING_RESISTANE = 2;
    /**
    * This is maximum sensing resistance of the sensor according to datasheet in kOhms
    */
    static constexpr byte MAX_SENSING_RESISTANCE = 20;

    static constexpr byte SENSOR_STATE_NOT_CALIBRATED = 0;
    static constexpr byte SENSOR_STATE_CALIBRATING = 1;
    static constexpr byte SENSOR_STATE_CALIBRATED = 2;

    // Constructors
    MQ2GasSensorNonBlocking(byte sensorAnalogPIN, byte sensorDigitalPIN);
    MQ2GasSensorNonBlocking(byte sensorAnalogPIN, byte sensorDigitalPIN, LCDHelper* lcdHelper);
    MQ2GasSensorNonBlocking(byte sensorAnalogPIN, byte sensorDigitalPIN ,LCDHelper* lcdHelper, unsigned long sensorWarmupTime, const float* smokeCurve);

    /**
     * This function attaches to analog and digital PIN specified in constructor
     */
    void attach();    
    /**
     * This function performs initial sensor calibration. It waits for sensor warm-up and does initial resistance reading. It is non-blocking.
     * You can tell when sensor is calibrated by calling {@link #isSensorCalibrated()}.
     */
    byte calibrateGasSensor();
    /**
     * This functions returns the raw sensor analog value.
     */
    word getSensorValue() const;
    /**
     * This functions returns sensor value recalculated to PPM.
     */
    word getSensorValuePPM() const;
    /**
     * This function returns the sensor resistance. It is not the "actual" value, but the last read. Resistance is being determined by multiple readings of sensor
     * value over some time, so this is from alst finished reading.
     */
    void measureSensorResistance();
    float getSensorResistance() const;
    float getSensorBaselineResistance() const;
    byte getSensorState() const;
    bool isSensorCalibrated() const;
    void onDigitalInputTriggered(void (*callback)()); // Method to set the callback

    void setSensorWarmupTime(unsigned long warmupTime);
    unsigned long getSensorWarmupTime() const;

    ~MQ2GasSensorNonBlocking();
};

#endif
