/*******************************************************************************
 *
 * File:     MessageMatcher.hpp
 *
 * Contents: Implementation of MessageMatcher.
 *
 * Copyright 2017
 *
 * Author: David Juhasz (david.juhasz@tuwien.ac.at)
 *
 ******************************************************************************/

#ifndef ROSA_CORE_MESSAGEMATCHER_HPP
#define ROSA_CORE_MESSAGEMATCHER_HPP

#include "rosa/core/Message.hpp"

#include <tuple>

namespace rosa {

// Template class with static functions type-checking a Message instance and
// extracting stored values from Message instsances into std::tuple instances
// with matching type arguments.
template <typename List> struct MessageMatcher;

// Definition of MessageMatcher for non-empty lists of types, like Message
// itself.
template <typename Type, typename... Ts>
struct MessageMatcher<TypeList<Type, Ts...>> {

  // Type Token associated with the give TypeList.
  static constexpr Token T = TypeToken<Type, Ts...>::Value;

  // Tells if stored values in Msg are matching types given as
  // TypeList<T, Ts...>, considering exact AtomConstants instead of AtomValues.
  static inline bool doesStronglyMatch(const Message &Msg) noexcept;

  // Gives a std::tuple with references to values stored in a type-matching Msg.
  // PRE: doesStronglyMatch(Msg)
  static inline std::tuple<const Type &, const Ts &...>
  extractedValues(const Message &Msg) noexcept;
};

// Convenience template alias turning a list of types into a TypeList for
// MessageMatcher.
template <typename Type, typename...Ts>
using MsgMatcher = MessageMatcher<TypeList<Type, Ts...>>;

// Nested namespace with implementation for features of MessageMatcher,
// consider it private.
namespace {

// Helper struct implementing type-checking and value extraction for
// MessageMatcher.
template <typename List> struct MessageMatcherImpl;

// Specialization handling the empty list of types.
template <> struct MessageMatcherImpl<EmptyTypeList> {
  static inline bool doesStronglyMatchFrom(const Message &Msg,
                                           const size_t Pos) noexcept {
    // Matching EmptyTypeList only if reached the end of the stored types.
    return Pos == Msg.Size;
  }

  static inline std::tuple<> extractedValuesFrom(const Message &Msg,
                                                 const size_t Pos) noexcept {
    // It is valid to extract an empty list only if we reached the end of
    // stored values.
    ASSERT(doesStronglyMatchFrom(Msg, Pos));
    return std::tie();
  }
};

// Specialization handling an AtomValue in the head.
template <AtomValue V, typename... Ts>
struct MessageMatcherImpl<TypeList<AtomConstant<V>, Ts...>> {

  static inline bool doesHeadStronglyMatchAt(const Message &Msg,
                                             const size_t Pos) noexcept {
    // Matching an AtomConstant in the head if there is a type stored at Pos,
    // the stored type is AtomValue, and the corresponding value matches the
    // AtomValue V.
    return Pos < Msg.Size && Msg.isTypeAt<AtomValue>(Pos) &&
           Msg.valueAt<AtomValue>(Pos) == V;
  }

  static inline bool doesStronglyMatchFrom(const Message &Msg,
                                           const size_t Pos) noexcept {
    // Matching a non-empty list if the head is matching and the rest of the
    // list is matching.
    return doesHeadStronglyMatchAt(Msg, Pos) &&
           MessageMatcherImpl<TypeList<Ts...>>::doesStronglyMatchFrom(Msg,
                                                                      Pos + 1);
  }

  static inline std::tuple<const AtomConstant<V> &, const Ts &...>
  extractedValuesFrom(const Message &Msg, const size_t Pos) noexcept {
    // Extracting for a non-empty list with a matching AtomConstant in the head
    // by getting the encoded AtomConstant and concatenating it with values
    // extracted for the rest of the list.
    ASSERT(doesHeadStronglyMatchAt(Msg, Pos));
    return std::tuple_cat(
        std::tie(AtomConstant<V>::Value),
        MessageMatcherImpl<TypeList<Ts...>>::extractedValuesFrom(Msg, Pos + 1));
  }

};

// Specialization handling an regular builtin type (not an AtomConstant) in the
// head.
template <typename T, typename... Ts>
struct MessageMatcherImpl<TypeList<T, Ts...>> {

  static inline bool doesHeadStronglyMatchAt(const Message &Msg,
                                             const size_t Pos) noexcept {
    // Matching the head if there is a type stored at Pos, and the stored type
    // is T.
    return Pos < Msg.Size && Msg.isTypeAt<T>(Pos);
  }

  static inline bool doesStronglyMatchFrom(const Message &Msg,
                                           const size_t Pos) noexcept {
    // Matching a non-empty list if the head is matching and the rest of the
    // list is matching.
    return doesHeadStronglyMatchAt(Msg, Pos) &&
           MessageMatcherImpl<TypeList<Ts...>>::doesStronglyMatchFrom(Msg,
                                                                      Pos + 1);
  }

  static inline std::tuple<const T &, const Ts &...>
  extractedValuesFrom(const Message &Msg, const size_t Pos) noexcept {
    // Extracting for a non-empty list with a matching head by getting the
    // value for the head and concatenating it with values extracted for the
    // rest of the list.
    ASSERT(doesHeadStronglyMatchAt(Msg, Pos));
    return std::tuple_cat(
        std::tie(Msg.valueAt<T>(Pos)),
        MessageMatcherImpl<TypeList<Ts...>>::extractedValuesFrom(Msg, Pos + 1));
  }
};

} // End namespace

template <typename Type, typename... Ts>
bool MessageMatcher<TypeList<Type, Ts...>>::doesStronglyMatch(
    const Message &Msg) noexcept {
  // NOTE: Fail quick on T, then match against list with squashed integers the
  // way Tokens are generated.
  return T == Msg.T &&
         MessageMatcherImpl<typename SquashedTypeList<
             TypeList<Type, Ts...>>::Type>::doesStronglyMatchFrom(Msg, 0);
}

template <typename Type, typename... Ts>
std::tuple<const Type &, const Ts &...>
MessageMatcher<TypeList<Type, Ts...>>::extractedValues(
    const Message &Msg) noexcept {
  ASSERT(doesStronglyMatch(Msg));
  // NOTE: Match against list with squashed integers as Tokens are generated.
  return MessageMatcherImpl<typename SquashedTypeList<
      TypeList<Type, Ts...>>::Type>::extractedValuesFrom(Msg, 0);
}

} // End namespace rosa

#endif // ROSA_CORE_MESSAGEMATCHER_HPP

