//===-- 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) noexcept;

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;

  /// Compares \p this object to another \c rosa::AgentHandle instance.
  ///
  /// The comparison is based on the memory addresses of the wrapped
  /// \c rosa::Agent instances.
  ///
  /// \param H \c rosa::AgentHandle to compare to
  ///
  /// \return if \p this object a \c rosa::Agent instance whose address is less
  /// than that of \p H
  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;
};


/// Template specialization for optionally storing \c rosa::AgentHandle
/// instances.
///
/// \ingroup Optional
///
/// Due to \c rosa::AgentHandle not supporting copying and moving of instances,
/// the member functions in this class fall back to destroying the old stored
/// object and creating a new one whenever the stored value is to be changed.
template <> class Optional<AgentHandle> {
public:
  using Type = AgentHandle;

  /// Creates an instance without value.
  ///
  /// \note Use it with its default parameter.
  Optional(const none_t & = none) : Valid(false) {}

  /// Creates a valid instance with value.
  ///
  /// \param X value to store in the object
  Optional(AgentHandle X) : Valid(false) {
    cr(std::move(X));
  }

  /// Creates an instance as a copy of another one.
  ///
  /// \param Other the instance whose state to copy
  Optional(const Optional &Other) : Valid(false) {
    if (Other.Valid) {
      cr(Other.Value);
    }
  }

  /// Creates an instance as a copy of another one.
  ///
  /// \param Other the instance whose state to obtain
  Optional(Optional &&Other) noexcept : Valid(false) {
    if (Other.Valid) {
      cr(std::move(Other.Value));
    }
  }

  /// Destroys \p this object.
  ~Optional(void) { destroy(); }

  /// Updates \p this object by copying the state of another one.
  ///
  /// \param Other the instance whose state to copy
  ///
  /// \return reference of the updated instance
  Optional &operator=(const Optional &Other) {
    if (Valid) {
      destroy();
    }
    if (Other.Valid) {
      cr(Other.Value);
    }
    return *this;
  }

  /// Updates \p this object by copying the state of another one.
  ///
  /// \param Other the instance whose state to obtain
  ///
  /// \return reference of the updated instance
  Optional &operator=(Optional &&Other) noexcept {
    if (Valid) {
      destroy();
    }
    if (Other.Valid) {
      cr(std::move(Other.Value));
    }
    return *this;
  }

  /// Checks whether \p this object contains a value.
  ///
  /// \return if \p this object contains a value
  explicit operator bool(void) const { return Valid; }

  /// Checks whether \p this object does not contain a value.
  ///
  /// \return if \p this object does not contain a value
  bool operator!(void)const { return !Valid; }

  /// Returns the value stored in \p this object.
  ///
  /// \return reference of the stored value
  ///
  /// \pre \p this object contains a value
  AgentHandle &operator*(void) {
    ASSERT(Valid);
    return Value;
  }

  /// Returns the value stored in \p this object.
  ///
  /// \return reference of the stored value
  ///
  /// \pre \p this object contains a value
  const AgentHandle &operator*(void)const {
    ASSERT(Valid);
    return Value;
  }

  /// Returns the value stored in \p this object.
  ///
  /// \return pointer to the stored value
  ///
  /// \pre \p this object contains a value
  const AgentHandle *operator->(void)const {
    ASSERT(Valid);
    return &Value;
  }

  /// Returns the value stored in \p this object.
  ///
  /// \return pointer of the stored value
  ///
  /// \pre \p this object contains a value
  AgentHandle *operator->(void) {
    ASSERT(Valid);
    return &Value;
  }

  /// Returns the value stored in \p this object.
  ///
  /// \return reference of the stored value
  ///
  /// \pre \p this object contains a value
  AgentHandle &value(void) {
    ASSERT(Valid);
    return Value;
  }

  /// Returns the value stored in \p this object.
  ///
  /// \return reference of the stored value
  ///
  /// \pre \p this object contains a value
  const AgentHandle &value(void) const {
    ASSERT(Valid);
    return Value;
  }

  /// Returns the stored value or a default.
  ///
  /// If \p this object contains a value, then the stored value is returned. A
  /// given default value is returned otherwise.
  ///
  /// \param DefaultValue the value to return if \p this object does not contain
  /// a value
  ///
  /// \return reference to either the stored value or \p DefaultValue if \p this
  /// object does not contain a value
  const AgentHandle &valueOr(const AgentHandle &DefaultValue) const {
    return Valid ? Value : DefaultValue;
  }

private:
  /// Deallocates the stored value if any.
  void destroy(void) {
    if (Valid) {
      Value.~AgentHandle();
      Valid = false;
    }
  }

  /// Updates the state of \p this object by copying a value into it.
  ///
  /// \tparam V type of \p X
  ///
  /// \param X value to copy
  ///
  /// \pre \p this object does not contain a value
  template <typename V> void cr(V &&X) {
    ASSERT(!Valid);
    Valid = true;
    new (&Value) AgentHandle(std::forward<V>(X));
  }

  /// Denotes if \p this object contains a value.
  bool Valid;

  /// Holds the stored value if any.
  union {
    AgentHandle Value; ///< The stored value.
  };
};




} // End namespace rosa

#endif // ROSA_CORE_AGENTHANDLE_HPP
