//======================================================================
// DSP.h
//======================================================================

#ifndef __DSP_h__
#define __DSP_h__

#include <Audio.h>
#include <dynamicFilters.h>
#include "Debug.h"

/**********************************************************************
 * Macros
 **********************************************************************/

#define MIN_RX_FILTER_LO      (0.0)     //! Min allowable value of the RX filter low-cut frequency
#define MAX_RX_FILTER_HI      (5000.0)  //! Max allowable value of the RX filter hi-cut frequency
#define MIN_RX_FILTER_WIDTH   (0.0)     //! Min allowable value of the RX filter bandwidth
#define MAX_RX_FILTER_WIDTH   (5000.0)  //! Max allowable value of the RX filter bandwidth
#define MIN_RX_FILTER_CENTER  (0.0)     //! Min allowable value of the RX filter center frequency
#define MAX_RX_FILTER_CENTER  (5000.0)  //! Max allowable value of the RX filter center frequency

#define DSP_MILLIS_PER_UPDATE (100)   //! Number of milliseconds between update of the DSP object

#define TX_VOX_MIC_THRESH   (0.0)     //! Threshold for mic VOX (not implemented, since mic requires special handling)
#define TX_VOX_LINE_THRESH  (0.25)    //! Threshold for line in VOX
#define TX_VOX_USB_THRESH   (0.25)    //! Threshold for USB VOX
#define TX_VOX_TUNE_THRESH  (0.0)     //! Threshold for tune (single tone) VOX (not expected to be used)
#define TX_VOX_TT_THRESH    (0.0)     //! Threshold for two-tone VOX (not expected to be used)
#define TX_VOX_DELAY        (500)     //! VOX delay in milliseconds

/**********************************************************************
 * Enumerations
 **********************************************************************/

//! Defines the four separate RX audio input channels available.
enum RxAudioCh {
  RX_AUDIO = 0,       // Normal receiver audio input channel
  RX_SPARE,           // Not used
  RX_TONE1 ,          // Optional tone #1 input channel (currently not used)
  RX_TONE2,           // Optional tone #2 input channel (currently not used)
  NUM_RX_AUDIO_CH     // Total number of channels
};

//! Defines the different RX audio inputs (not channels).
enum RxAudioIn {
  RIG_IN = 0,         // Normal rig input (receiver audio)
  NUM_RX_AUDIO_IN     // Total number of inputs
};

//! Defines the different RX audio outputs.
enum RxAudioOut {
  LINE_OUT = 0,       // Line audio out (and speaker)
  USB_OUT,            // USB audio out
  NUM_RX_AUDIO_OUT
};

//! Defines the four separate TX audio input channels available.
enum TxAudioCh {
  TX_LINE = 0,        // Line and/or mic audio input channel
  TX_USB,             // USB audio input channel
  TX_TONE1,           // Audio tone #1 input channel
  TX_TONE2,           // Audio tone #2 input channel
  NUM_TX_AUDIO_CH     // Toal number of channels
};

//! Defines the different TX audio input sources (not channels!).
enum TxAudioIn {
  MIC_IN = 0,         // Microphone transmit audio input
  LINE_IN,            // Line ("AUX") transmit audio input
  USB_IN,             // USB transmit audio input
  TUNE_IN,            // Tune input (transmits a single tone)
  TWO_TONE_IN,        // Two tone audio input (transmits two tones)
  NUM_TX_AUDIO_IN     // Total number of inputs
};

/**********************************************************************
 * Classes
 **********************************************************************/

//! Defines parameters for a simple audio channel that can be muted.
struct AudioChannel {
  bool mute = false;
  float level = 0.0;
};

/*!
 *  Contains the current 'persistent' state of the DSP.
 *  This includes all audio-specific state that can be saved to, or
 *  restored from, EEPROM.  It does not include 'transient' state (such 
 *  as whether we're currently transmitting or receiving).
 */
struct DSPState {
 
  //! Receiver audio inputs; all default to muted.
  AudioChannel rxIn[NUM_RX_AUDIO_CH] = {
    {true, 1.0},    // audio
    {true, 0.0},    // spare 1
    {true, 0.0},    // spare 2
    {true, 0.0}     // spare 3
  };

  //! Receiver audio output; defaults to un
  muted.
  AudioChannel rxOut[NUM_RX_AUDIO_OUT] = {
    {false, 1.0},   // line
    {false, 1.0}    // USB
  };

  //! Transmitter audio inputs; all default to muted.
  AudioChannel txIn[NUM_TX_AUDIO_CH] = {
    {true, 0.1},    // line
    {true, 0.1},    // USB
    {true, 0.1},    // tone 1
    {true, 0.1}     // tone 2
  };

  //! Tranmitter audio output; defaults to muted.
  AudioChannel txOut = {true, 1.0};

  //! Current RX filter settings
  float rxFilterLo = 300.0;
  float rxFilterHi = 3000.0;
};

