//===-- rosa/core/AgentHandle.hpp -------------------------------*- C++ -*-===//
//
//                                 The RoSA Framework
//
//===----------------------------------------------------------------------===//
///
/// \file rosa/core/AgentHandle.hpp
///
/// \author David Juhasz (david.juhasz@tuwien.ac.at)
///
/// \date 2017
///
/// \brief Declaration of a handle for \c rosa::Agent.
///
//===----------------------------------------------------------------------===//

#ifndef ROSA_CORE_AGENTHANDLE_HPP
#define ROSA_CORE_AGENTHANDLE_HPP

#include "rosa/core/AbstractAgent.hpp"

namespace rosa {

/// Wraps an actual \c 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> {

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

  /// The wrapped \c rosa::Agent instance.
  Agent &A;

  /// The \c rosa::MessagingSystem owning \c A.
  MessagingSystem &S;

  /// Creates a new instance without validating the state of the wrapped
  /// \c rosa::Agent.
  ///
  /// \note Used by a \c rosa::Agent instance to create a reference to itself
  /// during construction, when its state is not valid yet.
  ///
  /// \param A \c rosa::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
  /// \c rosa::Agent to wrap.
  AgentHandle(Agent &A, bool);

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

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

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

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

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

  /// Sends a given \c rosa::message_t instance to the wrapped \c rosa::Agent.
  ///
  /// \param M message to send
  ///
  /// \pre The wrapped \c 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
