//===-- rosa/deluxe/DeluxeSystem.hpp ----------------------------*- C++ -*-===//
//
//                                 The RoSA Framework
//
//===----------------------------------------------------------------------===//
///
/// \file rosa/deluxe/DeluxeSystem.hpp
///
/// \author David Juhasz (david.juhasz@tuwien.ac.at)
///
/// \date 2017-2019
///
/// \brief Specialization of \c rosa::MessagingSystem for the *deluxe
/// interface*.
///
/// \see \c rosa::deluxe::DeluxeContext
///
//===----------------------------------------------------------------------===//

#ifndef ROSA_DELUXE_DELUXESYSTEM_HPP
#define ROSA_DELUXE_DELUXESYSTEM_HPP

#include "rosa/core/MessagingSystem.hpp"

#include "rosa/deluxe/DeluxeAgent.hpp"
#include "rosa/deluxe/DeluxeSensor.hpp"

namespace rosa {
namespace deluxe {

/// Implements and extends the \c rosa::MessagingSystem interface to be
/// used by \c rosa::deluxe::DeluxeContext.
///
/// The class is a specialization of \c rosa::MessagingSystem, where objects
/// of two specialized subtypes of \c rosa::Agent, \c rosa::deluxe::DeluxeSensor
/// and \c rosa::deluxe::DeluxeAgent, constitute a system. The class extends the
/// \c rosa::MessagingSystem interface with features required to implement the
/// *deluxe interface*.
///
/// \see rosa::deluxe::DeluxeContext
class DeluxeSystem : public MessagingSystem {

  friend class DeluxeContext;

public:
  /// Returns an object implementing the \c rosa::deluxe::DeluxeSystem
  /// interface.
  ///
  /// \param Name name of the new instance
  ///
  /// \return \c std::unique_ptr for the new instance of
  /// \c rosa::DeluxeSystem
  static std::unique_ptr<DeluxeSystem>
  createSystem(const std::string &Name) noexcept;

protected:
  /// Creates a new instance.
  ///
  /// \note Protected constructor restricts instantiation for subclasses.
  DeluxeSystem(void) noexcept = default;

public:
  /// Creates a \c rosa::deluxe::DeluxeSensor instance owned by \p this object
  /// and returns a \p rosa::AgentHandle for it.
  ///
  /// \tparam T type of data the new \c rosa::deluxe::DeluxeSensor operates on
  ///
  /// \param Name name of the new \c rosa::deluxe::DeluxeSensor
  /// \param F function to generate the next value with during normal operation
  ///
  /// \return \c rosa::AgentHandle for new \c rosa::deluxe::DeluxeSensor
  template <typename T>
  AgentHandle createSensor(const std::string &Name,
                           DeluxeSensor::D<T> &&F) noexcept;

  /// Creates a \c rosa::deluxe::DeluxeAgent instance owned by \p this object
  /// and returns a \c rosa::AgentHandle for it.
  ///
  /// \tparam T type of data the new \c rosa::deluxe::DeluxeAgent outputs
  /// \tparam As types of inputs the new \c rosa::deluxe::DeluxeAgent takes
  ///
  /// \param Name name of the new \c rosa::deluxe::DeluxeAgent
  /// \param F function for the new \c rosa::deluxe::DeluxeAgent to process
  /// input values and generate output with
  ///
  /// \return \c rosa::AgentHandle for new \c rosa::deluxe::DeluxeAgent
  template <typename T, typename... As>
  AgentHandle createAgent(const std::string &Name,
                          DeluxeAgent::D<T, As...> &&F) noexcept;

protected:
  /// Tells whether a \c rosa::AgentHandle refers to a
  /// \c rosa::deluxe::DeluxeSensor owned by \p this object.
  ///
  /// \param H \c rosa::AgentHandle to check
  ///
  /// \return whether \p H refers to a \c rosa::deluxe::DeluxeSensor owned by
  /// \p this object
  virtual bool isDeluxeSensor(const AgentHandle &H) const noexcept = 0;

  /// Extracts a const qualified \c rosa::deluxe::DeluxeSensor reference from a
  /// const qualified \c rosa::AgentHandle if possible.
  ///
  /// The function returns a \c rosa::Optional object containing a const
  /// qualified reference to a \c rosa::deluxe::DeluxeSensor object extracted
  /// from a const qualified \c rosa::AgentHandle instance if the referred
  /// object is of type \c rosa::deluxeDeluxeSensor and owned by \p this object.
  /// The returned \c rosa::Optional object is empty otherwise.
  ///
  /// \see rosa::deluxe::DeluxeSystem::isDeluxeSensor
  ///
  /// \param H \c rosa::AgentHandle to extract a \c rosa::deluxe::DeluxeSensor
  /// from
  ///
  /// \return const qualified reference to \c rosa::deluxe::DeluxeSensor if
  /// \p H refers to an object which is of that type and is owned by \p this
  /// object
  Optional<const DeluxeSensor &> getDeluxeSensor(const AgentHandle &H) const
      noexcept;

