text: /**
   *  Deferred.h
   *
   *  Class describing a set of actions that could
   *  possibly happen in the future that can be
   *  caught.
   *
   *  @copyright 2014 - 2020 Copernica BV
   */
  
  /**
   *  Include guard
   */
  #pragma once
  
  /**
   *  Dependencies
   */
  #include <memory>
  #include <stdint.h>
  #include "callbacks.h"
  
  /**
   *  Set up namespace
   */
  namespace AMQP {
  
  // forward declaration
  class ChannelImpl;
  
  /**
   *  Class definition
   */
  class Deferred
  {
  private:
      /**
       *  Callback to execute either way
       *  @var    FinalizeCallback
       */
      FinalizeCallback _finalizeCallback;
  
  protected:
      /**
       *  Callback to execute on success
       *  @var    SuccessCallback
       */
      SuccessCallback _successCallback;
  
      /**
       *  Callback to execute on failure
       *  @var    ErrorCallback
       */
      ErrorCallback _errorCallback;
  
      /**
       *  Pointer to the next deferred object
       *  @var    Deferred
       */
      std::shared_ptr<Deferred> _next;
  
      /**
       *  Do we already know we failed?
       *  @var bool
       */
      bool _failed;
  
  
      /**
       *  The next deferred object
       *  @return Deferred
       */
      const std::shared_ptr<Deferred> &next() const
      {
          return _next;
      }
  
      /**
       *  Indicate success
       *  @return Deferred        Next deferred result
       */
      virtual const std::shared_ptr<Deferred> &reportSuccess() const
      {
          // execute callbacks if registered
          if (_successCallback) _successCallback();
  
          // return the next deferred result
          return _next;
      }
  
      /**
       *  Report success for queue declared messages
       *  @param  name            Name of the new queue
       *  @param  messagecount    Number of messages in the queue
       *  @param  consumercount   Number of consumers linked to the queue
       *  @return Deferred        Next deferred result
       */
      virtual const std::shared_ptr<Deferred> &reportSuccess(const std::string &name, uint32_t messagecount, uint32_t consumercount) const
      {
          // make sure compilers dont complain about unused parameters
          (void) name;
          (void) messagecount;
          (void) consumercount;
  
          // this is the same as a regular success message
          return reportSuccess();
      }
  
      /**
       *  Report success for frames that report delete operations
       *  @param  messagecount    Number of messages that were deleted
       *  @return Deferred
       */
      virtual const std::shared_ptr<Deferred> &reportSuccess(uint32_t messagecount) const
      {
          // make sure compilers dont complain about unused parameters
          (void) messagecount;
  
          // this is the same as a regular success message
          return reportSuccess();
      }
  
      /**
       *  Report success for a get operation
       *
       *  @param  messagecount    Number of messages left in the queue
       *  @param  deliveryTag     Delivery tag of the message coming in
       *  @param  redelivered     Was the message redelivered?
       */
      virtual const std::shared_ptr<Deferred> &reportSuccess(uint32_t messagecount, uint64_t deliveryTag, bool redelivered)
      {
          // make sure compilers dont complain about unused parameters
          (void) messagecount;
          (void) deliveryTag;
          (void) redelivered;
  
          // this is the same as a regular success message
          return reportSuccess();
      }
  
      /**
       *  Report success for frames that report cancel operations
       *  @param  name            Consumer tag that is cancelled
       *  @return Deferred
       */
      virtual const std::shared_ptr<Deferred> &reportSuccess(const std::string &name)
      {
          // make sure compilers dont complain about unused parameters
          (void) name;
  
          // this is the same as a regular success message
          return reportSuccess();
      }
  
      /**
       *  Indicate failure
       *  @param  error           Description of the error that occured
       *  @return Deferred        Next deferred result
       */
      const std::shared_ptr<Deferred> &reportError(const char *error)
      {
          // from this moment on the object should be listed as failed
          _failed = true;
  
          // execute callbacks if registered
          if (_errorCallback) _errorCallback(error);
  
          // return the next deferred result
          return _next;
      }
  
      /**
       *  Add a pointer to the next deferred result
       *  @param  deferred
       */
      void add(const std::shared_ptr<Deferred> &deferred)
      {
          // store pointer
          _next = deferred;
      }
      
      /**
       *  Remove this object from the chain of deferreds
       */
      void unchain()
      {
          // we no longer need the next pointer
          _next = nullptr;
      }
  
      /**
       *  The channel implementation may call our
       *  private members and construct us
       */
      friend class ChannelImpl;
      friend class Tagger;
  
  public:
      /**
       *  Protected constructor that can only be called
       *  from within the channel implementation
       *
       *  Note: this constructor _should_ be protected, but because make_shared
       *  will then not work, we have decided to make it public after all,
       *  because the work-around would result in not-so-easy-to-read code.
       *
       *  @param  failed  are we already failed?
       */
      Deferred(bool failed = false) : _failed(failed) {}
  
  public:
      /**
       *  Deleted copy and move constructors
       */
      Deferred(const Deferred &that) = delete;
      Deferred(Deferred &&that) = delete;
  
      /**
       *  Destructor
       */
      virtual ~Deferred()
      {
          // report to the finalize callback
          if (_finalizeCallback) _finalizeCallback();
      }
  
      /**
       *  Cast to a boolean
       */
      operator bool () const
      {
          return !_failed;
      }
  
      /**
       *  Register a function to be called
       *  if and when the operation succesfully
       *  completes.
       *
       *  Only one callback can be registered at a time.
       *  Successive calls to this function will clear
       *  callbacks registered before.
       *
       *  @param  callback    the callback to execute
       */
      inline Deferred &onSuccess(const SuccessCallback& callback) { return onSuccess(SuccessCallback(callback)); }
      Deferred &onSuccess(SuccessCallback&& callback)
      {
          // store callback
          _successCallback = std::move(callback);
  
          // allow chaining
          return *this;
      }
  
      /**
       *  Register a function to be called
       *  if and when the operation fails.
       *
       *  Only one callback can be registered at a time.
       *  Successive calls to this function will clear
       *  callbacks registered before.
       *
       *  @param  callback    the callback to execute
       */
      inline Deferred &onError(const ErrorCallback& callback) { return onError(ErrorCallback(callback)); }
      Deferred &onError(ErrorCallback&& callback)
      {
          // store callback
          _errorCallback = std::move(callback);
  
          // if the object is already in a failed state, we call the callback right away
          if (_failed) callback("Frame could not be sent");
  
          // allow chaining
          return *this;
      }
  
      /**
       *  Register a function to be called
       *  if and when the operation completes
       *  or fails. This function will be called
       *  either way.
       *
       *  In the case of success, the provided
       *  error parameter will be an empty string.
       *
       *  Only one callback can be registered at at time.
       *  Successive calls to this function will clear
       *  callbacks registered before.
       *
       *  @param  callback    the callback to execute
       */
      inline Deferred &onFinalize(const FinalizeCallback& callback) { return onFinalize(FinalizeCallback(callback)); }
      Deferred &onFinalize(FinalizeCallback&& callback)
      {
          // if the object is already in a failed state, we call the callback right away
          if (_failed) callback();
  
          // otherwise we store callback until it's time for the call
          else _finalizeCallback = std::move(callback);
  
          // allow chaining
          return *this;
      }
  };
  
  /**
   *  End namespace
   */
  }
  