// This file is part of Blend2D project <https://blend2d.com>
//
// See blend2d.h or LICENSE.md for license and copyright information
// SPDX-License-Identifier: Zlib

#ifndef BLEND2D_BITSET_H
#define BLEND2D_BITSET_H

#include "object.h"

//! \addtogroup bl_containers
//! \{

//! \name BLBitSet - Constants
//! \{

BL_DEFINE_ENUM(BLBitSetConstants) {
  //! Invalid bit-index.
  //!
  //! This is the only index that cannot be stored in `BLBitSet`.
  BL_BIT_SET_INVALID_INDEX = 0xFFFFFFFFu,

  //! Range mask used by `BLBitsetSegment::start` value - if set the segment is a range of all ones.
  BL_BIT_SET_RANGE_MASK = 0x80000000u,

  //! Number of words in a BLBitSetSegment.
  BL_BIT_SET_SEGMENT_WORD_COUNT = 4u
};

//! \}

//! \name BLBitSet - Structs
//! \{

//! BitSet segment.
//!
//! Segment provides either a dense set of bits starting at `start` or a range of bits all set to one. The start of
//! the segment is always aligned to segment size, which can be calculated as `32 * BL_BIT_SET_SEGMENT_WORD_COUNT`.
//! Even ranges are aligned to this value, thus up to 3 segments are used to describe a range that doesn't start/end
//! at the segment boundary.
//!
//! When the segment describes dense bits its size is always fixed and represents `32 * BL_BIT_SET_SEGMENT_WORD_COUNT`
//! bits, which is currently 128 bits. However, when the segment describes all ones, the first value in data `data[0]`
//! describes the last bit of the range, which means that an arbitrary range can be encoded within a single segment.
struct BLBitSetSegment {
  uint32_t _startWord;
  uint32_t _data[BL_BIT_SET_SEGMENT_WORD_COUNT];

#ifdef __cplusplus
  BL_INLINE_NODEBUG bool allOnes() const noexcept { return (_startWord & BL_BIT_SET_RANGE_MASK) != 0; }
  BL_INLINE_NODEBUG void clearData() noexcept { memset(_data, 0, sizeof(_data)); }
  BL_INLINE_NODEBUG void fillData() noexcept { memset(_data, 0xFF, sizeof(_data)); }

  BL_INLINE_NODEBUG uint32_t* data() noexcept { return _data; }
  BL_INLINE_NODEBUG const uint32_t* data() const noexcept { return _data; }
  BL_INLINE_NODEBUG uint32_t wordAt(size_t index) const noexcept { return _data[index]; }

  BL_INLINE_NODEBUG uint32_t _rangeStartWord() const noexcept { return _startWord & ~BL_BIT_SET_RANGE_MASK; }
  BL_INLINE_NODEBUG uint32_t _rangeEndWord() const noexcept { return _data[0]; }

  BL_INLINE_NODEBUG uint32_t _denseStartWord() const noexcept { return _startWord; }
  BL_INLINE_NODEBUG uint32_t _denseEndWord() const noexcept { return _startWord + BL_BIT_SET_SEGMENT_WORD_COUNT; }

  BL_INLINE_NODEBUG void _setRangeStartWord(uint32_t index) noexcept { _startWord = index; }
  BL_INLINE_NODEBUG void _setRangeEndWord(uint32_t index) noexcept { _data[0] = index; }

  BL_INLINE_NODEBUG uint32_t startWord() const noexcept { return _startWord & ~BL_BIT_SET_RANGE_MASK; }
  BL_INLINE_NODEBUG uint32_t startSegmentId() const noexcept { return startWord() / BL_BIT_SET_SEGMENT_WORD_COUNT; }
  BL_INLINE_NODEBUG uint32_t startBit() const noexcept { return _startWord * 32u; }

  BL_INLINE uint32_t endWord() const noexcept {
    uint32_t rangeEnd = _rangeEndWord();
    uint32_t denseEnd = _denseEndWord();
    return allOnes() ? rangeEnd : denseEnd;
  }

  BL_INLINE_NODEBUG uint32_t endSegmentId() const noexcept { return endWord() / BL_BIT_SET_SEGMENT_WORD_COUNT; }
  BL_INLINE_NODEBUG uint32_t lastBit() const noexcept { return endWord() * 32u - 1u; }
#endif
};

//! BitSet data view.
struct BLBitSetData {
  const BLBitSetSegment* segmentData;
  uint32_t segmentCount;
  BLBitSetSegment ssoSegments[3];

#ifdef __cplusplus
  BL_INLINE_NODEBUG bool empty() const noexcept {
    return segmentCount == 0;
  }

