#ifndef __TS590_h__
#define __TS590_h__

#include <Arduino.h>
#include "Rig.h"

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

#define TS590_COMMAND_MAX_LENGTH  50 // including terminator (which will get converted to null)

const int ts590CommandMaxLength = TS590_COMMAND_MAX_LENGTH;

void ts590SendCommand(const char*, ...);

/*!
 *  @brief  Send a syntax error response to the PC via CAT.
 */
inline void ts590SyntaxError() { ts590SendCommand("?"); }

/*!
 *  @brief  Send a communications error response to the PC via CAT.
 */
inline void ts590CommError() { ts590SendCommand("E"); }  

/*!
 *  @brief  Send a processing error response to the PC via CAT.
 */
inline void ts590ProcessError() { ts590SendCommand("O"); }

enum TS590Error {
  NoError,
  SyntaxError,
  CommError,
  ProcessError
};

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

/*!
 *  @brief  A TS590S/SG "CAT" command.  This is the base class for all
 *          CAT commands.
 */
class TS590Command {
  public:
  TS590Command(const char* pre);
  virtual ~TS590Command() = 0;

  /*!
   *  @brief  Return the 2-character prefix for the command.
   *  @return The 2-character prefix for the command.
   */
  inline const char* prefix() const { return &myPrefix[0]; }

  /*!
   *  @brief  Return the rig that this command will be used to control.
   */
  inline UBitxRig* rig() const { return theRig; }

  /*!
   *  @brief  Handle the provided Set command.  If the Set command
   *          results in an error, then set the appropriate flag with
   *          setSyntaxError(), setCommError(), or setProcessError().
   *  @param  cmd
   *          The current command string received from the PC via CAT.
   *          It should be null-terminated, and should no longer have
   *          the terminator (;).
   */
  virtual void handleCommand(const char* cmd) = 0;

  /*!
   *  @brief  Send a response back to the PC.  This assumes a
   *          successful command (no errors).
   */
  virtual void sendResponse(const char* cmd) = 0;

  virtual bool isReadCommand(const char* cmd) const;
  
  void process(const char* cmd);

  static void setSyntaxError();
  static void setCommError();
  static void setProcessError();
  static void setRig(UBitxRig* r);

  private:
  char myPrefix[3] = "\0\0";
  static TS590Error theError;
  static UBitxRig* theRig;
};

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

/*!
 *  @brief  CAT command for setting or reading the VFO A/B frequency.
 */
template<bool VFOA>
class TS590_FAB : public TS590Command {
  public:
  TS590_FAB(): TS590Command(VFOA ? "FA" : "FB") {}

  virtual void handleCommand(const char* cmd) {
    if (strlen(cmd) == 13) {
      unsigned long freq = strtoul(&cmd[2], NULL, 10);
      if (VFOA) {
        rig()->setFreqA(freq, true);
      } else {
        rig()->setFreqB(freq, true);
      }
    } else {
      setSyntaxError();
    }
  }

  virtual void sendResponse(const char* cmd) {
    ts590SendCommand(VFOA ? "FA%011u" : "FB%011u", VFOA ? rig()->getFreqA() : rig()->getFreqB());
  }
};

typedef TS590_FAB<true> TS590_FA;
typedef TS590_FAB<false> TS590_FB;

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

/*!
 *  @brief  CAT command for setting the receiver VFO.  This will always
 *          disable split mode, if it was previously enabled.
 */
class TS590_FR : public TS590Command {
  public:
  TS590_FR(): TS590Command("FR") {}
  virtual void handleCommand(const char* cmd);
  virtual void sendResponse(const char* cmd);
};

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

/*!
 *  @brief  CAT command for setting the transmitter VFO.  If it is
 *          different than the receiver VFO, then split mode will be
 *          automatically enabled.
 */
class TS590_FT : public TS590Command {
  public:
  TS590_FT(): TS590Command("FT") {}
  virtual void handleCommand(const char* cmd);
  virtual void sendResponse(const char* cmd);
};

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

/*!
 *  @brief  CAT command for setting the mode.
 */
class TS590_MD : public TS590Command {
  public:
  TS590_MD(): TS590Command("MD") {}
  virtual void handleCommand(const char* cmd);
  virtual void sendResponse(const char* cmd);
};

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

class UBitxTS590 {
  public:
  UBitxTS590(TS590Command** cmds, int len): commands(cmds), numCommands(len) {}
  
  void begin();
  void update();

  private:
  void processCommand();
  
  char buf[ts590CommandMaxLength] = {0};
  int bufLen = 0;
  TS590Command** commands;
  int numCommands;
};

extern UBitxTS590 TS590;

#endif

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