/*!
 *  Defines the DSP subsystem of the UBitx V5X.
 *  The DSP subsystem, which relies on the Teensy Audio Library, is
 *  responsible for setting up the audio inputs and outputs for both
 *  receive (RX) and transmit (TX) audio, maintaining the correct path
 *  between inputs and outputs based on current TX/RX state, and setting
 *  up audio filters and other audio-based modules for the RX and TX
 *  audio paths.
 */
class UBitxDSP {

  /********************************************************************
   * Object creation/deletion
   ********************************************************************/

  public:  
  UBitxDSP() {}

  /********************************************************************
   * Basic administration
   ********************************************************************/

  public:  
  void begin();
  void update();
  void end();

  /********************************************************************
   * Transmit/Receive switching
   ********************************************************************/
  
  public:
  void rx();
  inline void tx() { tx(txSrc); }
  void tx(TxAudioIn src);

  /********************************************************************
   * General audio setup -- called via begin()
   ********************************************************************/

  protected:
  virtual void setupRxAudio();
  virtual void setupTxAudio();

  /********************************************************************
   * Receive audio chain
   ********************************************************************/

  // Basic control of RX audio inputs and outputs.
  public:
  void setRxInLevel(RxAudioCh ch, float level);     // Set the audio input level for a given channel.
  void muteRxIn();                                  // Mute all RX audio input channels.
  void muteRxIn(RxAudioCh ch);                      // Mute a specific RX audio input channel.
  void unmuteRxIn(RxAudioCh ch);                    // Un-mute a specific RX audio input channel.
  
  void setLineOutLevel(float level);                // Set the line output level (0.0 - 1.0).
  void setUSBOutLevel(float level);                 // Set the USB output level (0.0 - 1.0).

  /********************************************************************
   * Transmit audio chain
   ********************************************************************/

  // Basic control of TX audio inputs and outputs.
  public:
  void setTxInLevel(TxAudioCh ch, float level); // Set the audio input level for a given channel.
  void muteTxIn();                              // Mute all TX audio input channels.
  void muteTxIn(TxAudioCh ch);                  // Mute a specific TX audio input channel.
  void unmuteTxIn(TxAudioCh ch);                // Un-mute a specific TX audio input channel.
  
  void setTxOutLevel(float level);              // Set the TX audio output level.
  void muteTxOut();                             // Mute the TX audio output.
  void unmuteTxOut();                           // Un-mute the TX audio output.

  void setLineInLevel(float level);                // Set the line input level (0.0 - 1.0).
  void setUSBInLevel(float level);                 // Set the USB input level (0.0 - 1.0).

  // Transmit audio selection (may be overriden at actual transmit time).
  public:
  void setTxAudioIn(TxAudioIn src, bool isTemp = false);  // Select a specific TX audio input path, and identify it as permanent or temporary.
  inline TxAudioIn getTxAudioIn() const { return txSrc; } // Return the current TX audio input.

  // Mic input controls.
  public:
  inline void setMicGain(float level) { micGain = static_cast<unsigned>(level * 63.0); } // Set the mic gain.

  /********************************************************************
   * Receive audio filter (band pass)
   ********************************************************************/

  public:
  void bypassRxFilter();
  void updateRxFilter();
  
  void setRxFilter(float lo, float hi);
  void setRxFilterLo(float lo);
  void setRxFilterHi(float hi);
  void setRxFilterWidth(float width);
  void setRxFilterCenter(float center);

  /*!
   *  Get the current low frequency bound of the RX band pass filter.
   *  @return The low frequency bound.
   */
  inline float getRxFilterLo() const { return state.rxFilterLo; }

  /*!
   *  Get the current high frequency bound of the RX band pass filter.
   *  @return The high frequency bound.
   */
  inline float getRxFilterHi() const { return state.rxFilterHi; }

  /*!
   *  Get the current width of the RX band pass filter.
   *  @return The filter width.
   */
  inline float getRxFilterWidth() const { return state.rxFilterHi - state.rxFilterLo; }

  /*!
   *  Get the current center frequency of the RX band pass filter.
   *  @return The center frequency.
   */
  inline float getRxFilterCenter() const { return (state.rxFilterHi + state.rxFilterLo) / 2.0; }

  /********************************************************************
   * Transmit Voice-Operated-Switch (VOX)
   ********************************************************************/

  public:
  float getVoxLevel() const;

  /********************************************************************
   * Private state
   ********************************************************************/
   
  private:
  DSPState state;

  bool isTx = false;
  TxAudioIn txSrc = MIC_IN;
  TxAudioIn txSrcLatched = MIC_IN;

  short coefficients[NUM_COEFFICIENTS] = {0};
  
  elapsedMillis sinceLastUpdate = 0;
  float usbVol = 0.0;

  unsigned micGain = 0;

  float prevVox = 0.0;
};

extern UBitxDSP& DSP;

#endif

//======================================================================
// EOF
//======================================================================
