/*******************************************************************************
 *
 * File:     Message.hpp
 *
 * Contents: Declaration of Message base-class.
 *
 * Copyright 2017
 *
 * Author: David Juhasz (david.juhasz@tuwien.ac.at)
 *
 ******************************************************************************/

#ifndef ROSA_CORE_MESSAGE_HPP
#define ROSA_CORE_MESSAGE_HPP

#include "rosa/support/log.h"
#include "rosa/support/type_token.hpp"

namespace rosa {

// Message interface. The interface provides means to check the type of the
// stored values, but actual data is to be managed by derived implementations.
// Messages are immutable data objects, obtaining their data upon creation and
// providing only constant references for the stored values.
// NOTE: Any reference obtained from a Message instance remains valid only as
// long as the owning Message object is not destroyed.
class Message {
protected:
  // Ctor.
  // NOTE: No implementation for empty list.
  template <typename Type, typename... Ts> Message(Type, Ts...) noexcept;

  // No copy and move.
  Message(const Message &) = delete;
  Message(Message &&) = delete;
  Message &operator=(const Message &) = delete;
  Message &operator=(Message &&) = delete;

public:
  // A valid, non-empty token representing the types of the values stored in
  // the Message.
  const Token T;

  // The number of types encoded in T, that is the number of values stored in
  // Message.
  const size_t Size;

  // Virtual dtor.
  virtual ~Message(void);

  // Tells if the value in position Pos is of type Type.
  // NOTE: Token encodes atoms as AtomValue and not directly AtomConstants.
  // PRE: Pos < Size
  template <typename Type> bool isTypeAt(const size_t Pos) const noexcept;

  // Gives a constant reference of the value of type Type in position Pos.
  // PRE: Pos < Size && isTypeAt(Pos)
  template <typename Type>
  const Type &getValueAt(const size_t Pos) const noexcept;

protected:
  // Provides an untyped pointer for the value in position Pos.
  // PRE: Pos < Size
  virtual const void *getPointerTo(const size_t Pos) const noexcept = 0;
};

template <typename Type, typename... Ts>
Message::Message(Type, Ts...) noexcept : T(TypeToken<Type, Ts...>::Value),
                                         Size(lengthOfToken(T)) {
  ASSERT(validToken(T) &&
         lengthOfToken(T) == (1 + sizeof...(Ts))); // Sanity check.
  LOG_TRACE("Creating Message with Token(" + to_string(T) + ")");
}

template <typename Type>
bool Message::isTypeAt(const size_t Pos) const noexcept {
  ASSERT(Pos < Size);
  Token T_ = T; // NOLINT
  dropNOfToken(T_, Pos);
  return isHeadOfTokenTheSameType<Type>(T_);
}

template <typename Type>
const Type &Message::getValueAt(const size_t Pos) const noexcept {
  ASSERT(Pos < Size && isTypeAt<Type>(Pos));
  return *static_cast<const Type *>(getPointerTo(Pos));
}

} // End namespace rosa

#endif // ROSA_CORE_MESSAGE_HPP