  /// Extracts a \c rosa::deluxe::DeluxeSensor reference from a
  /// \c rosa::AgentHandle if possible.
  ///
  /// The function returns a \c rosa::Optional object containing a reference to
  /// a \c rosa::deluxe::DeluxeSensor object extracted from a
  /// \c rosa::AgentHandle instance if the referred object is of type
  /// \c rosa::deluxeDeluxeSensor and owned by \p this object. The returned
  /// \c rosa::Optional object is empty otherwise.
  ///
  /// \see rosa::deluxe::DeluxeSystem::isDeluxeSensor
  ///
  /// \param H \c rosa::AgentHandle to extract a \c rosa::deluxe::DeluxeSensor
  /// from
  ///
  /// \return reference to \c rosa::deluxe::DeluxeSensor if \p H refers to an
  /// object which is of that type and is owned by \p this object
  Optional<DeluxeSensor &> getDeluxeSensor(AgentHandle &H) const noexcept;

  /// Tells whether a \c rosa::AgentHandle refers to a
  /// \c rosa::deluxe::DeluxeAgent owned by \p this object.
  ///
  /// \param H \c rosa::AgentHandle to check
  ///
  /// \return whether \p H refers to a \c rosa::deluxe::DeluxeAgent owned by
  /// \p this object
  virtual bool isDeluxeAgent(const AgentHandle &H) const noexcept = 0;

  /// Extracts a const qualified \c rosa::deluxe::DeluxeAgent reference from a
  /// const qualified \c rosa::AgentHandle if possible.
  ///
  /// The function returns a \c rosa::Optional object containing a const
  /// qualified reference to a \c rosa::deluxe::DeluxeAgent object extracted
  /// from a const qualified \c rosa::AgentHandle instance if the referred
  /// object is of type \c rosa::deluxeDeluxeAgent and owned by \p this object.
  /// The returned \c rosa::Optional object is empty otherwise.
  ///
  /// \see rosa::deluxe::DeluxeSystem::isDeluxeAgent
  ///
  /// \param H \c rosa::AgentHandle to extract a \c rosa::deluxe::DeluxeAgent
  /// from
  ///
  /// \return const qualified reference to \c rosa::deluxe::DeluxeAgent if \p H
  /// refers to an object which is of that type and is owned by \p this object
  Optional<const DeluxeAgent &> getDeluxeAgent(const AgentHandle &H) const
      noexcept;

  /// Extracts a \c rosa::deluxe::DeluxeAgent reference from a
  /// \c rosa::AgentHandle if possible.
  ///
  /// The function returns a \c rosa::Optional object containing a reference to
  /// a \c rosa::deluxe::DeluxeAgent object extracted from a
  /// \c rosa::AgentHandle instance if the referred object is of type
  /// \c rosa::deluxeDeluxeAgent and owned by \p this object. The returned
  /// \c rosa::Optional object is empty otherwise.
  ///
  /// \see rosa::deluxe::DeluxeSystem::isDeluxeAgent
  ///
  /// \param H \c rosa::AgentHandle to extract a \c rosa::deluxe::DeluxeAgent
  /// from
  ///
  /// \return reference to \c rosa::deluxe::DeluxeAgent if \p H refers to an
  /// object which is of that type and is owned by \p this object
  Optional<DeluxeAgent &> getDeluxeAgent(AgentHandle &H) const noexcept;
};

template <typename T>
AgentHandle DeluxeSystem::createSensor(const std::string &Name,
                                       DeluxeSensor::D<T> &&F) noexcept {
  Agent &DS = createUnit<DeluxeSensor, MessagingSystem>(
      [&](const id_t Id, MessagingSystem &S) {
        return new DeluxeSensor(atoms::SensorKind, Id, Name, S, std::move(F));
      });
  return {DS};
}

template <typename T, typename... As>
AgentHandle
DeluxeSystem::createAgent(const std::string &Name,
                          DeluxeAgent::D<T, As...> &&F) noexcept {
	
  Agent &DA = createUnit<DeluxeAgent, DeluxeSystem>(
      [&](const id_t Id, DeluxeSystem &S) {
        return new DeluxeAgent(atoms::AgentKind, Id, Name, S, std::move(F));
      });
  return {DA};
}

} // End namespace deluxe
} // End namespace rosa

#endif // ROSA_LIB_DELUXE_DELUXESYSTEM_HPP
