//===-- rosa/core/Agent.hpp -------------------------------------*- C++ -*-===//
//
//                                 The RoSA Framework
//
// Distributed under the terms and conditions of the Boost Software License 1.0.
// See accompanying file LICENSE.
//
// If you did not receive a copy of the license file, see
// http://www.boost.org/LICENSE_1_0.txt.
//
//===----------------------------------------------------------------------===//
///
/// \file rosa/core/Agent.hpp
///
/// \author David Juhasz (david.juhasz@tuwien.ac.at)
///
/// \date 2017
///
/// \brief Declaration of the base \c 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 \c rosa::Unit owned by a
/// \c rosa::MessagingSystem, capable of handling \c rosa::Message instances
/// as \c rosa::MessageHandler, and provides the \c rosa::AbstractAgent
/// interface with \c rosa::AgentHandle as reference type.
class Agent : public Unit,
              public MessageHandler,
              public AbstractAgent<AgentHandle> {
  friend class AgentHandle; ///< \c rosa::AgentHandle is our friend.

protected:
  /// A handle for \p 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 \c rosa::Unit instance
  /// \param Id unique identifier of the new \c rosa::Unit instance
  /// \param Name name of the new \c rosa::Unit instance
  /// \param S \c 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 \p this object.
  ~Agent(void);

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

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

  /// Comapres \p this object to a given reference.
  ///
  /// The comparison is based on the memory addresses.
  ///
  /// \param H reference to another object
  ///
  /// \return if \p this object compares less to \p H
  bool operator<(const AgentHandle &H) const noexcept override;

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

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

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

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

  /// Gives the referenced \c rosa::Agent instance for a \c rosa::AgentHandle.
  ///
  /// \note Intended for derived classes to be able to inspect
  /// \c rosa::AgentHandle instances.
  ///
  /// \param H \c rosa::AgentHandle to take the referenced \c rosa::Agent from
  ///
  /// \return reference to the \c rosa::Agent instance from \p H
  static Agent &unwrapAgent(const AgentHandle &H) noexcept { return H.A; }

  /// Gives the owning \c rosa::MessagingSystem of a \c rosa::Agent instance
  /// for a \c rosa::AgentHandle.
  ///
  /// \note Intended for for derived classes to be able to inspect
  /// \c rosa::AgentHandle instances.
  ///
  /// \param H \c rosa::AgentHandle to take the owning
  /// \c rosa::MessagingSystem from
  ///
  /// \return reference to the \c rosa::MessagingSystem owning the
  /// \c rosa::Agent instance from \p H
  static MessagingSystem &unwrapSystem(const AgentHandle &H) noexcept {
    return H.S;
  }
};

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
