// vi:set ft=cpp: -*- Mode: C++ -*-
/*
 * (c) 2012 Alexander Warg <warg@os.inf.tu-dresden.de>,
 *     economic rights: Technische Universität Dresden (Germany)
 *
 * License: see LICENSE.spdx (in this directory or the directories above)
 */

#pragma once

#include "type_list"

/** Our C++ library. */
namespace cxx {

/**
 * Definition for a member (part) of a bit field.
 *
 * \param T    The underlying type of the bit field.
 * \param LSB  The least significant bit of our bits.
 * \param MSB  The most significant bit of our bits.
 */
template<typename T, unsigned LSB, unsigned MSB>
class Bitfield
{
private:
  typedef remove_reference_t<T> Base_type;

  static_assert(MSB >= LSB, "boundary mismatch in bit-field definition");
  static_assert(MSB < sizeof(Base_type) * 8, "MSB outside of bit-field type");
  static_assert(LSB < sizeof(Base_type) * 8, "LSB outside of bit-field type");

  /**
   * Get the best unsigned type for `bits`.
   *
   * \param BITS  Number of bits to cover.
   */
  template<unsigned BITS> struct Best_type
  {
    template< typename TY > struct Cmp { enum { value = (BITS <= sizeof(TY)*8) }; };
    typedef cxx::type_list<
      unsigned char,
      unsigned short,
      unsigned int,
      unsigned long,
      unsigned long long
    > Unsigned_types;
    typedef cxx::find_type_t<Unsigned_types, Cmp> Type;
  };

public:
  enum
  {
    Bits  = MSB + 1 - LSB, ///< Number of bits
    Lsb = LSB,             ///< index of the LSB
    Msb = MSB,             ///< index of the MSB
  };

  /// Masks for bitswise operation on internal parts of a bitfield
  enum Masks : Base_type
  {
    /** Mask value to get #Bits bits. */
    Low_mask = static_cast<Base_type>(~0ULL) >> (sizeof(Base_type)*8 - Bits),
    /** Mask value to the bits out of a `T`. */
    Mask     = Low_mask << Lsb,
  };

  /**
   * Type to hold at least #Bits bits.
   *
   * This type can handle all values that can be stored in this part of the bit
   * field.
   */
  typedef typename Best_type<Bits>::Type Bits_type;

  /**
   * Type to hold at least #Bits + #Lsb bits.
   *
   * This type can handle all values that can be stored in this part of the bit
   * field when they are at the target location (#Lsb bits shifted to the left).
   */
  typedef typename Best_type<Bits + Lsb>::Type Shift_type;

private:
  static_assert(sizeof(Bits_type)*8 >= Bits, "error finding the type to store the bits");
  static_assert(sizeof(Shift_type)*8 >= Bits + Lsb, "error finding the type to keep the shifted bits");
  static_assert(sizeof(Bits_type) <= sizeof(Base_type), "size mismatch for Bits_type");
  static_assert(sizeof(Shift_type) <= sizeof(Base_type), "size mismatch for Shift_type");
  static_assert(sizeof(Bits_type) <= sizeof(Shift_type), "size mismatch for Shift_type and Bits_type");

public:
  /**
   * Get the bits out of `val`.
   *
   * \param val  The raw value of the whole bit field.
   *
   * \return The bits form #Lsb to #Msb shifted to the right.
   */
  static constexpr Bits_type get(Shift_type val)
  { return (val >> Lsb) & Low_mask; }

  /**
   * Get the bits in place out of `val`.
   *
   * \param val  The raw value of the whole bit field.
   *
   * \return The bits from #Lsb to #Msb (unshifted).
   *
   * This means other bits are masked out, however the result is not shifted to
   * the right.
   */
  static constexpr Base_type get_unshifted(Shift_type val)
  { return val & Mask; }

