// vi:set ft=cpp: -*- Mode: C++ -*-
/*
 * (c) 2014 Alexander Warg <alexander.warg@kernkonzept.com>
 *
 * License: see LICENSE.spdx (in this directory or the directories above)
 */

/// \file

#pragma once

// very simple type traits for basic L4 functions, for a more complete set
// use <l4/cxx/type_traits> or the standard <type_traits>.

namespace L4 {

/**
 * L4 basic type helpers for C++
 */
namespace Types {

  /**
   * Template for defining typical Flags bitmaps.
   * \tparam BITS_ENUM   enum type that defines a name for each bit in
   *                     the bitmap. The values of the enum members
   *                     must be the number of the bit (_not_ a mask).
   * \tparam UNDERLYING  The underlying data type used to represent the bitmap.
   *
   *
   * The resulting data type provides a type-safe version that allows
   * bitwise `and` and `or` operations with the BITS_ENUM members.
   * As well as, test for `0`or !`0`.
   *
   * Example:
   * ~~~~
   * enum Test_flag
   * {
   *   Do_weak_tests,
   *   Do_strong_tests
   * };
   *
   * typedef L4::Types::Flags<Test_flag> Test_flags;
   *
   * Test_flags x = Do_weak_tests;
   *
   * if (x & Do_strong_tests) { ... }
   * x |= Do_strong_tests;
   * if (x & Do_strong_tests) { ... }
   * ~~~~
   */
  template<typename BITS_ENUM, typename UNDERLYING = unsigned long>
  class Flags
  {
  public:
    /// type of the underlying value
    typedef UNDERLYING value_type;
    /// enum type defining a name for each bit
    typedef BITS_ENUM bits_enum_type;
    /// the Flags<> type itself
    typedef Flags<BITS_ENUM, UNDERLYING> type;

  private:
    value_type _v;
    explicit Flags(value_type v) : _v(v) {}

  public:
    /// The none type to get an empty bitmap
    enum None_type { None /**< Use this to get an empty bitmap */ };

    /**
     * Make an empty bitmap.
     *
     * Usually used for implicit conversion from `Flags::None`.
     * ~~~
     * Flags x = Flags::None;
     * ~~~
     */
    Flags(None_type) : _v(0) {}

    /// Make default Flags
    Flags() : _v(0) {}

    /**
     * Make flags from bit name.
     *
     * Usually used for implicit conversion for a bit name.
     * ~~~
     * Test_flags f = Do_strong_tests;
     * ~~~
     */
    Flags(BITS_ENUM e) : _v((value_type{1}) << e) {}

    /**
     * Make flags from a raw value of \a value_type.
     *
     * This function may be used for example in C wrapper code.
     */
    static type from_raw(value_type v) { return type(v); }

    /// Support for `if (flags)` syntax (test for non-empty flags).
    explicit operator bool () const
    { return _v != 0; }

    /// Support for `if (!flags)` syntax (test for empty flags).
    bool operator ! () const { return _v == 0; }

    /// Support `|` of two compatible Flags types.
    friend type operator | (type lhs, type rhs)
    { return type(lhs._v | rhs._v); }

    /// Support `|` of Flags type and bit name.
    friend type operator | (type lhs, bits_enum_type rhs)
    { return lhs | type(rhs); }

    /// Support `&` of two compatible Flags types.
    friend type operator & (type lhs, type rhs)
    { return type(lhs._v & rhs._v); }

    /// Support `&` of Flags type and bit name.
    friend type operator & (type lhs, bits_enum_type rhs)
    { return lhs & type(rhs); }

    /// Support `|=` of two compatible Flags types.
    type &operator |= (type rhs) { _v |= rhs._v; return *this; }
    /// Support `|=` of Flags type and bit name.
    type &operator |= (bits_enum_type rhs) { return operator |= (type(rhs)); }

    /// Support `&=` of two compatible Flags types.
    type &operator &= (type rhs) { _v &= rhs._v; return *this; }
    /// Support `&=` of Flags type and bit name.
    type &operator &= (bits_enum_type rhs) { return operator &= (type(rhs)); }

    /// Support `~` for Flags types.
    type operator ~ () const { return type(~_v); }