  BL_INLINE void reset() noexcept {
    segmentData = nullptr;
    segmentCount = 0;
  }
#endif
};

//! \}
//! \}

//! \addtogroup bl_c_api
//! \{

//! \name BLBitSet - C API
//! \{

BL_BEGIN_C_DECLS

BL_API BLResult BL_CDECL blBitSetInit(BLBitSetCore* self) BL_NOEXCEPT_C;
BL_API BLResult BL_CDECL blBitSetInitMove(BLBitSetCore* self, BLBitSetCore* other) BL_NOEXCEPT_C;
BL_API BLResult BL_CDECL blBitSetInitWeak(BLBitSetCore* self, const BLBitSetCore* other) BL_NOEXCEPT_C;
BL_API BLResult BL_CDECL blBitSetInitRange(BLBitSetCore* self, uint32_t startBit, uint32_t endBit) BL_NOEXCEPT_C;
BL_API BLResult BL_CDECL blBitSetDestroy(BLBitSetCore* self) BL_NOEXCEPT_C;
BL_API BLResult BL_CDECL blBitSetReset(BLBitSetCore* self) BL_NOEXCEPT_C;
BL_API BLResult BL_CDECL blBitSetAssignMove(BLBitSetCore* self, BLBitSetCore* other) BL_NOEXCEPT_C;
BL_API BLResult BL_CDECL blBitSetAssignWeak(BLBitSetCore* self, const BLBitSetCore* other) BL_NOEXCEPT_C;
BL_API BLResult BL_CDECL blBitSetAssignDeep(BLBitSetCore* self, const BLBitSetCore* other) BL_NOEXCEPT_C;
BL_API BLResult BL_CDECL blBitSetAssignRange(BLBitSetCore* self, uint32_t startBit, uint32_t endBit) BL_NOEXCEPT_C;
BL_API BLResult BL_CDECL blBitSetAssignWords(BLBitSetCore* self, uint32_t startWord, const uint32_t* wordData, uint32_t wordCount) BL_NOEXCEPT_C;
BL_API bool BL_CDECL blBitSetIsEmpty(const BLBitSetCore* self) BL_NOEXCEPT_C;
BL_API BLResult BL_CDECL blBitSetGetData(const BLBitSetCore* self, BLBitSetData* out) BL_NOEXCEPT_C;
BL_API uint32_t BL_CDECL blBitSetGetSegmentCount(const BLBitSetCore* self) BL_NOEXCEPT_C BL_PURE;
BL_API uint32_t BL_CDECL blBitSetGetSegmentCapacity(const BLBitSetCore* self) BL_NOEXCEPT_C BL_PURE;
BL_API uint32_t BL_CDECL blBitSetGetCardinality(const BLBitSetCore* self) BL_NOEXCEPT_C;
BL_API uint32_t BL_CDECL blBitSetGetCardinalityInRange(const BLBitSetCore* self, uint32_t startBit, uint32_t endBit) BL_NOEXCEPT_C;
BL_API bool BL_CDECL blBitSetHasBit(const BLBitSetCore* self, uint32_t bitIndex) BL_NOEXCEPT_C;
BL_API bool BL_CDECL blBitSetHasBitsInRange(const BLBitSetCore* self, uint32_t startBit, uint32_t endBit) BL_NOEXCEPT_C;
BL_API bool BL_CDECL blBitSetSubsumes(const BLBitSetCore* a, const BLBitSetCore* b) BL_NOEXCEPT_C;
BL_API bool BL_CDECL blBitSetIntersects(const BLBitSetCore* a, const BLBitSetCore* b) BL_NOEXCEPT_C;
BL_API bool BL_CDECL blBitSetGetRange(const BLBitSetCore* self, uint32_t* startOut, uint32_t* endOut) BL_NOEXCEPT_C;
BL_API bool BL_CDECL blBitSetEquals(const BLBitSetCore* a, const BLBitSetCore* b) BL_NOEXCEPT_C;
BL_API int BL_CDECL blBitSetCompare(const BLBitSetCore* a, const BLBitSetCore* b) BL_NOEXCEPT_C;
BL_API BLResult BL_CDECL blBitSetClear(BLBitSetCore* self) BL_NOEXCEPT_C;
BL_API BLResult BL_CDECL blBitSetShrink(BLBitSetCore* self) BL_NOEXCEPT_C;
BL_API BLResult BL_CDECL blBitSetOptimize(BLBitSetCore* self) BL_NOEXCEPT_C;
BL_API BLResult BL_CDECL blBitSetChop(BLBitSetCore* self, uint32_t startBit, uint32_t endBit) BL_NOEXCEPT_C;
BL_API BLResult BL_CDECL blBitSetAddBit(BLBitSetCore* self, uint32_t bitIndex) BL_NOEXCEPT_C;
BL_API BLResult BL_CDECL blBitSetAddRange(BLBitSetCore* self, uint32_t rangeStartBit, uint32_t rangeEndBit) BL_NOEXCEPT_C;
BL_API BLResult BL_CDECL blBitSetAddWords(BLBitSetCore* self, uint32_t startWord, const uint32_t* wordData, uint32_t wordCount) BL_NOEXCEPT_C;
BL_API BLResult BL_CDECL blBitSetClearBit(BLBitSetCore* self, uint32_t bitIndex) BL_NOEXCEPT_C;
BL_API BLResult BL_CDECL blBitSetClearRange(BLBitSetCore* self, uint32_t rangeStartBit, uint32_t rangeEndBit) BL_NOEXCEPT_C;

