// =====================================================================================================================
//  fennec, a free and open source game engine
//  Copyright © 2025 - 2026  Medusa Slockbower
//
//  This program is free software: you can redistribute it and/or modify
//  it under the terms of the GNU General Public License as published by
//  the Free Software Foundation, either version 3 of the License, or
//  (at your option) any later version.
//
//  This program is distributed in the hope that it will be useful,
//  but WITHOUT ANY WARRANTY; without even the implied warranty of
//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//  GNU General Public License for more details.
//
//  You should have received a copy of the GNU General Public License
//  along with this program.  If not, see <https://www.gnu.org/licenses/>.
// =====================================================================================================================

///
/// \file fennec/containers/bitfield.h
/// \brief
///
///
/// \details
/// \author Medusa Slockbower
///
/// \copyright Copyright © 2025 - 2026 Medusa Slockbower ([GPLv3](https://www.gnu.org/licenses/gpl-3.0.en.html))
///
///

#ifndef FENNEC_CONTAINERS_BITFIELD_H
#define FENNEC_CONTAINERS_BITFIELD_H

#include <fennec/lang/types.h>
#include <fennec/lang/utility.h>

#include <fennec/containers/array.h>

namespace fennec
{

///
/// \brief Bitfield Container with basic Bit Ops
///
/// \details
/// | Property    | Value       |
/// |:-----------:|:-----------:|
/// | stable      |      ⛔     |
/// | dynamic     |      ⛔     |
/// | homogeneous |      ✅     |
/// | distinct    |      ⛔     |
/// | ordered     |      ⛔     |
/// | linear      |      ✅     |
/// | space       | \emph{O(N)} |
/// | access      | \emph{O(1)} |
/// | find        | \emph{O(N)} |
/// | insertion   |      ⛔     |
/// | deletion    |      ⛔     |
///
/// \tparam N The number of bits in the bitfield
template<size_t N>
struct bitfield {

// Constants ===========================================================================================================
public:

	/// \name Constants
	/// @{

	static constexpr size_t bits  = N;           //!< The number of bits in the bitfield
	static constexpr size_t bytes = (N + 7) / 8; //!< The number of bytes that hold the bitfield

	/// @}


// Constructors & Destructor ===========================================================================================
public:

	/// \name Constructors & Destructor
	/// @{

	///
	/// \brief Default constructor.
	/// \details Initializes all bits with \emph{0}.
	///
	/// \par Complexity
	/// \emph{O(N)}
	///
	constexpr bitfield()
		: _bytes() {
	}

	///
	/// \brief Boolean array constructor.
	/// \param arr An array of boolean values resembling each bit.
	/// \details Initializes each bit with the respective boolean value in \emph{arr}
	///
	/// \par Complexity
	/// \emph{O(N)}
	///
	explicit constexpr bitfield(const bool (&arr)[N])
		: _bytes() {
		for (size_t i = 0; i < arr; ++i) {
			this->store(i, arr[i]);
		}
	}

	///
	/// \brief Index array constructor.
	/// \param arr An array of indices.
	/// \details Sets the bits of each index provided in \emph{arr}.
	///
	/// \par Complexity
	/// \emph{O(N)}
	///
	template<size_t I>
	explicit constexpr bitfield(const size_t (&arr)[I])
		: _bytes() {
		for (size_t i : arr) {
			this->set(i);
		}
	}

	///
	/// \param args A set of indices.
	/// \details This substitution assumes \emph{ArgsT...} can be taken as an array of indices. <br>
	///          Sets the bits of each index provided in \emph{args...}.
	///
	/// \par Complexity
	/// \emph{O(N)}
	///
	template<typename...ArgsT>
	constexpr bitfield(ArgsT&&...args)
		: _bytes() {
		(this->store(fennec::forward<ArgsT>(args), true), ...);
	}

	///
	/// \brief Variadic array constructor
	/// \param args A set of boolean values.
	/// \details This substitution assumes \emph{ArgsT...} can be taken as an array of booleans. <br>
	///          Initializes each bit with the respective boolean in \emph{args...}. <br>
	///          Does not necessitate the number of arguments be equal to the number of bits.
	///
	/// \par Complexity
	/// \emph{O(N)}
	///
	template<typename...ArgsT> requires((is_bool_v<ArgsT> or is_convertible_v<ArgsT, bool>) and ...)
	constexpr bitfield(ArgsT&&...args)
		: _bytes() {
		size_t i = 0;
		(this->store(i++, fennec::forward<ArgsT>(args)), ...);
	}

