/**
 * @file Bitset.h
 * @author apio (cloudapio.eu)
 * @brief A wrapper class to modify/inspect specific bits of an integer type.
 *
 * @copyright Copyright (c) 2023, the Luna authors.
 *
 */

#pragma once
#include <luna/Check.h>

/**
 * @brief A wrapper class to modify/inspect specific bits of an integer type, to avoid manual error-prone bit shifting.
 *
 * @tparam T The integer type to use.
 */
template <typename T> class Bitset
{
  public:
    /**
     * @brief Construct a new empty Bitset object.
     */
    Bitset() : m_value(0)
    {
    }

    /**
     * @brief Construct a new Bitset object with a value.
     *
     * @param value The value to use.
     */
    Bitset(T value) : m_value(value)
    {
    }

    /**
     * @brief Construct a copy of another Bitset object.
     *
     * @param other The original object.
     */
    Bitset(const Bitset<T>& other) : m_value(other.m_value)
    {
    }

    /**
     * @brief Update the internal value of this bitset.
     *
     * @param value The new value.
     * @return Bitset<T>& A reference to this itset.
     */
    Bitset<T>& operator=(T value)
    {
        m_value = value;
        return *this;
    }

    /**
     * @brief Set this bitset to the same value as another bitset.
     *
     * @param other The pther bitset.
     * @return Bitset<T>& A reference to this bitset.
     */
    Bitset<T>& operator=(const Bitset<T>& other)
    {
        if (&other == this) return *this;
        m_value = other.m_value;
        return *this;
    }

    /**
     * @brief Return the value contained in this bitset.
     *
     * @return T The contained value.
     */
    T value() const
    {
        return m_value;
    }

    /**
     * @brief Modify one of the individual bits in this bitset.
     *
     * @param index The index of the bit (this will set/unset 1 << index). It must be less than the bit width of the
     * underlying type.
     * @param b The value to set the bit to.
     */
    void set(int index, bool b)
    {
        check((usize)index < (sizeof(T) * 8));
        if (b) m_value |= (((T)1) << index);
        else
            m_value &= ~(((T)1) << index);
    }

    /**
     * @brief Get the value of one of the individual bits in this bitset.
     *
     * @param index The index of the bit (this will read 1 << index). It must be less than the bit width of the
     * underlying type.
     * @return bool The value of the requested bit.
     */
    bool get(int index)
    {
        check((usize)index < (sizeof(T) * 8));
        return m_value & (((T)1) << index);
    }

    /**
     * @brief Clear all the bits in this bitset.
     */
    void clear()
    {
        m_value = 0;
    }

  private:
    T m_value;
};