/***************************************************************************//**
 *
 * \file rosa/core/Agent.hpp
 *
 * \author David Juhasz (david.juhasz@tuwien.ac.at)
 *
 * \date 2017
 *
 * \brief Declaration of the base `rosa::Agent` class.
 *
 ******************************************************************************/

#ifndef ROSA_CORE_AGENT_HPP
#define ROSA_CORE_AGENT_HPP

#include "rosa/core/AgentHandle.hpp"
#include "rosa/core/MessageHandler.hpp"
#include "rosa/core/MessagingSystem.hpp"
#include "rosa/core/Unit.h"

#include "rosa/support/log.h"

namespace rosa {

/// Implements an *Agent* that is a special `rosa::Unit` owned by a
/// `rosa::MessagingSystem`, capable of handling `rosa::Message` instances
/// as `rosa::MessageHandler`, and provides the `rosa::AbstractAgent` interface
/// with `rosa::AgentHandle` as reference type.
class Agent : public Unit,
              public MessageHandler,
              public AbstractAgent<AgentHandle> {
  friend class AgentHandle; ///< `rosa::AgentHandle` is our friend.

protected:
  /// A handle for `this` object.
  const AgentHandle Self;

public:
  /// Creates a new instance by instantiating all the base-classes.
  ///
  /// \tparam Fun type of the first mandatory function for handling messages
  /// \tparam Funs types of any further functions for handling messages
  ///
  /// \param Kind kind of the new `rosa::Unit` instance
  /// \param Id unique identifier of the new `rosa::Unit` instance
  /// \param Name name of the new `rosa::Unit` instance
  /// \param S `rosa::MessagingSystem` owning the new instance
  /// \param F the first mandatory function for handling messages
  /// \param Fs optional further functions for handling messages
  template <typename Fun, typename... Funs>
  Agent(const AtomValue Kind, const id_t Id, const std::string &Name,
        MessagingSystem &S, Fun &&F, Funs &&... Fs);

  /// Destroys `this` object.
  ~Agent(void);

  /// Tells if `this` object is in a valid state.
  ///
  /// \note A `rosa::Agent` instance is always valid.
  ///
  /// \return if `this` object is in a valid state
  operator bool(void) const noexcept override;

  /// Tells if a given reference refers to `this` object.
  ///
  /// \param H reference to another object
  ///
  /// \return if `H` refers to `this` object
  bool operator==(const AgentHandle &H) const noexcept override;

  /// Returns a reference to `this` object.
  ///
  /// \return a reference to `this` object
  AgentHandle self(void) noexcept override;

  /// Sends a given `rosa::message_t` instance to `this` object.
  ///
  /// \param M message to send
  ///
  /// \note Since a `rosa::Agent` instance is always valid, there is no
  /// precondition for this function.
  /// \see rosa::AbstractAgent::sendMessage and
  /// `rosa::Agent::operator bool() const`
  void sendMessage(message_t &&M) noexcept override;

  /// Dumps `this` object into a `std::string` for tracing purposes.
  ///
  /// \return `string` representing the state of `this` object
  std::string dump(void) const noexcept override;

protected:
  /// Returns a reference to the `rosa::MessagingSystem` owning `this` object.
  ///
  /// \return reference of `S`
  MessagingSystem &system(void) const noexcept override;
};

template <typename Fun, typename... Funs>
Agent::Agent(const AtomValue Kind, const id_t Id, const std::string &Name,
             MessagingSystem &S, Fun &&F, Funs &&... Fs)
    : Unit(Kind, Id, Name, S), MessageHandler(std::move(F), std::move(Fs)...),
      Self(*this, /*valid*/ true) {
  LOG_TRACE("Agent is created.");
}

} // End namespace rosa

#endif // ROSA_CORE_AGENT_HPP