// TODO: Future API (BitSet).
/*
BL_API BLResult BL_CDECL blBitSetCombine(BLBitSetCore* dst, const BLBitSetCore* a, const BLBitSetCore* b, BLBooleanOp booleanOp) BL_NOEXCEPT_C;
*/

BL_API BLResult BL_CDECL blBitSetBuilderCommit(BLBitSetCore* self, BLBitSetBuilderCore* builder, uint32_t newAreaIndex) BL_NOEXCEPT_C;
BL_API BLResult BL_CDECL blBitSetBuilderAddRange(BLBitSetCore* self, BLBitSetBuilderCore* builder, uint32_t startBit, uint32_t endBit) BL_NOEXCEPT_C;

BL_END_C_DECLS

//! BitSet container [C API].
struct BLBitSetCore BL_CLASS_INHERITS(BLObjectCore) {
  BL_DEFINE_OBJECT_DETAIL
  BL_DEFINE_OBJECT_DCAST(BLBitSet)
};

//! BitSet builder [C API].
struct BLBitSetBuilderCore {
  //! Shift to get `_areaIndex` from bit index, equals to `log2(kBitCount)`.
  uint32_t _areaShift;
  //! Area index - index from 0...N where each index represents `kBitCount` bits.
  uint32_t _areaIndex;

  /*
  //! Area word data.
  uint32_t areaWords[1 << (areaShift - 5)];
  */

#ifdef __cplusplus
  enum : uint32_t {
    kInvalidAreaIndex = 0xFFFFFFFFu
  };

  BL_INLINE_NODEBUG uint32_t* areaWords() noexcept { return reinterpret_cast<uint32_t*>(this + 1); }
  BL_INLINE_NODEBUG const uint32_t* areaWords() const noexcept { return reinterpret_cast<const uint32_t*>(this + 1); }
#endif
};

//! \}

//! \cond INTERNAL
//! \name BLBitSet - Internals
//! \{

//! BitSet container [Impl].
struct BLBitSetImpl BL_CLASS_INHERITS(BLObjectImpl) {
  //! Count of used segments in `segmentData`.
  uint32_t segmentCount;
  //! Count of allocated segments in `segmentData`.
  uint32_t segmentCapacity;

#ifdef __cplusplus
  BL_INLINE_NODEBUG BLBitSetSegment* segmentData() const noexcept { return (BLBitSetSegment*)(this + 1); }
  BL_INLINE_NODEBUG BLBitSetSegment* segmentDataEnd() const noexcept { return segmentData() + segmentCount; }
#endif
};

//! \}
//! \endcond

//! \}

//! \addtogroup bl_containers
//! \{

//! \name BLBitSet - C++ API
//! \{
#ifdef __cplusplus

//! BitSet container [C++ API].
//!
//! BitSet container implements sparse BitSet that consists of segments, where each segment represents either dense
//! range of bits or a range of bits that are all set to one. In addition, the BitSet provides also a SSO mode, in
//! which it's possible to store up to 64 dense bits (2 consecutive BitWords) in the whole addressable range or a
//! range of ones. SSO mode optimizes use cases, in which very small BitSets are needed.
//!
//! The BitSet itself has been optimized for Blend2D use cases, which are the following:
//!
//!   1. Representing character coverage of fonts and unicode text. This use-case requires sparseness and ranges as
//!      some fonts, especially those designed for CJK use, provide thousands of glyphs that have pretty high code
//!      points - using BLBitArray would be very wasteful in this particular case.
class BLBitSet final : public BLBitSetCore {
public:
  //! \cond INTERNAL
  //! \name Internals
  //! \{

