/*******************************************************************************
 *
 * File:     AbstractAgent.hpp
 *
 * Contents: Declaration of an abstract interface for Agents.
 *
 * Copyright 2017
 *
 * Author: David Juhasz (david.juhasz@tuwien.ac.at)
 *
 ******************************************************************************/

#ifndef ROSA_CORE_ABSTRACTAGENT_HPP
#define ROSA_CORE_ABSTRACTAGENT_HPP

#include "rosa/core/Message.hpp"
#include "rosa/core/forward_declarations.h"

#include "rosa/support/debug.hpp"

#include <memory>

namespace rosa {

// Abstract class declaring an interface for Agents.
// NOTE: Ref is reference for AbstractAgent, whose actual value must be a class
// derived from AbstractAgent<Ref>.
template <typename Ref> class AbstractAgent {
  // NOTE: It can be statically checked if Ref is derived from
  // AbstractAgent<Ref>, but the static assertion cannot be defined directly in
  // the class body. That is because a class C derived from AbstractAgent<C> is
  // not complete when the static assertion in the definition of
  // AbstractAgent<C> would be evaluated. Thus, the static assertion is placed
  // in the constructor.
protected:
  // Ctor.
  // STATIC PRE: std::is_base_of<AbstractAgent<Ref>, Ref>::value
  AbstractAgent(void) noexcept;

public:
  // Dtor.
  virtual ~AbstractAgent(void) = default;

  // Tells if the Agent is in valid state.
  virtual operator bool(void) const noexcept = 0;

  // Tells if the given reference refers to this Agent.
  virtual bool operator==(const Ref &) const noexcept = 0;

  // Returns a reference to this Agent.
  virtual Ref self(void) noexcept = 0;

  // Sends the given Message to this Agent.
  // PRE: bool(*this)
  virtual void sendMessage(message_t &&) noexcept = 0;

  // Convenience template, which creates the Message from the given constant
  // lvalue references and sends to this Agent.
  // PRE: bool(*this)
  template <typename Type, typename... Types>
  void send(const Type &T, const Types &... Ts) noexcept;

  // Convenience template, which creates the Message from the given rvalue
  // references and sends to this Agent.
  // PRE: bool(*this)
  template <typename Type, typename... Types>
  void send(Type &&T, Types &&... Ts) noexcept;
};

template <typename Ref> AbstractAgent<Ref>::AbstractAgent(void) noexcept {
  STATIC_ASSERT((std::is_base_of<AbstractAgent<Ref>, Ref>::value),
                "not derived Agent"); // Sanity check.
}

template <typename Ref>
template <typename Type, typename... Types>
void AbstractAgent<Ref>::send(const Type &T, const Types &... Ts) noexcept {
  sendMessage(Message::create<Type, Types...>(T, Ts...));
}

template <typename Ref>
template <typename Type, typename... Types>
void AbstractAgent<Ref>::send(Type &&T, Types &&... Ts) noexcept {
  sendMessage(Message::create<Type, Types...>(std::move(T), std::move(Ts)...));
}

} // End namespace rosa

#endif // ROSA_CORE_ABSTRACTAGENT_HPP