    /**
     * Clear the given flag.
     * \param flag  The flag that shall be cleared.
     *
     * `flags.clear(The_flag)` is a shortcut for `flags &= ~Flags(The_flag)`.
     */
    type &clear(bits_enum_type flag) { return operator &= (~type(flag)); }

    /// Get the underlying value.
    value_type as_value() const { return _v; }
  };

  /**
   * Metafunction to get an unsigned integral type for the given size.
   *
   * \tparam SIZE  The size of the integer in bytes.
   */
  template<unsigned SIZE, bool = true> struct Int_for_size;

  template<> struct Int_for_size<sizeof(unsigned char), true>
  { typedef unsigned char type; };

  template<> struct Int_for_size<sizeof(unsigned short),
                                 (sizeof(unsigned short) > sizeof(unsigned char))>
  { typedef unsigned short type; };

  template<> struct Int_for_size<sizeof(unsigned),
                                 (sizeof(unsigned) > sizeof(unsigned short))>
  { typedef unsigned type; };

  template<> struct Int_for_size<sizeof(unsigned long),
                                 (sizeof(unsigned long) > sizeof(unsigned))>
  { typedef unsigned long type; };

  template<> struct Int_for_size<sizeof(unsigned long long),
                                 (sizeof(unsigned long long) > sizeof(unsigned long))>
  { typedef unsigned long long type; };

  /**
   * Metafunction to get an integral type of the same size as `T`.
   *
   * \tparam T  The type for which an unsigned integral type with
   *            the same size is needed.
   */
  template<typename T> struct Int_for_type
  {
    /**
     * The resulting unsigned integer type with the size like `T`.
     */
    typedef typename Int_for_size<sizeof(T)>::type type;
  };

  /**
   * Mixin class to define a set of friend bitwise operators on `DT`.
   *
   * \tparam DT  The type usually inheriting from Flags_ops_t
   *             with a member \a raw of enum or integral type.
   */
  template<typename DT>
  struct Flags_ops_t
  {
    /// bitwise or for DT
    friend constexpr DT operator | (DT l, DT r)
    { return DT(l.raw | r.raw); }

    /// bitwise and for DT
    friend constexpr DT operator & (DT l, DT r)
    { return DT(l.raw & r.raw); }

    /// Bitwise difference (clear bits) for DT.
    friend constexpr DT operator - (DT l, DT r)
    { return DT(l.raw & ~r.raw); }

    /// equality for DT
    friend constexpr bool operator == (DT l, DT r)
    { return l.raw == r.raw; }

    /// inequality for DT
    friend constexpr bool operator != (DT l, DT r)
    { return l.raw != r.raw; }

    /// bitwise or assignment for DT
    constexpr DT &operator |= (DT const r)
    {
      static_cast<DT *>(this)->raw |= r.raw;
      return *static_cast<DT *>(this);
    }

    /// bitwise and assignment for DT
    constexpr DT &operator &= (DT const r)
    {
      static_cast<DT *>(this)->raw &= r.raw;
      return *static_cast<DT *>(this);
    }

    /// Bitwise difference (clear bits) assignment for DT.
    constexpr DT &operator -= (DT const r)
    {
      static_cast<DT *>(this)->raw &= ~r.raw;
      return *static_cast<DT *>(this);
    }

    /// explicit conversion to bool for tests
    explicit constexpr operator bool () const
    { return static_cast<DT const *>(this)->raw != 0; }

    /// bitwise negation for DT
    constexpr DT operator ~ () const
    { return DT(~static_cast<DT const *>(this)->raw); }
  };

  /**
   * Template type to define a flags type with bitwise operations.
   *
   * \tparam DT  determinator type to make the resulting type
   *             unique (unused).
   * \tparam T   underlying type used to store the bits, usually
   *             an integral type.
   */
  template<typename DT, typename T>
  struct Flags_t : Flags_ops_t<Flags_t<DT, T>>
  {
    /// Raw integral value.
    T raw;
    /// Default (uninitializing) constructor
    Flags_t() = default;
    /// Explicit initialization from the underlying type.
    explicit constexpr Flags_t(T f) : raw(f) {}
  };

  /// Map a sequence of any types to the void type.
  template<typename...> using Void = void;