  /**
   * Set the bits corresponding to `val`.
   *
   * \param dest  The current value of the whole bit field.
   * \param val   The value to set into the bits.
   *
   * \return The new value of the whole bit field.
   *
   * \pre `val` must not contain more than #Bits bits.
   *
   * \note This function does not mask `val` to the right number of bits.
   */
  static constexpr Base_type set_dirty(Base_type dest, Shift_type val)
  {
    //assert (!(val & ~Low_mask));
    return (dest & ~Mask) | (val << Lsb);
  }

  /**
   * Set the bits corresponding to `val`.
   *
   * \param dest  The current value of the whole bit field.
   * \param val   The value shifted #Lsb bits to the left that shall be set into
   *              the bits.
   *
   * \return The new value of the whole bit field.
   *
   * \pre `val` must not contain more than #Bits bits shifted #Lsb bits to the
   *      left.
   *
   * \note This function does not mask `val` to the right number of bits.
   */
  static constexpr Base_type set_unshifted_dirty(Base_type dest, Shift_type val)
  {
    //assert (!(val & ~Mask));
    return (dest & ~Mask) | val;
  }

  /**
   * Set the bits corresponding to `val`.
   *
   * \param dest  The current value of the whole bit field.
   * \param val   The value to set into the bits.
   *
   * \return The new value of the whole bit field.
   */
  static Base_type set(Base_type dest, Bits_type val)
  { return set_dirty(dest, val & Low_mask); }

  /**
   * Set the bits corresponding to `val`.
   *
   * \param dest  The current value of the whole bit field.
   * \param val   The value shifted #Lsb bits to the left that shall be set into
   *              the bit field.
   *
   * \return the new value of the whole bit field.
   */
  static Base_type set_unshifted(Base_type dest, Shift_type val)
  { return set_unshifted_dirty(dest, val & Mask); }

  /**
   * Get the shifted bits for `val`.
   *
   * \param val  The value to set into the bits.
   *
   * \return The raw bit field value.
   *
   * \pre `val` must not contain more than #Bits bits.
   *
   * \note This function does not mask `val` to the right number of bits.
   */
  static constexpr Base_type val_dirty(Shift_type val) { return val << Lsb; }

  /**
   * Get the shifted bits for `val`.
   *
   * \param val  The value to set into the bits.
   *
   * \return The raw bit field value.
   */
  static constexpr Base_type val(Bits_type val) { return val_dirty(val & Low_mask); }

  /**
   * Get the shifted bits for `val`.
   *
   * \param val  The value shifted #Lsb bits to the left that shall be set into
   *             the bits.
   *
   * \return The raw bit field value.
   */
  static constexpr Base_type val_unshifted(Shift_type val) { return val & Mask; }

  /** Internal helper type */
  template< typename TT >
  class Value_base
  {
  private:
    TT v;

  public:
    constexpr Value_base(TT t) : v(t) {}
    constexpr Bits_type get() const { return Bitfield::get(v); }
    constexpr Base_type get_unshifted() const { return Bitfield::get_unshifted(v); }

    void set(Bits_type val) { v = Bitfield::set(v, val); }
    void set_dirty(Bits_type val) { v = Bitfield::set_dirty(v, val); }
    void set_unshifted(Shift_type val) { v = Bitfield::set_unshifted(v, val); }
    void set_unshifted_dirty(Shift_type val) { v = Bitfield::set_unshifted_dirty(v, val); }
  };

  /** Internal helper type */
  template< typename TT >
  class Value : public Value_base<TT>
  {
  public:
    constexpr Value(TT t) : Value_base<TT>(t) {}
    constexpr operator Bits_type () const { return this->get(); }
    constexpr Value &operator = (Bits_type val) { this->set(val); return *this; }
    constexpr Value &operator = (Value const &val)
    { this->set(val.get()); return *this; }
    Value(Value const &) = default;
  };

