#ifndef SESSION_H
#define SESSION_H

#include <clib/AMarquee_protos.h>
#include <pragmas/AMarquee_pragmas.h>
#include <libraries/AMarquee.h>

// class name:  Session
// requires:    amarquee.library v47+  (you must OpenLibrary() this BEFORE using this class!)
//
// This class is a C++ wrapper for the AMarquee client API.
// Everything here should be pretty self-explanatory, as
// almost every method in this class has a one-to-one 
// correspondence with a similarly named amarquee.library 
// C function.
//
// See amarquee.guide/Using AMarquee/API for a description
// of what all the methods do.
//
// NOTE:  I haven't run-tested this code, as I don't have
//        a C++ compiler for my Amiga.  It does compile on
//        other systems (with the C back-end stubbed out), though, 
//        and it's simple enough that I don't expect any problems
//        with it.  Please email me if you find any!
//        (jfriesne@ucsd.edu)
//

// This exception is thrown by the constructor if the QNewSession()
// call fails (e.g. out of memory or the TCP connection failed)
class SessionCreationFailedException
{
public:
  SessionCreationFailedException() {}
  ~SessionCreationFailedException() {}

private:
  // err, nothing to store here!
};

// NOTE:  Don't forget to OpenLibrary("amarquee.library", 47L) before
//        instantiating any Sessions!  (and of course be sure to
//        CloseLibrary(AMarqueeBase) before you exit...)
//
class Session {
public:
  // Pseudo-constructor methods:  These can be used as an
  // alternative to the "real" constructor below, if you don't want
  // to worry about catching SessionCreationFailedExceptions.
  // Each method will return a either valid Session object, which
  // the calling code is then responsible for deleting, or NULL 
  // to indicate failure.
  static inline Session * newSession(const char * hostName, LONG port, const char * progName) throw();
  static inline Session * newSessionAsync(const char * hostName, LONG port, const char * progName) throw();
  static inline Session * newHostSession(const char * hostName, LONG & port, const char * progName) throw();
  static inline Session * newServerSession(const char * whichHosts, const char * whichProgNames) throw();

  // Constructor:  If you want to use a Real Constructor, here's one.
  // This constructor calls either QNewSession() or QNewSessionAsync(), 
  // depending on the (async) parameter.
  // If the internal QNewSession() call fails, a SessionCreationFailedException will be thrown.
  inline Session(const char * hostName, LONG port, const char * progName, BOOL async = FALSE) throw(SessionCreationFailedException);
  
  // Destructor:  You MUST delete every Session you create, or it's crash city!
  inline ~Session() throw();

  // Accessor:  Wait() on this MsgPort for QMessages
  inline struct MsgPort * getMsgPort() const throw();
  
  // Operations:  Each of these queues up a transaction.
  // Each returns a positive ID number on success, or 0L on failure.
  
  inline LONG getOp(const char * wildpath, LONG maxBytes=-1L) throw();
  inline LONG setOp(const char * path, const void * buffer, ULONG bufferLength) throw();

  inline LONG subscribeOp(const char * wildpath, LONG maxBytes=-1L) throw();
  inline LONG clearSubscriptionsOp(LONG which=0L) throw();
  inline LONG getAndSubscribeOp(const char * wildpath, LONG maxBytes=-1L) throw();

  inline LONG streamOp(const char * path, const void * buffer, ULONG bufferLength) throw();  
  inline LONG messageOp(const char * hosts, const void * buffer, ULONG bufferLength) throw();

  inline LONG renameOp(const char * path, const char * label) throw();
  inline LONG deleteOp(const char * wildpath) throw();

  inline LONG pingOp() throw();
  inline LONG infoOp() throw();
  inline LONG debugOp(const char * string) throw();

  inline LONG setAccessOp(const char * newAccess) throw();
  inline LONG setMessageAccessOp(const char * newAccess, LONG maxBytes=-1L) throw();

  inline LONG requestPrivilegesOp(ULONG privBits) throw();
  inline LONG releasePrivilegesOp(ULONG privBits) throw();

  inline LONG killClientsOp(const char * hosts) throw();
  inline LONG setParameterOp(const char * paramName, const char * newValue) throw();
  inline LONG getParameterOp(const char * paramName) throw();
  inline LONG sysMessageOp(const char * hosts, const char * message) throw();

  // Call this when you've queued all your transactions.
  // It sends them on their merry way.
  inline LONG go(ULONG flags=0L) throw();

  // Call this function to free QMessage's you received 
  // from this->getMsgPort()->GetMsg()
  inline void freeQMessage(struct QMessage * qmsg) throw();

  // These return the approximate current status of outgoing queue.
  // Note that they aren't thread safe, so the results you get may
  // be inaccurate.
  inline ULONG getNumQueuedPackets() const throw();
  inline ULONG getNumQueuedBytes() const throw();

  // Returns an ASCII description of the passed-in error code.
  static inline const char * getErrorName(LONG error) throw();
 
  /* Introduced for v1.47, to support sharing QSessions between threads */
  inline BOOL detachSession(ULONG flags=0L) throw();
  inline BOOL reattachSession(ULONG flags=0L) throw();
    
private:
  inline Session(struct QSession *) throw();  // for our private use

  // Purposely unimplemented and inaccessible
  Session();
  Session(const Session &);    // disallow copy constructor
  Session& operator =(const Session &); // disallow assignment

  struct QSession * session;
};