  /// Helper template for Add_rvalue_reference.
  template<typename T, typename = void> struct __Add_rvalue_reference_helper
  { using type = T; };

  /// Helper template for Add_rvalue_reference.
  template<typename T> struct __Add_rvalue_reference_helper<T, Void<T &&>>
  { using type = T &&; };

  /// Create an rvalue reference of the given type.
  template<typename T> struct Add_rvalue_reference
  { using type = typename __Add_rvalue_reference_helper<T>::type; };

  /// Helper type for the Add_rvalue_reference.
  template<typename T> using Add_rvalue_reference_t
    = typename Add_rvalue_reference<T>::type;

  /**
   * Template for writing typed expressions in unevaluated contexts.
   *
   * In unevaluated contexts, the template converts a type (possibly an
   * incomplete type) to an expression of that type.
   *
   * \tparam T  Type to be converted to an expression of that type.
   */
  template<typename T> Add_rvalue_reference_t<T> declval() noexcept;

  /**
   * Boolean meta type
   * \tparam V  The boolean value
   * \ingroup l4_cxx_ipc_internal
   */
  template< bool V > struct Bool
  {
    typedef Bool<V> type; ///< The meta type itself
    enum { value = V };   ///< The boolean value
  };

  /// False meta value
  /// \ingroup l4_cxx_ipc_internal
  struct False : Bool<false> {};

  /// True meta value
  /// \ingroup l4_cxx_ipc_internal
  struct True : Bool<true> {};

  /// Wrapper for a static constant of the given type.
  template<typename T, T Value>
  struct Integral_constant
  {
    static T const value = Value;

    typedef T value_type;
    typedef Integral_constant<T, Value> type;
  };

  /**
   * Check whether the given type is an enumeration type.
   *
   * \note The implementation relies on the intrinsic __is_enum() compiler type
   *       trait that is provided by the mainstream C++ compilers. There is no
   *       fully portable and future-proof way of implementing this template
   *       (the only fragile and unmaintainable possibility is to exclude all
   *       other types to identify the enumeration type).
   *
   * \tparam T  Type to check whether it is an enumeration type.
   */
  template<typename T>
  struct Is_enum : Integral_constant<bool, __is_enum(T)> {};

  /**
   * Helper template for Underlying_type.
   *
   * \note The implementation relies on the intrinsic __underlying_type()
   *       compiler type trait that is provided by the mainstream C++
   *       compilers. There is no fully portable and future-proof way of
   *       implementing this template (the only fragile and unmaintainable
   *       possibility is to evaluate all possible underlying types).
   *
   * \tparam T  Enumeration type to get the underlying type of.
   */
  template<typename T, bool = Is_enum<T>::value>
  struct __Underlying_type_helper { using type = __underlying_type(T); };

  /// Helper template for Underlying_type.
  template<typename T> struct __Underlying_type_helper<T, false> {};

  /// Get an underlying type of an enumeration type.
  template<typename T> struct Underlying_type
    : public __Underlying_type_helper<T> {};

  /// Helper type for Underlying_type.
  template<typename T> using Underlying_type_t
    = typename Underlying_type<T>::type;

  /*********************/
  /**
   * Compare two data types for equality
   * \tparam A  The first data type
   * \tparam B  The second data type
   * \ingroup l4_cxx_ipc_internal
   *
   * The result is the boolean True if A and B are the same types.
   */
  template<typename A, typename B>
  struct Same : False {};

  template<typename A>
  struct Same<A, A> : True {};

  template<bool, typename = void> struct Enable_if {};
  template<typename T> struct Enable_if<true, T> { typedef T type; };

  /// Helper type for Enable_if.
  template<bool Condition, typename T = void>
    using Enable_if_t = typename Enable_if<Condition, T>::type;

  template<typename T1, typename T2, typename T = void>
  struct Enable_if_same : Enable_if<Same<T1, T2>::value, T> {};

  template<typename T> struct Remove_const { typedef T type; };
  template<typename T> struct Remove_const<T const> { typedef T type; };
  template<typename T> struct Remove_volatile { typedef T type; };
  template<typename T> struct Remove_volatile<T volatile> { typedef T type; };
  template<typename T> struct Remove_cv
  { typedef typename Remove_const<typename Remove_volatile<T>::type>::type type; };

