#ifndef LibAard_h
#define LibAard_h

#include <queue>
#include <termios.h>
#include <unistd.h>

class AardRemoteMessage;
class LogAard;

// Class for interface with AardRemote on linux.
//
class LibAard {
public:

   LibAard();

   virtual
   ~LibAard();

      // Open the serial device to talk to the remote. Typically this
      // is something like /dev/ttyACM0 for a USB connection or
      // /dev/rfcomm0 for a bluetooth connection.
      //
      // The remote microcode expects 115200 baud, so that is hard
      // coded into the open.
      //
      // Returns the file descriptor or -1 if there was a problem.
      // You can arrange to hook the file descriptor into some event
      // loop should you so desire in order to know when it might be
      // a good time to call PollMessage().
      //
   int
   OpenAardRemote(const char * devname);

      // Close the connection to the remote.
      //
   void
   CloseAardRemote();

      // Opening the device may or may not reset the remote. If it does
      // reset the remote, it will be a while before it is up and taking
      // again, so this routine is designed to be called after the open to
      // wait till the remote is willing to speak.
      //
      // It operates by sending a bad command byte and waiting for an error
      // response or an async MKAS_RESET message to come in. If the device
      // was reset by the open, the reset message will eventually show up, if
      // the device was not reset by the open, the error response from the
      // bad command byte will show up.
      //
      // It sends several different bad commands until it gets them
      // echoed back in sequence, then it seens a version query to make
      // sure it gets a good response from a good command.
      //
   void
   WaitForStartup();

      // Send just a command with no associated data.
      //
   void
   SendCommand(unsigned char cmdcode);

      // Send a command with a 16 bit chunk of data.
      //
   void
   SendCommand(unsigned char cmdcode, unsigned short cmdata);

      // Send an arbitrary length command.
      //
   void
   SendCommand(unsigned char cmdcode, const unsigned short * cmdata, int len);

      // Send an arbitrary length command.
      //
   void
   SendCommand(unsigned char cmdcode, const std::vector<unsigned short> & dat);

      // Read as much data as possible from the file descriptor, return
      // number of messages queued up to be processed following the read.
      //
   int
   ReadMoreData();

      // Check for data coming in on the file descriptor. If enough has shown
      // up to form a complete message, return a message object (which should
      // be deleted by the caller when it is done with it).
      //
      // If no message is available, return NULL.
      //
   AardRemoteMessage *
   PollMessage();

      // Wait for a message to come in for up to the given number of
      // milliseconds. If a message has not arrived in that time, return NULL.
      //
      // If milliseconds is 0, wait forever.
      //
      // The returned message object should be deleted by the caller.
      //
   AardRemoteMessage *
   WaitMessage(unsigned int milliseconds);

      // Return true if an error condition has cropped up on the file
      // descriptor (maybe someone unplugged the power, maybe you never
      // called OpenAardRemote(), etc).
      //
      // Clear flag by closing device and reopening again (of course if the
      // open fails, you'll still have the flag set).
      //
   bool
   HasError();

      // Set pointer to logger which will be called to log all low level
      // reads and writes. Set to NULL to disable logging.
      //
   void
   SetLogger(LogAard * new_logger);

private:

   int fd;
   int has_error;

   struct termios orig_ts;
   bool orig_ts_valid;

   AardRemoteMessage * incoming;
   bool have_leftover_byte;
   unsigned char leftover_byte;
   std::queue<AardRemoteMessage *> outbox;

   LogAard * logger;

   ssize_t
   write_and_log(const void * buf, size_t count);

   ssize_t
   read_and_log(void * buf, size_t count);

   void
   IncomingToOutbox();
};

#endif // LibAard_h