  /** Internal helper type */
  template< typename TT >
  class Value_unshifted : public Value_base<TT>
  {
  public:
    constexpr Value_unshifted(TT t) : Value_base<TT>(t) {}
    constexpr operator Shift_type () const { return this->get_unshifted(); }
    constexpr Value_unshifted &operator = (Shift_type val) { this->set_unshifted(val); return *this; }
    constexpr Value_unshifted &operator = (Value_unshifted const &val)
    { this->set_unshifted(val.get_unshifted()); return *this; }
    Value_unshifted(Value_unshifted const &) = default;
  };

  /** Reference type to access the bits inside a raw bit field. */
  typedef Value<Base_type &> Ref;
  /** Volatile reference type to access the bits inside a raw bit field. */
  typedef Value<Base_type volatile &> Ref_volatile;
  /** Value type to access the bits inside a raw bit field. */
  typedef Value<Base_type const> Val;

  /** Reference type to access the bits inside a raw bit field (in place). */
  typedef Value_unshifted<Base_type &> Ref_unshifted;
  /** Volatile reference type to access the bits inside a raw bit field (in place). */
  typedef Value_unshifted<Base_type volatile &> Ref_unshifted_volatile;
  /** Value type to access the bits inside a raw bit field (in place). */
  typedef Value_unshifted<Base_type const> Val_unshifted;
};

#define CXX_BITFIELD_MEMBER(LSB, MSB, name, data_member) \
  /** @{ */ \
  /** Type to access the `name` bits (LSB to MSB) of `data_member`. */ \
  typedef cxx::Bitfield<decltype(data_member), LSB, MSB> name ## _bfm_t; \
  /** Get the `name` bits (LSB to MSB) of `data_member`. */ \
  constexpr typename name ## _bfm_t::Val name() const { return data_member; } \
  typename name ## _bfm_t::Val name() const volatile { return data_member; } \
  /** Get a reference to the `name` bits (LSB to MSB) of `data_member`. */ \
  constexpr typename name ## _bfm_t::Ref name() { return data_member; } \
  typename name ## _bfm_t::Ref_volatile name() volatile { return data_member; } \
  /** @} */

#define CXX_BITFIELD_MEMBER_RO(LSB, MSB, name, data_member) \
  /** @{ */ \
  /** Type to access the `name` bits (LSB to MSB) of `data_member`. */ \
  typedef cxx::Bitfield<decltype(data_member), LSB, MSB> name ## _bfm_t; \
  /** Get the `name` bits (LSB to MSB) of `data_member`. */ \
  constexpr typename name ## _bfm_t::Val name() const { return data_member; } \
  typename name ## _bfm_t::Val name() const volatile { return data_member; } \
  /** @} */

#define CXX_BITFIELD_MEMBER_UNSHIFTED(LSB, MSB, name, data_member) \
  /** @{ */ \
  /** Type to access the `name` bits (LSB to MSB) of `data_member`. */ \
  typedef cxx::Bitfield<decltype(data_member), LSB, MSB> name ## _bfm_t; \
  /** Get the `name` bits (LSB to MSB) of `data_member`. */ \
  constexpr typename name ## _bfm_t::Val_unshifted name() const { return data_member; } \
  typename name ## _bfm_t::Val_unshifted name() const volatile { return data_member; } \
  /** Get a reference to the `name` bits (LSB to MSB) of `data_member`. */ \
  constexpr typename name ## _bfm_t::Ref_unshifted name() { return data_member; } \
  typename name ## _bfm_t::Ref_unshifted_volatile name() volatile { return data_member; } \
  /** @} */

#define CXX_BITFIELD_MEMBER_UNSHIFTED_RO(LSB, MSB, name, data_member) \
  /** @{ */ \
  /** Type to access the `name` bits (LSB to MSB) of `data_member`. */ \
  typedef cxx::Bitfield<decltype(data_member), LSB, MSB> name ## _bfm_t; \
  /** Get the `name` bits (LSB to MSB) of `data_member`. */ \
  constexpr typename name ## _bfm_t::Val_unshifted name() const { return data_member; } \
  typename name ## _bfm_t::Val_unshifted name() const volatile { return data_member; } \
  /** @} */
}