  template<typename T> struct Remove_pointer { typedef T type; };
  template<typename T> struct Remove_pointer<T*> { typedef T type; };
  template<typename T> struct Remove_reference { typedef T type; };
  template<typename T> struct Remove_reference<T&> { typedef T type; };
  template<typename T> struct Remove_pr { typedef T type; };
  template<typename T> struct Remove_pr<T&> { typedef T type; };
  template<typename T> struct Remove_pr<T*> { typedef T type; };
} // Types

} // L4

/**
 * Mechanism to opt-in for enum bitwise operators.
 *
 * For an enumeration type T to opt-in for bitwise operators, there needs to be
 * a declaration of a function
 *
 *   void enum_bitops_enable(T)
 *
 * available for the argument-dependent lookup (the function is never actually
 * called, thus no definition is required and no code is generated).
 */
namespace Enum_bitops {
  using namespace L4::Types;

  /// Marker for the opt-in ADL function.
  template<typename, typename = void> struct Has_marker : False {};

  /// Marker for the opt-in ADL function.
  template<typename T>
  struct Has_marker<T, Void<decltype(enum_bitops_enable(declval<T>()))>>
    : True {};

  /// Check whether the given enum type opts in for the bitwise operators.
  template<typename T>
  struct Enable
    : Integral_constant<bool, Is_enum<T>::value && Has_marker<T>::value> {};
} // Enum_bitops

/**
 * Bitwise operators on enumeration types.
 *
 * These operators allow to use the enum type as a bitmask type with '~', '&'
 * and '|' operators that keep the enum type as result. The compound assignment
 * operators '&=' and '|=' are also provided.
 *
 * The set-difference '-' and '-=' operators (a shorthand for intersection with
 * a negated second argument) can be used to clear specific bits from the enum
 * value. There is also the to_underlying() helper function.
 */
inline namespace Enum_bitops_impl {
  /// Convert enum value to its underlying type value.
  template<typename T,
           typename = L4::Types::Enable_if_t<L4::Types::Is_enum<T>::value>>
  constexpr L4::Types::Underlying_type_t<T> to_underlying(T const arg) noexcept
  { return static_cast<L4::Types::Underlying_type_t<T>>(arg); }

  /// Negate enum value.
  template<typename T,
           typename = L4::Types::Enable_if_t<Enum_bitops::Enable<T>::value>>
  constexpr T operator ~ (T const a) noexcept
  { return static_cast<T>(~to_underlying(a)); }

  /// Intersect enum values.
  template<typename T,
           typename = L4::Types::Enable_if_t<Enum_bitops::Enable<T>::value>>
  constexpr T operator & (T l, T r) noexcept
  { return static_cast<T>(to_underlying(l) & to_underlying(r)); }

  /// Intersect and assign enum values.
  template<typename T,
           typename = L4::Types::Enable_if_t<Enum_bitops::Enable<T>::value>>
  constexpr T &operator &= (T &a, T const b) noexcept
  {
    a = a & b;
    return a;
  }

  /// Union enum values.
  template<typename T,
           typename = L4::Types::Enable_if_t<Enum_bitops::Enable<T>::value>>
  constexpr T operator | (T l, T r) noexcept
  { return static_cast<T>(to_underlying(l) | to_underlying(r)); }

  /// Union and assign enum values.
  template<typename T,
           typename = L4::Types::Enable_if_t<Enum_bitops::Enable<T>::value>>
  constexpr T &operator |= (T &a, T const b) noexcept
  {
    a = a | b;
    return a;
  }

  /// Difference (intersect with negation, clear bits) enum values.
  template<typename T,
           typename = L4::Types::Enable_if_t<Enum_bitops::Enable<T>::value>>
  constexpr T operator - (T l, T r) noexcept
  { return static_cast<T>(to_underlying(l) & ~to_underlying(r)); }

  /// Difference (intersect with negation, clear bits) and assign enum values.
  template<typename T,
           typename = L4::Types::Enable_if_t<Enum_bitops::Enable<T>::value>>
  constexpr T &operator -= (T &a, T const b) noexcept
  {
    a = a - b;
    return a;
  }
} // Enum_bitops_impl