  enum : uint32_t {
    //! Number of words that can be used by SSO dense representation (2 words => 64 bits).
    kSSOWordCount = 2,
    kSSODenseSignature = BLObjectInfo::packTypeWithMarker(BL_OBJECT_TYPE_BIT_SET),
    kSSOEmptySignature = BLObjectInfo::packTypeWithMarker(BL_OBJECT_TYPE_BIT_SET) | BL_OBJECT_INFO_R_FLAG
  };

  BL_INLINE_NODEBUG BLBitSetImpl* _impl() const noexcept { return static_cast<BLBitSetImpl*>(_d.impl); }

  BL_INLINE_NODEBUG void _initRangeInternal(uint32_t startBit = 0u, uint32_t endBit = 0u) noexcept {
    _d.initStatic(BLObjectInfo{kSSOEmptySignature});
    _d.u32_data[0] = startBit;
    _d.u32_data[1] = endBit;
  }

  //! \}
  //! \endcond

  //! \name Construction & Destruction
  //! \{

  BL_INLINE BLBitSet() noexcept { _initRangeInternal(); }

  BL_INLINE BLBitSet(BLBitSet&& other) noexcept {
    _d = other._d;
    other._initRangeInternal();
  }

  BL_INLINE BLBitSet(const BLBitSet& other) noexcept { blBitSetInitWeak(this, &other); }

  BL_INLINE BLBitSet(uint32_t startBit, uint32_t endBit) noexcept {
    uint32_t mask = uint32_t(-int32_t(startBit < endBit));
    _initRangeInternal(startBit & mask, endBit & mask);
  }

  //! Destroys the BitSet.
  BL_INLINE ~BLBitSet() noexcept {
    if (BLInternal::objectNeedsCleanup(_d.info.bits))
      blBitSetDestroy(this);
  }

  //! \}

  //! \name Overloaded Operators
  //! \{

  //! Tests whether the BitSet has a content.
  //!
  //! \note This is essentially the opposite of `empty()`.
  BL_INLINE explicit operator bool() const noexcept { return !empty(); }

  //! Move assignment.
  //!
  //! \note The `other` BitSet is reset by move assignment, so its state after the move operation is the same as
  //! a default constructed BitSet.
  BL_INLINE BLBitSet& operator=(BLBitSet&& other) noexcept { blBitSetAssignMove(this, &other); return *this; }
  //! Copy assignment, performs weak copy of the data held by the `other` BitSet.
  BL_INLINE BLBitSet& operator=(const BLBitSet& other) noexcept { blBitSetAssignWeak(this, &other); return *this; }

  BL_INLINE bool operator==(const BLBitSet& other) const noexcept { return  equals(other); }
  BL_INLINE bool operator!=(const BLBitSet& other) const noexcept { return !equals(other); }
  BL_INLINE bool operator<(const BLBitSet& other) const noexcept { return compare(other) < 0; }
  BL_INLINE bool operator<=(const BLBitSet& other) const noexcept { return compare(other) <= 0; }
  BL_INLINE bool operator>(const BLBitSet& other) const noexcept { return compare(other) > 0; }
  BL_INLINE bool operator>=(const BLBitSet& other) const noexcept { return compare(other) >= 0; }

  //! \}

  //! \name Common Functionality
  //! \{

  //! Clears the content of the BitSet and releases its data.
  //!
  //! After reset the BitSet content matches a default constructed instance.
  BL_INLINE BLResult reset() noexcept { return blBitSetReset(this); }

  //! Swaps the content of this string with the `other` string.
  BL_INLINE void swap(BLBitSetCore& other) noexcept { _d.swap(other._d); }

  //! \name Accessors
  //! \{

  //! Tests whether the BitSet is empty (has no content).
  //!
  //! Returns `true` if the BitSet's size is zero.
  BL_INLINE bool empty() const noexcept { return blBitSetIsEmpty(this); }

  //! Returns the number of segments this BitSet occupies.
  //!
  //! \note If the BitSet is in SSO mode then the returned value is the number of segments the BitSet would occupy
  //! when the BitSet was converted to dynamic.
  BL_INLINE uint32_t segmentCount() const noexcept { return blBitSetGetSegmentCount(this); }

  //! Returns the number of segments this BitSet has allocated.
  //!
  //! \note If the BitSet is in SSO mode the returned value is always zero.
  BL_INLINE uint32_t segmentCapacity() const noexcept { return _d.sso() ? 0 : _impl()->segmentCapacity; }