////////////////////////////////////////////////////////////////////////
//
// CODE IMPLEMENTATION:  You shouldn't need to look below this line!
//
////////////////////////////////////////////////////////////////////////

inline Session::Session(struct QSession * s) throw()  
{
  session = s;
}

inline Session * Session::newSession(const char * hostName, LONG port, const char * progName) throw()
{
  struct QSession * s = QNewSession((char *)hostName, port, (char *)progName);
  return(s ? new Session(s) : NULL);
}

inline Session * Session::newSessionAsync(const char * hostName, LONG port, const char * progName) throw()
{
  struct QSession * s = QNewSessionAsync((char *) hostName, port, (char *)progName);
  return(s ? new Session(s) : NULL);
}

inline Session * Session::newHostSession(const char * hostName, LONG & port, const char * progName) throw()
{
  struct QSession * s = QNewHostSession((char *) hostName, &port, (char *) progName);
  return(s ? new Session(s) : NULL);
}

inline Session * Session::newServerSession(const char * whichHosts, const char * whichProgNames) throw()
{
  struct QSession * s = QNewServerSession((char *) whichHosts, (char *) whichProgNames);
  return(s ? new Session(s) : NULL);
}

inline Session::Session(const char * h, LONG p, const char * n, BOOL a) throw(SessionCreationFailedException)  
{
  session = a ? QNewSessionAsync((char *)h,p,(char *)n) : QNewSession((char *)h,p,(char *)n);
  if (session == NULL) 
  {
    SessionCreationFailedException ex;
    throw(ex);
  }
}

inline Session::~Session() throw()  
{
  (void)QFreeSession(session);
}

inline struct MsgPort * Session::getMsgPort() const throw()
{
  return(session->qMsgPort);
}
    
inline LONG Session::getOp(const char * wildpath, LONG maxBytes) throw()
{
  return(QGetOp(session, (char *)wildpath, maxBytes));
}

inline LONG Session::setOp(const char * path, const void * buffer, ULONG bufferLength) throw()
{
  return(QSetOp(session, (char *)path, (void *)buffer, bufferLength));
}

inline LONG Session::subscribeOp(const char * wildpath, LONG maxBytes) throw()
{
  return(QSubscribeOp(session, (char *)wildpath, maxBytes));
}

inline LONG Session::clearSubscriptionsOp(LONG which) throw()
{
  return(QClearSubscriptionsOp(session, which));
}

inline LONG Session::getAndSubscribeOp(const char * wildpath, LONG maxBytes) throw()
{
  return(QGetAndSubscribeOp(session, (char *)wildpath, maxBytes));
}

inline LONG Session::streamOp(const char * path, const void * buffer, ULONG bufferLength) throw()  
{
  return(QStreamOp(session, (char *)path, (void *)buffer, bufferLength));
}

inline LONG Session::messageOp(const char * hosts, const void * buffer, ULONG bufferLength) throw()
{
  return(QMessageOp(session, (char *)hosts, (void *)buffer, bufferLength));
}

inline LONG Session::renameOp(const char * path, const char * label) throw()
{
  return(QRenameOp(session, (char *)path, (char *)label));
}

inline LONG Session::deleteOp(const char * wildpath) throw()
{
  return(QDeleteOp(session, (char *)wildpath));
}

inline LONG Session::pingOp() throw()
{
  return(QPingOp(session));
}

inline LONG Session::infoOp() throw()
{
  return(QInfoOp(session));
}

inline LONG Session::debugOp(const char * string) throw()
{
  return(QDebugOp(session, (char *)string));
}

inline LONG Session::setAccessOp(const char * newAccess) throw()
{
  return(QSetAccessOp(session, (char *)newAccess));
}

inline LONG Session::setMessageAccessOp(const char * newAccess, LONG maxBytes) throw()
{
  return(QSetMessageAccessOp(session, (char *)newAccess, maxBytes));
}

inline LONG Session::requestPrivilegesOp(ULONG privBits) throw()
{
  return(QRequestPrivilegesOp(session, privBits));
}

inline LONG Session::releasePrivilegesOp(ULONG privBits) throw()
{
  return(QReleasePrivilegesOp(session, privBits));
}

inline LONG Session::killClientsOp(const char * hosts) throw()
{
  return(QKillClientsOp(session, (char *)hosts));
}

inline LONG Session::setParameterOp(const char * paramName, const char * newValue) throw()
{
  return(QSetParameterOp(session, (char *)paramName, (char *)newValue));
}

inline LONG Session::getParameterOp(const char * paramName) throw()
{
  return(QGetParameterOp(session, (char *)paramName));
}

inline LONG Session::sysMessageOp(const char * hosts, const char * message) throw()
{
  return(QSysMessageOp(session, (char *)hosts, (char *)message));
}

inline LONG Session::go(ULONG flags) throw()
{
  return(QGo(session, flags));
}

inline void Session::freeQMessage(struct QMessage * qmsg) throw()
{
  FreeQMessage(session, qmsg);
}

inline ULONG Session::getNumQueuedPackets() const throw()
{
  return(QNumQueuedPackets(session));
}

inline ULONG Session::getNumQueuedBytes() const throw()
{
  return(QNumQueuedBytes(session));
}

inline const char * Session::getErrorName(LONG error) throw()
{
  return(QErrorName(error));
}

inline BOOL Session::detachSession(ULONG flags) throw()
{
  return(QDetachSession(session, flags));
}

inline BOOL Session::reattachSession(ULONG flags) throw()
{
  return(QReattachSession(session, flags));
}

#endif /* SESSION_H */
