// vi:set ft=cpp: -*- Mode: C++ -*-
/*
 * (c) 2012 Alexander Warg <warg@os.inf.tu-dresden.de>,
 *     economic rights: Technische Universität Dresden (Germany)
 *
 * This file is part of TUD:OS and distributed under the terms of the
 * GNU General Public License 2.
 * Please see the COPYING-GPL-2 file for details.
 *
 * As a special exception, you may use this file as part of a free software
 * library without restriction.  Specifically, if other files instantiate
 * templates or use macros or inline functions from this file, or you compile
 * this file and link it with other files to produce an executable, this
 * file does not by itself cause the resulting executable to be covered by
 * the GNU General Public License.  This exception does not however
 * invalidate any other reasons why the executable file might be covered by
 * the GNU General Public License.
 */

#pragma once

#include "type_list"

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

/** \brief 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 mos significant bit if our bits.
 */
template<typename T, unsigned LSB, unsigned MSB>
class Bitfield
{
private:
  static_assert(MSB >= LSB, "boundary mismatch in bit-field definition");
  static_assert(MSB < sizeof(T) * 8, "MSB outside of bit-field type");
  static_assert(LSB < sizeof(T) * 8, "LSB outside of bit-field type");

  /** \brief Get the best unsigned type for \a 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 typename cxx::find_type<Unsigned_types, Cmp>::type Type;
  };

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

  enum Masks : T
  {
    /** Mask value to get #Bits bits. */
    Low_mask = ((T)~0ULL) >> (sizeof(T)*8 - Bits),
    /** Mask value to the bits out of a \a T. */
    Mask     = Low_mask << Lsb,
  };

  /** \brief 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;

  /** \brief 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(T), "size mismatch for Bits_type");
  static_assert(sizeof(Shift_type) <= sizeof(T), "size mismatch for Shift_type");
  static_assert(sizeof(Bits_type) <= sizeof(Shift_type), "size mismacht for Shift_type and Bits_type");

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

  /** \brief Get the bits in place out of \a 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 T get_unshifted(Shift_type val)
  { return val & Mask; }

  /** \brief Set the bits corresponding to \a 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 \a val must contain not more than bits than #Bits.
   *  \note This function does not mask \a val to the right number of bits.
   */
  static T set_dirty(T dest, Shift_type val)
  {
    //assert (!(val & ~Low_mask));
    return (dest & ~Mask) | (val << Lsb);
  }

  /** \brief Set the bits corresponding to \a 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 \a val must contain not more than bits than #Bits shifted #Lsb bits
   *       to the left.
   *  \note This function does not mask \a val to the right number of bits.
   */
  static T set_unshifted_dirty(T dest, Shift_type val)
  {
    //assert (!(val & ~Mask));
    return (dest & ~Mask) | val;
  }

  /** \brief Set the bits corresponding to \a 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 T set(T dest, Bits_type val)
  { return set_dirty(dest, val & Low_mask); }

  /** \brief Set the bits corresponding to \a 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.
   */
  static T set_unshifted(T dest, Shift_type val)
  { return set_unshifted_dirty(dest, val & Mask); }

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

  /** \brief Get the shifted bits for \a val.
   *  \param val the value to set into the bits.
   *  \return the raw bit field value containing.
   */
  static T val(Bits_type val) { return val_dirty(val & Low_mask); }

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

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

  public:
    Value_base(TT t) : v(t) {}
    Bits_type get() const { return Bitfield::get(v); }
    T 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:
    Value(TT t) : Value_base<TT>(t) {}
    operator Bits_type () const { return this->get(); }
    Value &operator = (Bits_type val) { this->set(val); return *this; }
  };

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

  /** Reference type to access the bits inside a raw bit field. */
  typedef Value<T&> Ref;
  /** Value type to access the bits inside a raw bit field. */
  typedef Value<T const> Val;

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

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

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

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

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