  //! Returns the range of the BitSet as `[startOut, endOut)`.
  //!
  //! Returns true if the query was successful, false if the BitSet is empty.
  BL_INLINE bool getRange(uint32_t* startOut, uint32_t* endOut) const noexcept { return blBitSetGetRange(this, startOut, endOut); }

  //! Returns the number of bits set in the BitSet.
  BL_INLINE uint32_t cardinality() const noexcept { return blBitSetGetCardinality(this); }

  //! Returns the number of bits set in the given `[startBit, endBit)` range.
  BL_INLINE uint32_t cardinalityInRange(uint32_t startBit, uint32_t endBit) const noexcept { return blBitSetGetCardinalityInRange(this, startBit, endBit); }

  //! Stores a normalized BitSet data represented as segments into `out`.
  //!
  //! If the BitSet is in SSO mode, it will be converter to temporary segments provided by `BLBitSetData::ssoSegments`,
  //! if the BitSet is in dynamic mode (already contains segments) then only a pointer to the data will be stored into
  //! `out`.
  //!
  //! \remarks The data written into `out` can reference the data in the BitSet, thus the BitSet cannot be manipulated
  //! during the use of the data. This function is ideal for inspecting the content of the BitSet in a unique way and
  //! for implementing iterators that don't have to be aware of how SSO data is represented and used.
  BL_INLINE BLResult getData(BLBitSetData* out) const noexcept { return blBitSetGetData(this, out); }

  //! \}

  //! \name Test Operations
  //! \{

  //! Returns a bit-value at the given `bitIndex`.
  BL_INLINE bool hasBit(uint32_t bitIndex) const noexcept { return blBitSetHasBit(this, bitIndex); }
  //! Returns whether the bit-set has at least on bit in the given range `[start:end)`.
  BL_INLINE bool hasBitsInRange(uint32_t startBit, uint32_t endBit) const noexcept { return blBitSetHasBitsInRange(this, startBit, endBit); }

  //! Returns whether this BitSet subsumes `other`.
  BL_INLINE bool subsumes(const BLBitSetCore& other) const noexcept { return blBitSetSubsumes(this, &other); }
  //! Returns whether this BitSet intersects with `other`.
  BL_INLINE bool intersects(const BLBitSetCore& other) const noexcept { return blBitSetIntersects(this, &other); }

  //! \}

  //! \name Equality & Comparison
  //! \{

  //! Returns whether this BitSet and `other` are bitwise equal.
  BL_INLINE bool equals(const BLBitSetCore& other) const noexcept { return blBitSetEquals(this, &other); }
  //! Compares this BitSet with `other` and returns either `-1`, `0`, or `1`.
  BL_INLINE int compare(const BLBitSetCore& other) const noexcept { return blBitSetCompare(this, &other); }

  //! \}

  //! \name Content Manipulation
  //! \{

  //! Move assignment, the same as `operator=`, but returns a `BLResult` instead of `this`.
  BL_INLINE BLResult assign(BLBitSetCore&& other) noexcept { return blBitSetAssignMove(this, &other); }

  //! Copy assignment, the same as `operator=`, but returns a `BLResult` instead of `this`.
  BL_INLINE BLResult assign(const BLBitSetCore& other) noexcept { return blBitSetAssignWeak(this, &other); }

  //! Copy assignment, but creates a deep copy of the `other` BitSet instead of weak copy.
  BL_INLINE BLResult assignDeep(const BLBitSetCore& other) noexcept { return blBitSetAssignDeep(this, &other); }

  //! Replaces the content of the BitSet by the given range.
  BL_INLINE BLResult assignRange(uint32_t startBit, uint32_t endBit) noexcept { return blBitSetAssignRange(this, startBit, endBit); }

  //! Replaces the content of the BitSet by bits specified by `wordData` of size `wordCount` [the size is in uint32_t units].
  BL_INLINE BLResult assignWords(uint32_t startWord, const uint32_t* wordData, uint32_t wordCount) noexcept { return blBitSetAssignWords(this, startWord, wordData, wordCount); }

  //! Clears the content of the BitSet without releasing its dynamically allocated data, if possible.
  BL_INLINE BLResult clear() noexcept { return blBitSetClear(this); }

  //! Shrinks the capacity of the BitSet to match the actual content.
  BL_INLINE BLResult shrink() noexcept { return blBitSetShrink(this); }

  //! Optimizes the BitSet by clearing unused pages and by merging continuous pages, without reallocating the BitSet.
  //! This functions should always return `BL_SUCCESS`.
  BL_INLINE BLResult optimize() noexcept { return blBitSetOptimize(this); }