	///
	/// \brief copy constructor
	/// \param bf bitfield to copy
	///
	/// \par Complexity
	/// \emph{O(N)}
	///
	bitfield(const bitfield& bf)
		: _bytes(bf._bytes) {
	}

	///
	/// \brief move constructor
	/// \param bf bitfield to move
	///
	/// \par Complexity
	/// \emph{O(1)}
	///
	bitfield(bitfield&& bf) noexcept
		: _bytes(bf._bytes) {
	}

	///
	/// \brief destructor
	constexpr ~bitfield() = default;

	/// @}


// Assignment ==========================================================================================================
public:

	/// \name Assignment Operators
	/// @{

	///
	/// \brief copy assignment
	/// \param bf bitfield to copy
	/// \returns a reference to \emph{this}
	///
	/// \par Complexity
	/// \emph{O(N)}
	///
	bitfield& operator=(const bitfield& bf) = default;

	///
	/// \brief move assignment
	/// \param bf bitfield to move
	/// \returns a reference to \emph{this}
	///
	/// \par Complexity
	/// \emph{O(1)}
	///
	bitfield& operator=(bitfield&& bf) noexcept = default;

	/// @}


// Access & Modifiers ==================================================================================================
public:

	/// \name Access & Modifiers
	/// @{

	///
	/// \brief test a bit
	/// \param i the index of the bit
	/// \returns the value stored in the bit as a boolean
	///
	/// \par Complexity
	/// \emph{O(1)}
	///
	bool test(size_t i) const {
		assertd(i < bits, "Index out of Bounds!");
		size_t b = i / 8;
		const size_t o = i % 8;
		return _bytes[b] & (1 << o);
	}

	///
	/// \brief set a bit
	/// \param i the index of the bit
	///
	/// \par Complexity
	/// \emph{O(1)}
	///
	void set(size_t i) {
		assertd(i < bits, "Index out of Bounds!");
		size_t b = i / 8;
		const size_t o = i % 8;
		_bytes[b] |= (1 << o);
	}

	///
	/// \brief clear a bit
	/// \param i the index of the bit
	///
	/// \par Complexity
	/// \emph{O(1)}
	///
	void clear(size_t i) {
		assertd(i < bits, "Index out of Bounds!");
		size_t b = i / 8;
		const size_t o = i % 8;
		_bytes[b] &= ~(1 << o);
	}

	///
	/// \brief toggle a bit
	/// \param i the index of the bit
	///
	/// \par Complexity
	/// \emph{O(1)}
	///
	void toggle(size_t i) {
		assertd(i < bits, "Index out of Bounds!");
		size_t b = i / 8;
		const size_t o = i % 8;
		_bytes[b] ^= (1 << o);
	}

	///
	/// \brief store \emph{v} in bit \emph{i}
	/// \param i the index of the bit
	/// \param v the value to store
	///
	/// \par Complexity
	/// \emph{O(1)}
	///
	void store(size_t i, bool v) {
		assertd(i < bits, "Index out of Bounds!");
		size_t b = i / 8;
		const size_t o = i % 8;
		(_bytes[b] &= ~((1 << o))) |= ((v << o));
	}

	///
	/// \brief not operator
	/// \returns a bitfield containing the bit-wise inverse
	///
	/// \par Complexity
	/// \emph{O(N)}
	///
	bitfield operator~() const {
		bitfield res = *this;
		res._inv_helper(make_index_metasequence_t<bytes>{});
		return res;
	}

	/// @}


// Private Member Variables ============================================================================================
private:
	array<uint8_t, bytes> _bytes;


// Private Helpers =====================================================================================================
private:
	template<size_t...I>
	void _inv_helper(index_metasequence<I...>) {
		((_bytes[I] = ~_bytes[I]), ...);
	}
};


}

#endif // FENNEC_CONTAINERS_BITFIELD_H