#ifndef __Sensor_h__
#define __Sensor_h__

#include <ADC.h>
#include "Debug.h"
#include "HamFuncs.h"

/**********************************************************************/

#ifndef UBITX_SENSORS_S_METER_PIN
#define UBITX_SENSORS_S_METER_PIN 28
#endif

#ifndef UBITX_SENSORS_FWD_PWR_PIN
#define UBITX_SENSORS_FWD_PWR_PIN 26
#endif

#ifndef UBITX_SENSORS_REV_PWR_PIN
#define UBITX_SENSORS_REV_PWR_PIN 20
#endif

#ifndef UBITX_SENSORS_SUPPLY_PIN
#define UBITX_SENSORS_SUPPLY_PIN 21
#endif

#ifndef UBITX_SENSORS_SPARE1_PIN
#define UBITX_SENSORS_SPARE1_PIN 27
#endif

#ifndef UBITX_SENSORS_SPARE2_PIN
#define UBITX_SENSORS_SPARE2_PIN 31
#endif

#ifndef UBITX_SENSORS_AVG_SAMPLES
#define UBITX_SENSORS_AVG_SAMPLES 16
#endif

#ifndef UBITX_SENSORS_S_METER_R1
#define UBITX_SENSORS_S_METER_R1 0.0
#endif

#ifndef UBITX_SENSORS_S_METER_R2
#define UBITX_SENSORS_S_METER_R2 1.0
#endif

#ifndef UBITX_SENSORS_FWD_PWR_R1
#define UBITX_SENSORS_FWD_PWR_R1 22000.0
#endif

#ifndef UBITX_SENSORS_FWD_PWR_R2
#define UBITX_SENSORS_FWD_PWR_R2 33000.0
#endif

#ifndef UBITX_SENSORS_REV_PWR_R1
#define UBITX_SENSORS_REV_PWR_R1 22000.0
#endif

#ifndef UBITX_SENSORS_REV_PWR_R2
#define UBITX_SENSORS_REV_PWR_R2 33000.0
#endif

#ifndef UBITX_SENSORS_SUPPLY_R1
#define UBITX_SENSORS_SUPPLY_R1 56000.0
#endif

#ifndef UBITX_SENSORS_SUPPLY_R2
#define UBITX_SENSORS_SUPPLY_R2 10000.0
#endif

#ifndef UBITX_SENSORS_S_METER_LVL0
#define UBITX_SENSORS_S_METER_LVL0 2
#endif

#ifndef UBITX_SENSORS_S_METER_LVL1
#define UBITX_SENSORS_S_METER_LVL1 4
#endif

#ifndef UBITX_SENSORS_S_METER_LVL2
#define UBITX_SENSORS_S_METER_LVL2 8
#endif

#ifndef UBITX_SENSORS_S_METER_LVL3
#define UBITX_SENSORS_S_METER_LVL3 16
#endif

#ifndef UBITX_SENSORS_S_METER_LVL4
#define UBITX_SENSORS_S_METER_LVL4 32
#endif

#ifndef UBITX_SENSORS_S_METER_LVL5
#define UBITX_SENSORS_S_METER_LVL5 64
#endif

#ifndef UBITX_SENSORS_S_METER_LVL6
#define UBITX_SENSORS_S_METER_LVL6 128
#endif

#ifndef UBITX_SENSORS_S_METER_LVL7
#define UBITX_SENSORS_S_METER_LVL7 256
#endif

#ifndef UBITX_SENSORS_S_METER_LVL8
#define UBITX_SENSORS_S_METER_LVL8 512
#endif

/**********************************************************************/