  //! Bounds the BitSet to the given interval `[start:end)`.
  BL_INLINE BLResult chop(uint32_t startBit, uint32_t endBit) noexcept { return blBitSetChop(this, startBit, endBit); }
  //! Truncates the BitSet so it's maximum bit set is less than `n`.
  BL_INLINE BLResult truncate(uint32_t n) noexcept { return blBitSetChop(this, 0, n); }

  //! Adds a bit to the BitSet at the given `index`.
  BL_INLINE BLResult addBit(uint32_t bitIndex) noexcept { return blBitSetAddBit(this, bitIndex); }
  //! Adds a range of bits `[rangeStartBit:rangeEndBit)` to the BitSet.
  BL_INLINE BLResult addRange(uint32_t rangeStartBit, uint32_t rangeEndBit) noexcept { return blBitSetAddRange(this, rangeStartBit, rangeEndBit); }
  //! Adds a dense data to the BitSet starting a bit index `start`.
  BL_INLINE BLResult addWords(uint32_t startWord, const uint32_t* wordData, uint32_t wordCount) noexcept { return blBitSetAddWords(this, startWord, wordData, wordCount); }

  //! Clears a bit in the BitSet at the given `index`.
  BL_INLINE BLResult clearBit(uint32_t bitIndex) noexcept { return blBitSetClearBit(this, bitIndex); }
  //! Clears a range of bits `[rangeStartBit:rangeEndBit)` in the BitSet.
  BL_INLINE BLResult clearRange(uint32_t rangeStartBit, uint32_t rangeEndBit) noexcept { return blBitSetClearRange(this, rangeStartBit, rangeEndBit); }

  /*
  // TODO: Future API (BitSet).

  BL_INLINE BLResult and_(const BLBitSetCore& other) noexcept { return blBitSetCombine(this, this, &other, BL_BOOLEAN_OP_AND); }
  BL_INLINE BLResult or_(const BLBitSetCore& other) noexcept { return blBitSetCombine(this, this, &other, BL_BOOLEAN_OP_OR); }
  BL_INLINE BLResult xor_(const BLBitSetCore& other) noexcept { return blBitSetCombine(this, this, &other, BL_BOOLEAN_OP_XOR); }
  BL_INLINE BLResult andNot(const BLBitSetCore& other) noexcept { return blBitSetCombine(this, this, &other, BL_BOOLEAN_OP_AND_NOT); }
  BL_INLINE BLResult notAnd(const BLBitSetCore& other) noexcept { return blBitSetCombine(this, this, &other, BL_BOOLEAN_OP_NOT_AND); }
  BL_INLINE BLResult combine(const BLBitSetCore& other, BLBooleanOp booleanOp) noexcept { return blBitSetCombine(this, this, &other, booleanOp); }

  static BL_INLINE BLResult and_(BLBitSetCore& dst, const BLBitSetCore& a, const BLBitSetCore& b) noexcept { return blBitSetCombine(&dst, &a, &b, BL_BOOLEAN_OP_AND); }
  static BL_INLINE BLResult or_(BLBitSetCore& dst, const BLBitSetCore& a, const BLBitSetCore& b) noexcept { return blBitSetCombine(&dst, &a, &b, BL_BOOLEAN_OP_OR); }
  static BL_INLINE BLResult xor_(BLBitSetCore& dst, const BLBitSetCore& a, const BLBitSetCore& b) noexcept { return blBitSetCombine(&dst, &a, &b, BL_BOOLEAN_OP_XOR); }
  static BL_INLINE BLResult andNot(BLBitSetCore& dst, const BLBitSetCore& a, const BLBitSetCore& b) noexcept { return blBitSetCombine(&dst, &a, &b, BL_BOOLEAN_OP_AND_NOT); }
  static BL_INLINE BLResult notAnd(BLBitSetCore& dst, const BLBitSetCore& a, const BLBitSetCore& b) noexcept { return blBitSetCombine(&dst, &a, &b, BL_BOOLEAN_OP_NOT_AND); }
  static BL_INLINE BLResult combine(BLBitSetCore& dst, const BLBitSetCore& a, const BLBitSetCore& b, BLBooleanOp booleanOp) noexcept { return blBitSetCombine(&dst, &a, &b, booleanOp); }
  */

  //! \}
};

