// 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;
  };

  /**
   * Helper macro to define a set of bitwise operators on an
   * enum type.
   *
   * This allows to use the enum type as bitmask type with '&',
   * '|', and '~' operators that keep the enum type as result.
   */
#define L4_TYPES_FLAGS_OPS_DEF(T)                                            \
    friend constexpr T operator ~ (T f)                                      \
    {                                                                        \
      return T(~static_cast<typename L4::Types::Int_for_type<T>::type>(f));  \
    }                                                                        \
                                                                             \
    friend constexpr T operator | (T l, T r)                                 \
    {                                                                        \
      return T(static_cast<typename L4::Types::Int_for_type<T>::type>(l)     \
               | static_cast<typename L4::Types::Int_for_type<T>::type>(r)); \
    }                                                                        \
                                                                             \
    friend constexpr T operator & (T l, T r)                                 \
    {                                                                        \
      return T(static_cast<typename L4::Types::Int_for_type<T>::type>(l)     \
               & static_cast<typename L4::Types::Int_for_type<T>::type>(r)); \
    }

  /**
   * 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); }

    /// 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
    DT operator |= (DT r)
    {
      static_cast<DT *>(this)->raw |= r.raw;
      return *static_cast<DT *>(this);
    }

    /// bitwise and assignment for DT
    DT operator &= (DT 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) costructor
    Flags_t() = default;
    /// Explicit initialization from the underlying type.
    explicit constexpr Flags_t(T f) : raw(f) {}
  };


  /**
   * 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> {};

  /*********************/
  /**
   * 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 EXP, typename T = void> struct Enable_if {};
  template<typename T> struct Enable_if<true, T> { typedef 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