const int   uBitxSensorsSMeterPin  = UBITX_SENSORS_S_METER_PIN;
const int   uBitxSensorsFwdPwrPin  = UBITX_SENSORS_FWD_PWR_PIN;
const int   uBitxSensorsRevPwrPin  = UBITX_SENSORS_REV_PWR_PIN;
const int   uBitxSensorsSupplyPin  = UBITX_SENSORS_SUPPLY_PIN;
const int   uBitxSensorsSpare1Pin  = UBITX_SENSORS_SPARE1_PIN;
const int   uBitxSensorsSpare2Pin  = UBITX_SENSORS_SPARE2_PIN;
const int   uBitxSensorsAvgSamples = UBITX_SENSORS_AVG_SAMPLES;
const float uBitxSensorsSMeterR1   = UBITX_SENSORS_S_METER_R1;
const float uBitxSensorsSMeterR2   = UBITX_SENSORS_S_METER_R2;
const float uBitxSensorsFwdPwrR1   = UBITX_SENSORS_FWD_PWR_R1;
const float uBitxSensorsFwdPwrR2   = UBITX_SENSORS_FWD_PWR_R2;
const float uBitxSensorsRevPwrR1   = UBITX_SENSORS_REV_PWR_R1;
const float uBitxSensorsRevPwrR2   = UBITX_SENSORS_REV_PWR_R2;
const float uBitxSensorsSupplyR1   = UBITX_SENSORS_SUPPLY_R1;
const float uBitxSensorsSupplyR2   = UBITX_SENSORS_SUPPLY_R2;

const int uBitxSensorsSMeterValues[] = {
  UBITX_SENSORS_S_METER_LVL0,
  UBITX_SENSORS_S_METER_LVL1,
  UBITX_SENSORS_S_METER_LVL2,
  UBITX_SENSORS_S_METER_LVL3,
  UBITX_SENSORS_S_METER_LVL4,
  UBITX_SENSORS_S_METER_LVL5,
  UBITX_SENSORS_S_METER_LVL6,
  UBITX_SENSORS_S_METER_LVL7,
  UBITX_SENSORS_S_METER_LVL8
};

const int uBitxSensorsSMeterLevels = sizeof(uBitxSensorsSMeterValues) / 
                                     sizeof(uBitxSensorsSMeterValues[0]);

extern ADC adc;

/**********************************************************************/

/*!
 *  @brief  Class that maintains a "trailing average" of the last X
 *          samples provided.  It is a template that can be instantiated
 *          with both the (numeric) data type that is being stored and
 *          averaged, as well as the number of samples to maintain the
 *          trailing average across.
 */
template <typename T, int N>
class TrailingAverage {
  public:
  /*!
   *  @brief  Create a new TrailingAverage object.  Data type averaged,
   *          and number of elements to average, are determined when the
   *          template is instantiated.
   */
  TrailingAverage(): 
  average(T(0)),
  current(0),
  divisor(T(N))
  {
    for (int i = 0; i < N; i++) {
      data[i] = T(0);
    }
  }

  /*!
   *  @brief  Add a new element to the average.  The current last (Nth)
   *          element is removed, and the new element is added.
   *  @param  val
   *          The new element/value to incorporate into the average.
   */
  inline void add(T val) {
    //int last = (current - 1) % N;
    //average -= data[last];
    //current = (current + 1) % N;
    //data[current] = val / divisor;
    //average += data[current];
    average -= data[current];
    data[current] = val / divisor;
    average += data[current];
    current = (current + 1) % N;    
  }

  /*!
   *  @brief  Read the current value of the average.
   *  @return The current average.
   */
  inline T read() {
    return average;
  }

  private:
  T data[N];
  T average;
  int current;
  T divisor;
};

/**********************************************************************/

/*!
 *  @brief  Class that handles the various sensors in the uBitx: 
 *          S-Meter, forward/reverse power and SWR, and supply voltage.
 */
class UBitxSensors {
  public:
  /*!
   *  @brief  Create a new UBitxSensors object.  It uses the default
   *          S-Meter, Forward Power, Reverse Power, and Supply Voltage
   *          ADC pins.
   */
  UBitxSensors():
  sMeterPin(uBitxSensorsSMeterPin),
  fwdPwrPin(uBitxSensorsFwdPwrPin),
  revPwrPin(uBitxSensorsRevPwrPin),
  supplyPin(uBitxSensorsSupplyPin),
  spare1Pin(uBitxSensorsSpare1Pin),
  spare2Pin(uBitxSensorsSpare2Pin)
  {
    pinMode(sMeterPin, INPUT);  // analog
    pinMode(fwdPwrPin, INPUT);  // analog
    pinMode(revPwrPin, INPUT);  // analog
    pinMode(supplyPin, INPUT);  // analog
    pinMode(spare1Pin, INPUT);  // analog
    pinMode(spare2Pin, INPUT);  // analog
  }