//! BitSet builder [C++ API].
//!
//! BitSet builder is a low-level utility class that can be used to efficiently build a BitSet in C++. It maintains
//! a configurable buffer (called area) where intermediate bits are set, which are then committed to BitSet when
//! an added bit/range is outside of the area or when user is done with BitSet building. The commit uses \ref
//! blBitSetBuilderCommit() function, which was specifically designed for `BLBitSetBuilderT<BitCount>` in addition
//! to the `BLBitSetBuilder` alias.
//!
//! \note The destructor doesn't do anything. If there are still bits to be committed, they will be lost.
template<uint32_t BitCount>
class BLBitSetBuilderT final : public BLBitSetBuilderCore {
public:
  static_assert(BitCount >= 128, "BitCount must be at least 128");
  static_assert((BitCount & (BitCount - 1)) == 0, "BitCount must be a power of 2");

  //! \name Constants
  //! \{

  enum : uint32_t {
    kAreaBitCount = BitCount,
    kAreaWordCount = kAreaBitCount / uint32_t(sizeof(uint32_t) * 8u),
    kAreaShift = BLInternal::ConstCTZ<kAreaBitCount>::kValue
  };

  //! \}

  //! \name Members
  //! \{

  //! Area words data.
  uint32_t _areaWords[kAreaWordCount];

  //! BitSet we are building.
  //!
  //! \note This member is not part of C API. C API requires both BLBitSetCore and BLBitSetBuilderCore to be passed.
  BLBitSetCore* _bitSet;

  //! \}

  //! \name Construction & Destruction
  //! \{

  //! Constructs a new BitSet builder having no BitSet assigned.
  BL_INLINE BLBitSetBuilderT() noexcept {
    _bitSet = nullptr;
    _areaShift = kAreaShift;
    _areaIndex = kInvalidAreaIndex;
  }

  //! Constructs a new BitSet builder having the given `bitSet` assigned.
  //!
  //! \note The builder only stores a pointer to the `bitSet` - the user must guarantee to not destroy the BitSet
  //! before the builder is destroyed or reset.
  BL_INLINE explicit BLBitSetBuilderT(BLBitSetCore* bitSet) noexcept {
    _bitSet = bitSet;
    _areaShift = kAreaShift;
    _areaIndex = kInvalidAreaIndex;
  }

  BL_INLINE BLBitSetBuilderT(const BLBitSetBuilderT&) = delete;
  BL_INLINE BLBitSetBuilderT& operator=(const BLBitSetBuilderT&) = delete;

  //! \}

  //! \name Accessors
  //! \{

  //! Returns whether the BitSet builder is valid, which means that is has an associated `BLBitSet` instance.
  BL_INLINE bool isValid() const noexcept { return _bitSet != nullptr; }
  //! Returns an associated `BLBitSet` instance that this builder commits to.
  BL_INLINE BLBitSet* bitSet() const noexcept { return static_cast<BLBitSet*>(_bitSet); }

  //! \}

  //! \name Builder Interface
  //! \{

  BL_INLINE BLResult reset() noexcept {
    _bitSet = nullptr;
    _areaShift = kAreaShift;
    _areaIndex = kInvalidAreaIndex;
    return BL_SUCCESS;
  }

  BL_INLINE BLResult reset(BLBitSetCore* bitSet) noexcept {
    _bitSet = bitSet;
    _areaShift = kAreaShift;
    _areaIndex = kInvalidAreaIndex;
    return BL_SUCCESS;
  }

  //! Adds a bit to the area maintained by BitSet builder.
  //!
  //! If the area of `bitIndex` is different compared to the current active area, the current area will be
  //! committed to the BitSet. This is actually the only operation that can return \ref BL_ERROR_OUT_OF_MEMORY.
  BL_INLINE BLResult addBit(uint32_t bitIndex) noexcept {
    uint32_t areaIndex = bitIndex / kAreaBitCount;
    if (_areaIndex != areaIndex)
      BL_PROPAGATE(blBitSetBuilderCommit(_bitSet, this, areaIndex));

    bitIndex &= kAreaBitCount - 1;
    _areaWords[bitIndex / 32u] |= uint32_t(0x80000000u) >> (bitIndex % 32u);
    return BL_SUCCESS;
  }

  //! Adds a `[startBit, endBit)` range of bits to the BitSet.
  //!
  //! If the range is relatively small and fits into a single builder area, it will be added to that area.
  //! On the other hand, if the range is large, the area will be kept and the builder would call \ref
  //! BLBitSet::addRange() instead. If the are of the range is different compared to the current active area,
  //! the data in the current active area will be committed.
  BL_INLINE BLResult addRange(uint32_t startBit, uint32_t endBit) noexcept {
    return blBitSetBuilderAddRange(_bitSet, this, startBit, endBit);
  }

