////////////////////////////////////////////////////////////////////////////
//
// Copyright (c) 2010-2026 60East Technologies Inc., All Rights Reserved.
//
// This computer software is owned by 60East Technologies Inc. and is
// protected by U.S. copyright laws and other laws and by international
// treaties.  This computer software is furnished by 60East Technologies
// Inc. pursuant to a written license agreement and may be used, copied,
// transmitted, and stored only in accordance with the terms of such
// license agreement and with the inclusion of the above copyright notice.
// This computer software or any other copies thereof may not be provided
// or otherwise made available to any other person.
//
// U.S. Government Restricted Rights.  This computer software: (a) was
// developed at private expense and is in all respects the proprietary
// information of 60East Technologies Inc.; (b) was not developed with
// government funds; (c) is a trade secret of 60East Technologies Inc.
// for all purposes of the Freedom of Information Act; and (d) is a
// commercial item and thus, pursuant to Section 12.212 of the Federal
// Acquisition Regulations (FAR) and DFAR Supplement Section 227.7202,
// Government's use, duplication or disclosure of the computer software
// is subject to the restrictions set forth by 60East Technologies Inc..
//
////////////////////////////////////////////////////////////////////////////
#ifndef __AMPSEXCEPTION_HPP_
#define __AMPSEXCEPTION_HPP_
#define _AMPS_SKIP_AMPSPLUSPLUS
#include <amps/amps.h>
#undef _AMPS_SKIP_AMPSPLUSPLUS
#include <stdexcept>

namespace AMPS
{
///
/// Base class for all exceptions in AMPS.
/// May be constructed with text, or with a client handle,
/// in which case the exception text is made equal to the last
/// error message on that client.
///
  class AMPSException : public std::runtime_error
  {
  protected:
    amps_result _result;
  public:
    ///
    /// Construct an AMPSException from a text message.
    ///
    /// \param message_ The text of the error message.
    /// \param result_ The last amps_result you were given.
    AMPSException(const std::string& message_,
                  amps_result result_)
      : std::runtime_error(message_),
        _result(result_)
    {
    }

    ///
    /// Construct an AMPSException from the last error message
    /// on client.
    /// \param client_ The client to retrieve an error message from.
    /// \param result_ The result code you last retrieved from this client.
    ///
    AMPSException(amps_handle client_,
                  amps_result result_)
      : std::runtime_error(""),
        _result(result_)
    {
      char buffer[1024];
      amps_client_get_error(client_, buffer,
                            sizeof(buffer));
      buffer[sizeof(buffer) - 1] = '\0';
      (std::runtime_error&)*this = std::runtime_error(buffer);
    }
    virtual ~AMPSException() {;}

    ///
    /// Returns a null-terminated string containing self's
    /// error message.
    ///
    const char* toString() const
    {
      return what();
    }

    ///
    /// Returns the actual name of the subclass thrown --
    /// useful when RTTI is disabled, but you'd still like to log
    /// the exact exception type.
    ///
    virtual const char* getClassName() const
    {
      return "AMPSException";
    }

    ///
    /// Constructs and throws the appropriate exception
    /// corresponding to a given result
    /// \param context_ The object to pass to the exception's constructor.
    /// \param result_ the result that should be used to determine the exception type.
    template <class T>
    static void throwFor(const T& context_, amps_result result_);
  };

#define AMPS_EX_TYPE(x, b) \
  class x : public b\
  {\
  public:\
    x(const std::string& message, amps_result result = AMPS_E_OK)\
      : b(message, result)\
    {\
      ;\
    }\
    x(amps_handle client, amps_result result)\
      : b(client, result)\
    {\
      ;\
    }\
    virtual const char* getClassName() const\
    {\
      return #x;\
    }\
  };
  AMPS_EX_TYPE(CommandException, AMPSException)
  AMPS_EX_TYPE(ConnectionException, AMPSException)
  AMPS_EX_TYPE(UsageException, AMPSException)
  AMPS_EX_TYPE(StoreException, AMPSException)
  AMPS_EX_TYPE(MessageStreamFullException, AMPSException)
  AMPS_EX_TYPE(ConnectionRefusedException, ConnectionException)
  AMPS_EX_TYPE(DisconnectedException, ConnectionException)
  AMPS_EX_TYPE(AlreadyConnectedException, ConnectionException)
  AMPS_EX_TYPE(AuthenticationException, ConnectionException)
  AMPS_EX_TYPE(InvalidURIException, ConnectionException)
  AMPS_EX_TYPE(NameInUseException, ConnectionException)
  AMPS_EX_TYPE(NotEntitledException, ConnectionException)
  AMPS_EX_TYPE(ReconnectMaximumExceededException, ConnectionException)
  AMPS_EX_TYPE(RetryOperationException, ConnectionException)
  AMPS_EX_TYPE(TimedOutException, ConnectionException)
  AMPS_EX_TYPE(TransportTypeException, ConnectionException)
  AMPS_EX_TYPE(BadFilterException, CommandException)
  AMPS_EX_TYPE(BadSowKeyException, CommandException)
  AMPS_EX_TYPE(BadRegexTopicException, CommandException)
  AMPS_EX_TYPE(DuplicateLogonException, CommandException)
  AMPS_EX_TYPE(InvalidBookmarkException, CommandException)
  AMPS_EX_TYPE(InvalidOptionsException, CommandException)
  AMPS_EX_TYPE(InvalidOrderByException, CommandException)
  AMPS_EX_TYPE(InvalidSubIdException, CommandException)
  AMPS_EX_TYPE(InvalidTopicException, CommandException)
  AMPS_EX_TYPE(LogonRequiredException, CommandException)
  AMPS_EX_TYPE(MissingFieldsException, CommandException)
  AMPS_EX_TYPE(PublishException, CommandException)
  AMPS_EX_TYPE(SubscriptionAlreadyExistsException, CommandException)
  AMPS_EX_TYPE(SubidInUseException, CommandException)
  AMPS_EX_TYPE(UnknownException, CommandException)
  AMPS_EX_TYPE(ReplayFailedException, CommandException)
  AMPS_EX_TYPE(PublishStoreGapException, StoreException)

  template <class T>
  void AMPSException::throwFor(const T& context_, amps_result result_)
  {
    switch (result_)
    {
    case AMPS_E_OK:
      return;
    case AMPS_E_COMMAND:
      throw CommandException(context_, result_);
    case AMPS_E_CONNECTION:
      throw ConnectionException(context_, result_);
    case AMPS_E_TOPIC:
      throw InvalidTopicException(context_, result_);
    case AMPS_E_FILTER:
      throw BadFilterException(context_, result_);
    case AMPS_E_RETRY:
      throw RetryOperationException(context_, result_);
    case AMPS_E_DISCONNECTED:
      throw DisconnectedException(context_, result_);
    case AMPS_E_CONNECTION_REFUSED:
      throw ConnectionRefusedException(context_, result_);
    case AMPS_E_URI:
      throw InvalidURIException(context_, result_);
    case AMPS_E_TRANSPORT_TYPE:
      throw TransportTypeException(context_, result_);
    case AMPS_E_USAGE:
      throw UsageException(context_, result_);
    default:
      throw AMPSException(context_, result_);
    }
  }
}
#endif

