/***************************************************************************//**
 *
 * \file rosa/core/AgentHandle.hpp
 *
 * \author David Juhasz (david.juhasz@tuwien.ac.at)
 *
 * \date 2017
 *
 * \brief Declaration of a handle for `rosa::Agent`.
 *
 ******************************************************************************/

#ifndef ROSA_CORE_AGENTHANDLE_HPP
#define ROSA_CORE_AGENTHANDLE_HPP

#include "rosa/core/AbstractAgent.hpp"

namespace rosa {

/// Wraps an actual `rosa::Agent` to decouple its public interface.
/// \note Such decoupling might be necessary when operating with remote
/// *systems*, sometime in the future.
class AgentHandle : public AbstractAgent<AgentHandle> {

  /// `rosa::Agent` and `rosa::MessagingSystem` are our friends, they may
  /// inspect the private member fields of the class.
  ///@{
  friend class Agent;
  friend class MessagingSystem;
  ///@}

  /// The wrapped `rosa::Agent` instance.
  Agent &A;

  /// The `rosa::MessagingSystem` owning `A`.
  MessagingSystem &S;

  /// Creates a new instance without validating the state of the wrapped
  /// `rosa::Agent`.
  ///
  /// \note Used by a `rosa::Agent` instance to create a reference to itself
  /// during construction, when its state is not valid yet.
  ///
  /// \param A `Agent` to wrap
  ///
  /// \note There a second argument, which is ignored, that is only present to
  /// separate this constructor from the public constructor taking only a
  /// `rosa::Agent` to wrap.
  AgentHandle(Agent &A, bool);

public:
  /// Creates a new instance validating the state of the wrapped `rosa::Agent`.
  ///
  /// \note The wrapped `rosa::Agent` must be in a valid state to instantiate
  /// `rosa::AgentHandle` with this constructor.
  ///
  /// \param A `Agent` to wrap
  ///
  /// \pre `A` is registered in its owning *system*:\code
  /// A.system().isUnitRegistered(A)
  /// \endcode
  AgentHandle(Agent &A);

  /// Destroys `this` object.
  ///
  /// The destructor has nothing to take care of.
  ~AgentHandle(void) = default;

  /// Tells if the wrapped `rosa::Agent` is in a valid state.
  ///
  /// \note A `rosa::AgentHandler` belongs to a `rosa::MessagingSystem`. Working
  /// with a `rosa::AgentHandler` whose originating `rosa::MessagingSystem` has
  /// already been destroyed results in *undefined* behavior.
  ///
  /// \return if the wrapped `rosa::Agent` is in a valid state
  operator bool(void) const noexcept override;

  /// Tells if another `rosa::AgentHandle` wraps the same `rosa::Agent` as
  /// `this` object.
  ///
  /// \param H `AgentHandle` whose wrapped `Agent` to check
  ///
  /// \return if `H` wraps `A` like `this` object
  bool operator==(const AgentHandle &H) const noexcept override;

  /// Returns a reference to the wrapped `rosa::Agent`.
  ///
  /// \return a reference to `A`
  AgentHandle self(void) noexcept override;

  /// Sends a given `rosa::message_t` instance to the wrapped `rosa::Agent`.
  ///
  /// \param M message to send
  ///
  /// \pre The wrapped `rosa::Agent` instance is in a valid state:\code
  /// bool(*this)
  /// \endcode
  void sendMessage(message_t &&M) noexcept override;

};

} // End namespace rosa

#endif // ROSA_CORE_AGENTHANDLE_HPP