  /*!
   *  @brief  Update the value of the S-Meter by reading the associated
   *          ADC pin.
   */
  inline void updateSMeter() {
    int value = adc.analogRead(sMeterPin);
    sMeter.add(value);
  }

  /*!
   *  @brief  Update the value of the Forward and Reverse Power 
   *          measurements by reading the associated ADC pin.
   */
  void updatePower() {
    ADC::Sync_result value = adc.analogSyncRead(revPwrPin, fwdPwrPin);

    float fwdV = HF::adcIn(value.result_adc0);
    float revV = HF::adcIn(value.result_adc1);

    fwdV = HF::divIn(fwdV, uBitxSensorsFwdPwrR1, uBitxSensorsFwdPwrR2);
    fwdV = HF::bridgeFwd(fwdV);

    revV = HF::divIn(revV, uBitxSensorsRevPwrR1, uBitxSensorsRevPwrR2);
    revV = HF::bridgeFwd(revV);

    fwdPwr.add(HF::P(fwdV));
    revPwr.add(HF::P(revV));
    vswr.add(HF::VSWR(fwdV, revV));
  }

  /*!
   *  @brief  Update the value of the Supply Voltage measurement by 
   *          reading the associated ADC pin.
   */
  inline void updateSupply() {
    float value = HF::adcIn(adc.analogRead(supplyPin));
    value = HF::divIn(value, uBitxSensorsSupplyR1, uBitxSensorsSupplyR2);
    supply.add(value);
  }

  /*!
   *  @brief  Return the unscaled value of the S-Meter reading.
   *  @return Unscaled S-Meter reading.
   */
  inline int sMeterUnscaled() {
    return sMeter.read();
  }

  /*!
   *  @brief  Return the scaled value of the S-Meter reading.  This
   *          is the value that is used to directly control the S-Meter
   *          display on the Nextion LCD.
   *  @return Scaled S-Meter reading.
   */
  int sMeterScaled() {
    int sig = sMeter.read() >> 2;
    // small number of elements; just doing a linear search
    for (int i = uBitxSensorsSMeterLevels; i > 0; i--) {
      if (sig > uBitxSensorsSMeterValues[i - 1]) {
        return i;
      }
    }
    return 0;
  }

  /*!
   *  @brief  Return the current Forward Power measurement.
   *  @return Forward Power measurement.
   */
  inline float Pfwd() {
    return fwdPwr.read();
  }

  /*!
   *  @brief  Return the current Reverse Power measurement.
   *  @return Reverse Power measurement.
   */
  inline float Prev() {
    return revPwr.read();
  }

  /*!
   *  @brief  Return the current Voltage Standing Wave Ration (VSWR).
   *  @return Current VSWR calculation.
   */
  inline float VSWR() {
    return vswr.read();
  }

  /*!
   *  @brief  Return the current Voltage Standing Wave Ration (VSWR),
   *          scaled for the Nextion display protocol.
   *  @return Current VSWR calculation (scaled).
   */
  float scaledVSWR() {
    int val = int(vswr.read());
    if (val < 0) {
      return 0;
    } else if (val > uBitxSensorsSMeterLevels) {
      return uBitxSensorsSMeterLevels;
    } else {
      return val;
    }
  }

  /*!
   *  @brief  Return the current Supply Voltage measurement.
   *  @return Current Supply Voltage.
   */
  inline float supplyVoltage() {
    return supply.read();
  }

  private:  

  // Pins
  int sMeterPin;
  int fwdPwrPin;
  int revPwrPin;
  int supplyPin;
  int spare1Pin;
  int spare2Pin;

  // Buffers for averages
  TrailingAverage<int, uBitxSensorsAvgSamples>   sMeter;
  TrailingAverage<float, uBitxSensorsAvgSamples> fwdPwr;
  TrailingAverage<float, uBitxSensorsAvgSamples> revPwr;
  TrailingAverage<float, uBitxSensorsAvgSamples> vswr;
  TrailingAverage<float, uBitxSensorsAvgSamples> supply;
};

extern UBitxSensors Sensors;

#endif
