/******************************************************************************
 *
 * File:     squashed_int.hpp
 *
 * Contents: Facilities for squashing integer types into standard equivalents.
 *
 * Copyright 2017
 *
 * Author: David Juhasz (david.juhasz@tuwien.ac.at)
 *
 * This implementation is partially based on the squashed_int implementation of
 * CAF.
 * TODO: Check license.
 *
 ******************************************************************************/

#ifndef ROSA_SUPPORT_SQUASHED_INT_HPP
#define ROSA_SUPPORT_SQUASHED_INT_HPP

#include "rosa/support/type_list.hpp"
#include "rosa/support/type_pair.hpp"

namespace rosa {

// Compile-time list of integer types.
// NOTE: This list is used to select a proper type as type_nr_t,
// always make sure that type_nr_t remains correct whenever changing the list.
using IntegerTypesBySize = TypeList< // bytes
    none_t,                          // 0
    TypePair<int8_t, uint8_t>,       // 1
    TypePair<int16_t, uint16_t>,     // 2
    none_t,                          // 3
    TypePair<int32_t, uint32_t>,     // 4
    none_t,                          // 5
    none_t,                          // 6
    none_t,                          // 7
    TypePair<int64_t, uint64_t>      // 8
    >;

// Squashes integer types into [u]int_[8|16|32|64]_t equivalents.
// STATIC PRE: std::is_integral<T>::value
template <typename T> struct SquashedInt {
  STATIC_ASSERT((std::is_integral<T>::value), "squashing a non-integral type");
  using TPair = typename TypeListAt<IntegerTypesBySize, sizeof(T)>::Type;
  using Type =
      typename std::conditional<std::is_signed<T>::value, typename TPair::First,
                                typename TPair::Second>::type;
};

// Convenience alias for obtaining a squashed int type.
template <typename T>
using squashed_int_t = typename SquashedInt<T>::Type;

// Squashes T if T is integral, otherwise uses T.
template <typename T, bool IsIntegral = std::is_integral<T>::value>
struct SquashedType {
  using Type = T;
};

template <typename T>
struct SquashedType<T, true> {
  using Type = squashed_int_t<T>;
};

// Convenience alias for obtaining a squashed type.
template <typename T>
using squashed_t = typename SquashedType<T>::Type;

// Replaces all integral types with their corresponding squashed integer.
template <typename List> struct SquashedTypeList;

template <> struct SquashedTypeList<EmptyTypeList> {
  using Type = EmptyTypeList;
};

template <typename T, typename... Ts>
struct SquashedTypeList<TypeList<T, Ts...>> {
  using Type = typename TypeListPush<
      squashed_t<T>, typename SquashedTypeList<TypeList<Ts...>>::Type>::Type;
};

} // End namespace rosa

#endif // ROSA_SUPPORT_SQUASHED_INT_HPP