  //! Commits changes in the current active area to the BitSet.
  //!
  //! \note This must be called in order to finalize building the BitSet. If this function is not called the
  //! BitSet could have missing bits that are in the current active area.
  BL_INLINE BLResult commit() noexcept {
    return blBitSetBuilderCommit(_bitSet, this, kInvalidAreaIndex);
  }

  //! Similar to \ref commit(), but the additional parameter `newAreaIndex` will be used to set the current
  //! active area.
  BL_INLINE BLResult commit(uint32_t newAreaIndex) noexcept {
    return blBitSetBuilderCommit(_bitSet, this, newAreaIndex);
  }

  //! \}
};

//! BitSet builder [C++ API] that is configured to have a temporary storage of 512 bits.
using BLBitSetBuilder = BLBitSetBuilderT<512>;

//! BitSet word iterator [C++ API].
//!
//! Low-level iterator that sees a BitSet as an array of bit words. It only iterates non-zero words and
//! returns zero at the end of iteration.
//!
//! A simple way of printing all non-zero words of a BitWord:
//!
//! ```
//! BLBitSet set;
//! set.addRange(100, 200);
//!
//! BLBitSetWordIterator it(set);
//! while (uint32_t bits = it.nextWord()) {
//!   printf("{WordIndex: %u, WordData: %08X }\n", it.wordIndex(), bits);
//! }
//! ```
class BLBitSetWordIterator final {
public:
  //! \name Members
  //! \{

  const BLBitSetSegment* _segmentPtr;
  const BLBitSetSegment* _segmentEnd;
  BLBitSetData _data;
  uint32_t _wordIndex;

  //! \}

  //! \name Construction & Destruction
  //! \{

  //! Creates a default constructed iterator, not initialized to iterate any BitSet.
  BL_INLINE BLBitSetWordIterator() noexcept { reset(); }
  //! Creates an iterator, that will iterate the given `bitSet`.
  //!
  //! \note The `bitSet` cannot change or be destroyed during iteration.
  BL_INLINE BLBitSetWordIterator(const BLBitSetCore& bitSet) noexcept { reset(bitSet); }
  //! Creates a copy of `other` iterator.
  BL_INLINE BLBitSetWordIterator(const BLBitSetWordIterator& other) noexcept = default;

  //! \}

  //! \name Overloaded Operators
  //! \{

  BL_INLINE BLBitSetWordIterator& operator=(const BLBitSetWordIterator& other) noexcept = default;

  //! \}

  //! \name Reset
  //! \{

  //! Resets the iterator (puts it into a default constructed state).
  BL_INLINE void reset() noexcept {
    _segmentPtr = nullptr;
    _segmentEnd = nullptr;
    _data.reset();
    _wordIndex = 0;
  }

  //! Reinitializes the iterator to iterate the given `bitSet`, from the beginning.
  BL_INLINE void reset(const BLBitSetCore& bitSet) noexcept {
    blBitSetGetData(&bitSet, &_data);
    _segmentPtr = _data.segmentData;
    _segmentEnd = _data.segmentData + _data.segmentCount;
    _wordIndex = _segmentPtr != _segmentEnd ? uint32_t(_segmentPtr->startWord() - 1u) : uint32_t(0xFFFFFFFFu);
  }

  //! \}

  //! \name Iterator Interface
  //! \{

  //! Returns the next (or the first, if called the first time) non-zero word of the BitSet or zero if the
  //! iteration ended.
  //!
  //! Use `wordIndex()` to get the index (in word units) of the word returned.
  BL_INLINE uint32_t nextWord() noexcept {
    if (_segmentPtr == _segmentEnd)
      return 0;

    _wordIndex++;
    for (;;) {
      if (_segmentPtr->allOnes()) {
        if (_wordIndex < _segmentPtr->_rangeEndWord())
          return 0xFFFFFFFFu;
      }
      else {
        uint32_t endWord = _segmentPtr->_denseEndWord();
        while (_wordIndex < endWord) {
          uint32_t bits = _segmentPtr->_data[_wordIndex & (BL_BIT_SET_SEGMENT_WORD_COUNT - 1u)];
          if (bits != 0u)
            return bits;
          _wordIndex++;
        }
      }

      if (++_segmentPtr == _segmentEnd)
        return 0;
      _wordIndex = _segmentPtr->startWord();
    }
  }

  //! Returns the current bit index of a word returned by `nextWord()`.
  BL_INLINE uint32_t bitIndex() const noexcept { return _wordIndex * 32u; }

  //! Returns the current word index of a word returned by `nextWord()`.
  BL_INLINE uint32_t wordIndex() const noexcept { return _wordIndex; }

  //! \}
};

#endif
//! \}

//! \}

#endif // BLEND2D_BITSET_H
