use codec::{Decode, Encode};
use num_traits::PrimInt;
use scale_info::TypeInfo;

use sp_arithmetic::traits::AtLeast8BitUnsigned;
use sp_runtime::{traits::UniqueSaturatedInto, RuntimeDebug};
use sp_std::{cmp::{min, max}, marker::PhantomData, vec, vec::Vec};

use ghost_traits::bounded_bitmap as traits;

use frame_support::{pallet_prelude::MaxEncodedLen, traits::Get, BoundedVec};

use core::ops::{BitAnd, BitAndAssign, BitOr, BitOrAssign, BitXor, BitXorAssign, Not, Shl, Shr};

const fn inner_bits<Inner>() -> usize {
    core::mem::size_of::<Inner>() * 8
}

const fn inner_mask<Inner>() -> usize {
    let bits_per_element = inner_bits::<Inner>();
    bits_per_element - 1
}

const fn inner_shift<Inner>() -> u32 {
    let bits_per_element = inner_bits::<Inner>();
    bits_per_element.trailing_zeros()
}

pub fn validate_bitmap_sizes<B: traits::BoundedBitmapMetadata>(authorities: u32) {
    assert!(
        B::total_bits_capacity() >= authorities,
        "CRITICAL CONFIG ERROR: max authorities does not fit into max chunks!",
    );
}

#[derive(Encode, Decode, RuntimeDebug, TypeInfo, MaxEncodedLen)]
#[scale_info(skip_type_params(MaxChunks))]
pub struct BoundedBitmap<Inner, MaxChunks>
where
    MaxChunks: Get<u32>,
{
    data: BoundedVec<Inner, MaxChunks>,
    active_bits_count: u32,
}

impl<Inner, MaxChunks> BoundedBitmap<Inner, MaxChunks>
where
    MaxChunks: Get<u32>,
    Inner: AtLeast8BitUnsigned + PrimInt + Copy + core::fmt::Debug,
    Inner: BitAnd<Output = Inner>
        + BitOr<Output = Inner>
        + BitXor<Output = Inner>
        + Not<Output = Inner>,
    Inner: BitAndAssign + BitOrAssign + BitXorAssign,
    Inner: Shl<usize, Output = Inner> + Shr<usize, Output = Inner>,
{
    fn combine<F>(&self, rhs: &Self, op: F) -> Self
    where
        Inner: Copy,
        F: Fn(Inner, Inner) -> Inner,
    {
        let active_bits_count = max(self.active_bits_count, rhs.active_bits_count);
        let bounded_len = min(MaxChunks::get() as usize, max(self.data.len(), rhs.data.len()));

        let mut raw_vec = vec![Inner::zero(); bounded_len];

        for index in 0..bounded_len {
            let a = self.data.get(index).copied().unwrap_or_else(Inner::zero);
            let b = rhs.data.get(index).copied().unwrap_or_else(Inner::zero);
            raw_vec[index] = op(a, b);
        }

        let active_bits_count_usize: usize = active_bits_count.unique_saturated_into();

        let target_chunk = active_bits_count_usize >> inner_shift::<Inner>();
        let reminder = active_bits_count_usize & inner_mask::<Inner>();

        if reminder > 0 {
            if target_chunk < raw_vec.len() {
                let mask_u64 = (Inner::one() << reminder) - Inner::one();
                let masked: Inner = mask_u64.unique_saturated_into();
                raw_vec[target_chunk] &= masked;
            }

            let after_target_chunk = target_chunk.saturating_add(1);
            if after_target_chunk < raw_vec.len() {
                raw_vec[after_target_chunk..].fill(Inner::zero());
            }
        }

        Self {
            active_bits_count,
            data: BoundedVec::<Inner, MaxChunks>::try_from(raw_vec)
                .expect("Should never happen because bounded_len caps at MaxChunks"),
        }
    }

    fn empty<N>(n: N) -> Self
    where
        N: UniqueSaturatedInto<usize>,
    {
        let mut n: usize = n.unique_saturated_into();

        let max_chunks: usize = MaxChunks::get().unique_saturated_into();
        let max_bits = max_chunks.saturating_mul(inner_bits::<Inner>());
        if n > max_bits {
            n = max_bits;
        }

        let reminder = n & inner_mask::<Inner>();
        let elements = n >> inner_shift::<Inner>();

        let total_size = if reminder > 0 {
            elements.saturating_add(1)
        } else {
            elements
        };

        let raw_vec = vec![Inner::zero(); min(total_size, MaxChunks::get() as usize)];
        let bounded_data = BoundedVec::<Inner, MaxChunks>::try_from(raw_vec)
            .expect("Should never happen because bounded_len caps at MaxChunks");

        Self {
            data: bounded_data,
            active_bits_count: n.unique_saturated_into(),
        }
    }

    fn empty_from(bitmap: &Self) -> Self {
        Self::empty(bitmap.active_bits_count())
    }

    fn from_vec<Outer>(values: &Vec<Outer>) -> Self
    where
        Outer: AtLeast8BitUnsigned + TryInto<usize> + PartialEq + Copy,
    {
        let value_bits = inner_bits::<Outer>();
        let n = values.len().saturating_mul(value_bits);

        let mut new_bitmap = Self::empty(n);

        for value in values.iter() {
            new_bitmap.insert(*value);
        }

        new_bitmap
    }

    fn from_bitmask<Outer>(values: &Vec<Outer>) -> Self
    where
        Outer: AtLeast8BitUnsigned + PrimInt,
    {
        let value_bits = inner_bits::<Outer>();
        let n = values.iter().enumerate().rev()
            .find(|(_, &val)| val != Outer::zero())
            .map(|(idx, &val)| {
                let active_bits = value_bits - (val.leading_zeros() as usize);
                idx.saturating_mul(value_bits).saturating_add(active_bits)
            })
            .unwrap_or(0);

        let mut new_bitmap = Self::empty(n);

        for (value_index, value) in values.iter().enumerate() {
            for bit_index in 0..value_bits {
                let bit_mask = Outer::one() << bit_index;
                if (*value & bit_mask) == Outer::zero() {
                    continue;
                }

                let index = value_index
                    .saturating_mul(value_bits)
                    .saturating_add(bit_index);

                if let None = new_bitmap.insert(index as u32) {
                    return new_bitmap;
                }
            }
        }

        new_bitmap
    }

    fn all_ones<N>(n: N) -> Self
    where
        N: UniqueSaturatedInto<usize>,
    {
        let n: usize = n.unique_saturated_into();
        let mut new_bitmap = Self::empty(n);
        let reminder = n & inner_mask::<Inner>();

        new_bitmap
            .data
            .iter_mut()
            .rev()
            .skip(if reminder > 0 { 1 } else { 0 })
            .for_each(|inner| *inner = !Inner::zero());

        if reminder > 0 {
            if let Some(inner) = new_bitmap.data.iter_mut().last() {
                let mask_u64 = (1u64 << reminder) - 1;
                let masked: Inner = mask_u64.unique_saturated_into();
                *inner = masked;
            }
        }

        new_bitmap
    }

    fn nullify(&mut self) {
        self.data
            .iter_mut()
            .for_each(|inner| *inner = Inner::zero());
    }

    fn truncate(&mut self) {
        let Some(highest_bit) = self.highest_bit::<usize>() else {
            self.active_bits_count = 0;
            return;
        };

        let element_index: usize = highest_bit >> inner_shift::<Inner>();
        let required_elements_count = element_index.saturating_add(1);
        let new_highest_bit = highest_bit.saturating_add(1);
        let reminder = new_highest_bit & inner_mask::<Inner>();

        self.data.truncate(required_elements_count);

        if reminder > 0 {
            if let Some(target_element) = self.data.get_mut(element_index) {
                let mask_u64 = (1u64 << reminder) - 1;
                let masked: Inner = mask_u64.unique_saturated_into();
                *target_element &= masked;
            }
        }

        self.active_bits_count = new_highest_bit as u32;
    }

    fn insert<N: Copy + TryInto<usize>>(&mut self, i: N) -> Option<N> {
        let index: usize = i.try_into().ok()?;

        if index >= self.active_bits_count as usize {
            return None;
        }

        let element_index = index >> inner_shift::<Inner>();
        let bit_shift = index & inner_mask::<Inner>();

        self.data.get_mut(element_index).map(|element| {
            *element |= Inner::one() << bit_shift;
            i
        })
    }

    fn remove<N: Copy + TryInto<usize>>(&mut self, i: N) -> Option<N> {
        let index: usize = i.try_into().ok()?;

        if index >= self.active_bits_count as usize {
            return None;
        }

        let element_index = (index >> inner_shift::<Inner>()) as usize;
        let bit_shift = index & inner_mask::<Inner>();

        self.data.get_mut(element_index).map(|element| {
            *element &= !(Inner::one() << bit_shift);
            i
        })
    }

    fn active_bits_count(&self) -> u32 {
        self.active_bits_count
    }

    fn highest_bit<N>(&self) -> Option<N>
    where
        usize: UniqueSaturatedInto<N>,
    {
        let shift = inner_shift::<Inner>();

        self.data
            .iter()
            .enumerate()
            .rev()
            .find_map(|(element_index, &element)| {
                if element.is_zero() {
                    return None;
                }

                let mut temp = element;
                let mut bit_index = 0;

                loop {
                    temp = temp >> 1u32;
                    if temp.is_zero() {
                        break;
                    }
                    bit_index += 1;
                }

                let bit_offset = element_index.checked_shl(shift).unwrap_or(usize::MAX);

                let index = bit_offset.saturating_add(bit_index);

                Some(index.unique_saturated_into())
            })
    }

    fn count_ones<N>(&self) -> N
    where
        u32: UniqueSaturatedInto<N>,
    {
        let ones = self
            .data
            .iter()
            .map(|&element| element.count_ones())
            .sum::<u32>();

        ones.unique_saturated_into()
    }

    fn count_zeros<N>(&self) -> N
    where
        u32: UniqueSaturatedInto<N>,
    {
        let zeros = self
            .active_bits_count
            .saturating_sub(self.count_ones::<u32>());

        zeros.unique_saturated_into()
    }

    fn is_empty(&self) -> bool {
        self.data.iter().all(|inner| *inner == Inner::zero())
    }

    fn contains<N>(&self, index: N) -> bool
    where
        N: UniqueSaturatedInto<usize>,
    {
        let index: usize = index.unique_saturated_into();

        if index >= self.active_bits_count as usize {
            return false;
        }

        let element_index = (index >> inner_shift::<Inner>()) as usize;
        let bit_shift = index & inner_mask::<Inner>();

        self.data
            .get(element_index)
            .map(|element| {
                let mask = Inner::one() << bit_shift;
                (*element & mask) != Inner::zero()
            })
            .unwrap_or(false)
    }
}

impl<Inner, MaxChunks> traits::BoundedBitmapMetadata for BoundedBitmap<Inner, MaxChunks>
where
    MaxChunks: Get<u32>,
    Inner: AtLeast8BitUnsigned + PrimInt + Copy + core::fmt::Debug,
{
    fn total_bits_capacity() -> u32 {
        let inner_bits = inner_bits::<Inner>() as u32;
        MaxChunks::get().saturating_mul(inner_bits)
    }

    fn chunk_size() -> u32 {
        inner_bits::<Inner>() as u32
    }
}

impl<Inner, MaxChunks> traits::BoundedBitmapWriter for BoundedBitmap<Inner, MaxChunks>
where
    MaxChunks: Get<u32>,
    Inner: AtLeast8BitUnsigned + PrimInt + Copy + core::fmt::Debug,
    Inner: BitAnd<Output = Inner>
        + BitOr<Output = Inner>
        + BitXor<Output = Inner>
        + Not<Output = Inner>,
    Inner: BitAndAssign + BitOrAssign + BitXorAssign,
    Inner: Shl<usize, Output = Inner> + Shr<usize, Output = Inner>,
{
    fn insert<N: Copy + TryInto<usize>>(&mut self, i: N) -> Option<N> {
        self.insert(i)
    }

    fn remove<N: Copy + TryInto<usize>>(&mut self, i: N) -> Option<N> {
        self.remove(i)
    }

    fn truncate(&mut self) {
        self.truncate();
    }
    fn nullify(&mut self) {
        self.nullify();
    }
}

impl<Inner, MaxChunks> traits::BoundedBitmapReader for BoundedBitmap<Inner, MaxChunks>
where
    MaxChunks: Get<u32>,
    Inner: AtLeast8BitUnsigned + PrimInt + Copy + core::fmt::Debug,
    Inner: BitAnd<Output = Inner>
        + BitOr<Output = Inner>
        + BitXor<Output = Inner>
        + Not<Output = Inner>,
    Inner: BitAndAssign + BitOrAssign + BitXorAssign,
    Inner: Shl<usize, Output = Inner> + Shr<usize, Output = Inner>,
{
    fn active_bits_count(&self) -> u32 {
        self.active_bits_count()
    }
    fn is_empty(&self) -> bool {
        self.is_empty()
    }

    fn highest_bit<N>(&self) -> Option<N>
    where
        usize: UniqueSaturatedInto<N>,
    {
        self.highest_bit()
    }

    fn count_ones<N>(&self) -> N
    where
        u32: UniqueSaturatedInto<N>,
    {
        self.count_ones()
    }

    fn count_zeros<N>(&self) -> N
    where
        u32: UniqueSaturatedInto<N>,
    {
        self.count_zeros()
    }

    fn contains<N>(&self, index: N) -> bool
    where
        N: UniqueSaturatedInto<usize>,
    {
        self.contains(index)
    }
}

impl<Inner, MaxChunks> traits::BoundedBitmapGenerator for BoundedBitmap<Inner, MaxChunks>
where
    MaxChunks: Get<u32>,
    Inner: AtLeast8BitUnsigned + PrimInt + Copy + core::fmt::Debug,
    Inner: BitAnd<Output = Inner>
        + BitOr<Output = Inner>
        + BitXor<Output = Inner>
        + Not<Output = Inner>,
    Inner: BitAndAssign + BitOrAssign + BitXorAssign,
    Inner: Shl<usize, Output = Inner> + Shr<usize, Output = Inner>,
{
    fn empty<N>(n: N) -> Self
    where
        N: UniqueSaturatedInto<usize>,
    {
        Self::empty(n)
    }

    fn all_ones<N>(n: N) -> Self
    where
        N: UniqueSaturatedInto<usize>,
    {
        Self::all_ones(n)
    }

    fn empty_from(bitmap_ref: &Self) -> Self {
        Self::empty_from(bitmap_ref)
    }

    fn from_bitmask<Outer>(values_ref: &Vec<Outer>) -> Self
    where
        Outer: AtLeast8BitUnsigned + PrimInt,
    {
        Self::from_bitmask(values_ref)
    }

    fn from_vec<Outer>(values_ref: &Vec<Outer>) -> Self
    where
        Outer: AtLeast8BitUnsigned + TryInto<usize> + PartialEq + Copy,
    {
        Self::from_vec(values_ref)
    }
}

impl<Inner, MaxChunks> traits::BoundedBitmapIterable for BoundedBitmap<Inner, MaxChunks>
where
    MaxChunks: Get<u32>,
    Inner: AtLeast8BitUnsigned + PrimInt + Copy + core::fmt::Debug,
    Inner: BitAnd<Output = Inner>
        + BitOr<Output = Inner>
        + BitXor<Output = Inner>
        + Not<Output = Inner>,
    Inner: BitAndAssign + BitOrAssign + BitXorAssign,
    Inner: Shl<usize, Output = Inner> + Shr<usize, Output = Inner>,
{
    type IntoIter<'a, ItemType> = BoundedBitmapIter<'a, Inner, MaxChunks, ItemType>
    where
        Self: 'a,
        ItemType: 'static,
        u32: UniqueSaturatedInto<ItemType>;

    fn iter<ItemType>(&self) -> Self::IntoIter<'_, ItemType>
    where
        ItemType: 'static,
        u32: UniqueSaturatedInto<ItemType>,
    {
        BoundedBitmapIter {
            bounded_bitmap: self,
            current_bit: 0,
            _marker: sp_std::marker::PhantomData,
        }
    }
}

macro_rules! impl_bitmap_binary_ops {
    ($trait:ident, $method:ident, $trait_assign:ident, $method_assign:ident, $op:tt) => (
        impl<Inner, MaxChunks> $trait for BoundedBitmap<Inner, MaxChunks>
        where
        	MaxChunks: Get<u32>,
			Inner: AtLeast8BitUnsigned + PrimInt + Copy + core::fmt::Debug,
			Inner: BitAnd<Output = Inner> + BitOr<Output = Inner> + BitXor<Output = Inner> + Not<Output = Inner>,
			Inner: BitAndAssign + BitOrAssign + BitXorAssign,
	    	Inner: Shl<usize, Output = Inner> + Shr<usize, Output = Inner>,
        {
            type Output = BoundedBitmap<Inner, MaxChunks>;
            fn $method(self, rhs: Self) -> Self::Output {
                self.combine(&rhs, |a, b| a $op b)
            }
        }

        impl<'a, Inner, MaxChunks> $trait<&'a BoundedBitmap<Inner, MaxChunks>> for BoundedBitmap<Inner, MaxChunks>
        where
        	MaxChunks: Get<u32>,
			Inner: AtLeast8BitUnsigned + PrimInt + Copy + core::fmt::Debug,
			Inner: BitAnd<Output = Inner> + BitOr<Output = Inner> + BitXor<Output = Inner> + Not<Output = Inner>,
			Inner: BitAndAssign + BitOrAssign + BitXorAssign,
	    	Inner: Shl<usize, Output = Inner> + Shr<usize, Output = Inner>,
        {
            type Output = BoundedBitmap<Inner, MaxChunks>;
            fn $method(self, rhs: &'a BoundedBitmap<Inner, MaxChunks>) -> Self::Output {
                self.combine(&rhs, |a, b| a $op b)
            }
        }

        impl<Inner, MaxChunks> $trait_assign for BoundedBitmap<Inner, MaxChunks>
        where
        	MaxChunks: Get<u32>,
			Inner: AtLeast8BitUnsigned + PrimInt + Copy + core::fmt::Debug,
			Inner: BitAnd<Output = Inner> + BitOr<Output = Inner> + BitXor<Output = Inner> + Not<Output = Inner>,
			Inner: BitAndAssign + BitOrAssign + BitXorAssign,
	    	Inner: Shl<usize, Output = Inner> + Shr<usize, Output = Inner>,
        {
            fn $method_assign(&mut self, rhs: BoundedBitmap<Inner, MaxChunks>) {
                *self = self.combine(&rhs, |a, b| a $op b)
            }
        }

        impl<'a, Inner, MaxChunks> $trait_assign<&'a BoundedBitmap<Inner, MaxChunks>> for BoundedBitmap<Inner, MaxChunks>
        where
        	MaxChunks: Get<u32>,
			Inner: AtLeast8BitUnsigned + PrimInt + Copy + core::fmt::Debug,
			Inner: BitAnd<Output = Inner> + BitOr<Output = Inner> + BitXor<Output = Inner> + Not<Output = Inner>,
			Inner: BitAndAssign + BitOrAssign + BitXorAssign,
	    	Inner: Shl<usize, Output = Inner> + Shr<usize, Output = Inner>,
        {
            fn $method_assign(&mut self, rhs: &'a BoundedBitmap<Inner, MaxChunks>) {
                *self = self.combine(rhs, |a, b| a $op b)
            }
        }

        impl<'a, 'b, Inner, MaxChunks> $trait<&'b BoundedBitmap<Inner, MaxChunks>> for &'a BoundedBitmap<Inner, MaxChunks>
        where
        	MaxChunks: Get<u32>,
			Inner: AtLeast8BitUnsigned + PrimInt + Copy + core::fmt::Debug,
			Inner: BitAnd<Output = Inner> + BitOr<Output = Inner> + BitXor<Output = Inner> + Not<Output = Inner>,
			Inner: BitAndAssign + BitOrAssign + BitXorAssign,
	    	Inner: Shl<usize, Output = Inner> + Shr<usize, Output = Inner>,
        {
            type Output = BoundedBitmap<Inner, MaxChunks>;

            fn $method(
                self,
                rhs: &'b BoundedBitmap<Inner, MaxChunks>,
            ) -> Self::Output {
                self.combine(rhs, |a, b| a $op b)
            }
        }

        impl<'a, Inner, MaxChunks> $trait<BoundedBitmap<Inner, MaxChunks>> for &'a BoundedBitmap<Inner, MaxChunks>
        where
            MaxChunks: Get<u32>,
            Inner: AtLeast8BitUnsigned + PrimInt + Copy + core::fmt::Debug,
            Inner: BitAnd<Output = Inner> + BitOr<Output = Inner> + BitXor<Output = Inner> + Not<Output = Inner>,
            Inner: BitAndAssign + BitOrAssign + BitXorAssign,
            Inner: Shl<usize, Output = Inner> + Shr<usize, Output = Inner>,
        {
            type Output = BoundedBitmap<Inner, MaxChunks>;

            fn $method(
                self,
                rhs: BoundedBitmap<Inner, MaxChunks>,
            ) -> Self::Output {
                self.combine(&rhs, |a, b| a $op b)
            }
        }
    );
}

impl_bitmap_binary_ops!(BitOr,  bitor,  BitOrAssign,  bitor_assign,  |);
impl_bitmap_binary_ops!(BitAnd, bitand, BitAndAssign, bitand_assign, &);
impl_bitmap_binary_ops!(BitXor, bitxor, BitXorAssign, bitxor_assign, ^);

impl<Inner, MaxChunks> Clone for BoundedBitmap<Inner, MaxChunks>
where
    MaxChunks: Get<u32>,
    Inner: Copy,
{
    fn clone(&self) -> Self {
        let raw_vec: Vec<Inner> = self.data.to_vec();
        let bounded_data = BoundedVec::<Inner, MaxChunks>::try_from(raw_vec).unwrap_or_default();

        Self {
            data: bounded_data,
            active_bits_count: self.active_bits_count,
        }
    }
}

impl<Inner, MaxChunks> Not for BoundedBitmap<Inner, MaxChunks>
where
    MaxChunks: Get<u32>,
    Inner: AtLeast8BitUnsigned + PrimInt + Copy + core::fmt::Debug,
    Inner: BitAnd<Output = Inner>
        + BitOr<Output = Inner>
        + BitXor<Output = Inner>
        + Not<Output = Inner>,
    Inner: BitAndAssign + BitOrAssign + BitXorAssign,
    Inner: Shl<usize, Output = Inner> + Shr<usize, Output = Inner>,
{
    type Output = Self;

    fn not(self) -> Self::Output {
        let n = self.active_bits_count;
        let full_bitmap = BoundedBitmap::<Inner, MaxChunks>::all_ones(n);

        full_bitmap ^ self
    }
}

impl<'a, Inner, MaxChunks> Not for &'a BoundedBitmap<Inner, MaxChunks>
where
    MaxChunks: Get<u32>,
    Inner: AtLeast8BitUnsigned + PrimInt + Copy + core::fmt::Debug,
    Inner: BitAnd<Output = Inner>
        + BitOr<Output = Inner>
        + BitXor<Output = Inner>
        + Not<Output = Inner>,
    Inner: BitAndAssign + BitOrAssign + BitXorAssign,
    Inner: Shl<usize, Output = Inner> + Shr<usize, Output = Inner>,
{
    type Output = BoundedBitmap<Inner, MaxChunks>;

    fn not(self) -> Self::Output {
        let n = self.active_bits_count;
        let full_bitmap = BoundedBitmap::<Inner, MaxChunks>::all_ones(n);

        full_bitmap ^ self
    }
}

impl<Inner, MaxChunks> PartialEq for BoundedBitmap<Inner, MaxChunks>
where
    MaxChunks: Get<u32>,
    Inner: AtLeast8BitUnsigned + Copy + PartialEq,
{
    fn eq(&self, other: &Self) -> bool {
        self.active_bits_count == other.active_bits_count && self.data == other.data
    }
}

impl<Inner, MaxChunks> Eq for BoundedBitmap<Inner, MaxChunks>
where
    MaxChunks: Get<u32>,
    Inner: AtLeast8BitUnsigned + Copy + Eq,
{
}

impl<Inner, MaxChunks> Default for BoundedBitmap<Inner, MaxChunks>
where
    MaxChunks: Get<u32>,
    Inner: AtLeast8BitUnsigned + PrimInt + Copy + core::fmt::Debug,
    Inner: BitAnd<Output = Inner>
        + BitOr<Output = Inner>
        + BitXor<Output = Inner>
        + Not<Output = Inner>,
    Inner: BitAndAssign + BitOrAssign + BitXorAssign,
    Inner: Shl<usize, Output = Inner> + Shr<usize, Output = Inner>,
{
    fn default() -> Self {
        let num_chunks = MaxChunks::get() as usize;
        let bits_per_chunk = inner_bits::<Inner>();
        let total_bits = num_chunks * bits_per_chunk;
        Self::empty(total_bits)
    }
}

pub struct BoundedBitmapIter<'a, Inner, MaxChunks, ItemType>
where
    MaxChunks: Get<u32>,
{
    bounded_bitmap: &'a BoundedBitmap<Inner, MaxChunks>,
    current_bit: u32,
    _marker: PhantomData<ItemType>,
}

impl<'a, Inner, MaxChunks, ItemType> Iterator for BoundedBitmapIter<'a, Inner, MaxChunks, ItemType>
where
    MaxChunks: Get<u32>,
    Inner: AtLeast8BitUnsigned + Copy,
    Inner: BitAnd<Output = Inner> + Not<Output = Inner>,
    u32: UniqueSaturatedInto<ItemType>,
{
    type Item = ItemType;

    fn next(&mut self) -> Option<Self::Item> {
        while self.current_bit < self.bounded_bitmap.active_bits_count {
            let index_u32 = self.current_bit;
            let index_usize = index_u32 as usize;
            self.current_bit = self.current_bit.saturating_add(1);

            let element_index = index_usize >> inner_shift::<Inner>();
            let bit_shift = (index_usize & inner_mask::<Inner>()) as u32;

            match self.bounded_bitmap.data.get(element_index) {
                Some(element) => {
                    if let Some(mask) = Inner::one().checked_shl(bit_shift) {
                        if !(*element & mask).is_zero() {
                            return Some(index_u32.unique_saturated_into());
                        }
                    }
                }
                None => {
                    self.current_bit = self.bounded_bitmap.active_bits_count;
                    return None;
                }
            }
        }
        None
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use frame_support::traits::Get;
    use sp_runtime::traits::{One, Zero};

    use super::traits::*;

    #[derive(Clone, Copy, PartialEq, Eq, core::fmt::Debug, Encode, Decode, MaxEncodedLen)]
    pub struct Entries4;
    impl Get<u32> for Entries4 {
        fn get() -> u32 {
            4
        }
    }

    #[derive(Clone, Copy, PartialEq, Eq, core::fmt::Debug, Encode, Decode, MaxEncodedLen)]
    pub struct Entries8;
    impl Get<u32> for Entries8 {
        fn get() -> u32 {
            8
        }
    }

    #[derive(Clone, Copy, PartialEq, Eq, core::fmt::Debug, Encode, Decode, MaxEncodedLen)]
    pub struct Entries16;
    impl Get<u32> for Entries16 {
        fn get() -> u32 {
            16
        }
    }

    #[derive(Clone, Copy, PartialEq, Eq, core::fmt::Debug, Encode, Decode, MaxEncodedLen)]
    pub struct Entries128;
    impl Get<u32> for Entries128 {
        fn get() -> u32 {
            128
        }
    }

    #[derive(Clone, Copy, PartialEq, Eq, core::fmt::Debug, Encode, Decode, MaxEncodedLen)]
    pub struct Entries512;
    impl Get<u32> for Entries512 {
        fn get() -> u32 {
            512
        }
    }

    #[derive(Clone, Copy, PartialEq, Eq, core::fmt::Debug, Encode, Decode, MaxEncodedLen)]
    pub struct Entries1024;
    impl Get<u32> for Entries1024 {
        fn get() -> u32 {
            1024
        }
    }

    macro_rules! test_bitmap {
        ($mod_name:ident, $inner_type:ty, $max_chunks:ty) => {
            mod $mod_name {
                use super::*;

                type TestBitmap = BoundedBitmap<$inner_type, $max_chunks>;

                #[test]
                fn test_empty_initialization_and_saturation() {
                    let bits_per_element = inner_bits::<$inner_type>();
                    let max_allowed_elements = <$max_chunks>::get() as usize;
                    let absolute_max_bits = max_allowed_elements.saturating_mul(bits_per_element);

                    let bitmap = TestBitmap::empty(3usize);
                    assert_eq!(bitmap.active_bits_count(), 3);
                    assert_eq!(bitmap.data.len(), 1);

                    let huge_bitmap = TestBitmap::empty(absolute_max_bits + 1000);
                    assert_eq!(huge_bitmap.active_bits_count, absolute_max_bits as u32);
                    assert_eq!(huge_bitmap.data.len(), max_allowed_elements);
                }

                #[test]
                fn test_sequential_insert_and_out_of_bounds() {
                    let bits_per_element = inner_bits::<$inner_type>();
                    let mut bitmap = TestBitmap::empty(bits_per_element * 2);

                    assert!(bitmap.insert(0u32).is_some());
                    assert_eq!(bitmap.count_ones::<u32>(), 1);

                    let boundary_bit = bits_per_element as u32;
                    assert!(bitmap.insert(boundary_bit).is_some());
                    assert_eq!(bitmap.count_ones::<u32>(), 2);

                    let out_of_bounds_bit = (bits_per_element * 2) as u32;
                    assert!(bitmap.insert(out_of_bounds_bit).is_none());
                    assert_eq!(bitmap.count_ones::<u32>(), 2);
                }

                #[test]
                fn test_truncate_and_memory_releasing() {
                    let bits_per_element = inner_bits::<$inner_type>();
                    let mut bitmap = TestBitmap::empty(bits_per_element * 2);
                    assert_eq!(bitmap.data.len(), 2);

                    bitmap.insert(0u32).unwrap();
                    bitmap.truncate();
                    assert_eq!(bitmap.data.len(), 1);
                    assert_eq!(bitmap.active_bits_count, 1);
                }

                #[test]
                fn test_not_inversion_and_tail_nullifying() {
                    let mut bitmap = TestBitmap::empty(5usize);
                    bitmap.insert(0u32).unwrap();

                    let inverted = !bitmap;

                    if let Some(&last_element) = inverted.data.get(0) {
                        let expected_mask = (<$inner_type>::one() << 5) - <$inner_type>::one();
                        assert_eq!(last_element & !expected_mask, <$inner_type>::zero());
                    }
                }

                #[test]
                fn test_bitor_different_sizes() {
                    let bits_per_element = inner_bits::<$inner_type>();
                    let mut bitmap_a = TestBitmap::empty(5usize);
                    bitmap_a.insert(2u32).unwrap();

                    let mut bitmap_b = TestBitmap::empty(bits_per_element + 5);
                    let far_bit = (bits_per_element + 1) as u32;
                    bitmap_b.insert(far_bit).unwrap();

                    let result = bitmap_a | bitmap_b;
                    assert_eq!(result.active_bits_count, (bits_per_element + 5) as u32);
                    assert_eq!(result.count_ones::<u32>(), 2);
                    assert_eq!(result.data.len(), 2);
                }

                #[test]
                fn test_bitxor_different_sizes() {
                    let bits_per_element = inner_bits::<$inner_type>();
                    let mut bitmap_a = TestBitmap::empty(10usize);
                    let mut bitmap_b = TestBitmap::empty(bits_per_element + 10);

                    bitmap_a.insert(5u32).unwrap();
                    bitmap_b.insert(5u32).unwrap();
                    bitmap_a.insert(8u32).unwrap();

                    let far_bit = (bits_per_element + 2) as u32;
                    bitmap_b.insert(far_bit).unwrap();

                    let result = bitmap_a ^ bitmap_b;
                    assert_eq!(result.active_bits_count, (bits_per_element + 10) as u32);
                    assert_eq!(result.count_ones::<u32>(), 2);
                }

                #[test]
                fn test_bitand_different_sizes() {
                    let bits_per_element = inner_bits::<$inner_type>();
                    let bigger_length = (bits_per_element + 5) as u32;

                    let mut bitmap_a = TestBitmap::empty(5usize);
                    bitmap_a.insert(2u32).unwrap();

                    let mut bitmap_b = TestBitmap::empty(bigger_length);
                    bitmap_b.insert(2u32).unwrap();

                    let far_bit = (bits_per_element + 1) as u32;
                    bitmap_b.insert(far_bit).unwrap();

                    let mut result = bitmap_a & bitmap_b;
                    assert_eq!(result.count_ones::<u32>(), 1);
                    assert_eq!(result.active_bits_count, bigger_length);

                    result.truncate();
                    assert_eq!(result.data.len(), 1);
                }

                #[test]
                fn test_bitor_assign_different_sizes() {
                    let bits_per_element = inner_bits::<$inner_type>();
                    let mut bitmap_a = TestBitmap::empty(5usize);
                    bitmap_a.insert(1u32).unwrap();

                    let mut bitmap_b = TestBitmap::empty(bits_per_element + 5);
                    let far_bit = (bits_per_element + 1) as u32;
                    bitmap_b.insert(far_bit).unwrap();

                    bitmap_a |= bitmap_b;
                    assert_eq!(bitmap_a.active_bits_count, (bits_per_element + 5) as u32);
                    assert_eq!(bitmap_a.count_ones::<u32>(), 2);
                    assert_eq!(bitmap_a.data.len(), 2);
                }

                #[test]
                fn test_and_assign_different_sizes() {
                    let bits_per_element = inner_bits::<$inner_type>();
                    let bigger_length = (bits_per_element + 5) as u32;

                    let mut bitmap_a = TestBitmap::empty(5usize);
                    bitmap_a.insert(2u32).unwrap();

                    let mut bitmap_b = TestBitmap::empty(bigger_length);
                    bitmap_b.insert(2u32).unwrap();

                    let far_bit = (bits_per_element + 1) as u32;
                    bitmap_b.insert(far_bit).unwrap();

                    bitmap_a &= bitmap_b;
                    assert_eq!(bitmap_a.active_bits_count(), bigger_length);
                    assert_eq!(bitmap_a.count_ones::<u32>(), 1);

                    bitmap_a.truncate();
                    assert_eq!(bitmap_a.active_bits_count(), 3);
                    assert_eq!(bitmap_a.data.len(), 1);
                }

                #[test]
                fn test_xor_assign_different_sizes() {
                    let bits_per_element = inner_bits::<$inner_type>();
                    let bigger_length = (bits_per_element + 5) as u32;

                    let mut bitmap_a = TestBitmap::empty(10usize);
                    let mut bitmap_b = TestBitmap::empty(bigger_length);

                    bitmap_a.insert(5u32).unwrap();
                    bitmap_b.insert(5u32).unwrap();

                    bitmap_a.insert(8u32).unwrap();

                    let far_bit = (bits_per_element + 2) as u32;
                    bitmap_b.insert(far_bit).unwrap();

                    bitmap_a ^= bitmap_b;
                    assert_eq!(bitmap_a.active_bits_count(), bigger_length);
                    assert_eq!(bitmap_a.count_ones::<u32>(), 2);
                    assert_eq!(bitmap_a.data.len(), 2);
                }

                #[test]
                fn test_iterator_only_iterates_over_ones() {
                    let max_allowed_chunks = <$max_chunks>::get();
                    let mut bitmap = TestBitmap::default();

                    let bit_1 = 0u32;
                    let bit_2 = max_allowed_chunks.saturating_div(2);
                    let bit_3 = max_allowed_chunks.saturating_sub(1);

                    bitmap.insert(bit_1).unwrap();
                    bitmap.insert(bit_2).unwrap();
                    bitmap.insert(bit_3).unwrap();

                    let mut iter = bitmap.iter();

                    assert_eq!(iter.next(), Some(bit_1));
                    assert_eq!(iter.next(), Some(bit_2));
                    assert_eq!(iter.next(), Some(bit_3));
                    assert_eq!(iter.next(), None);
                }

                #[test]
                fn test_default_and_nullify_and_contains() {
                    let default_bitmap = TestBitmap::default();
                    let num_chunks = <$max_chunks>::get();
                    let bits_per_chunk = inner_bits::<$inner_type>();
                    let total_bits = (num_chunks as usize) * bits_per_chunk;

                    assert_eq!(default_bitmap.active_bits_count(), total_bits as u32);
                    assert_eq!(default_bitmap.count_ones::<u32>(), 0);

                    let reminder = total_bits & inner_mask::<$inner_type>();
                    let mut expected = total_bits >> inner_shift::<$inner_type>();
                    if reminder > 0 {
                        expected = expected.saturating_add(1);
                    }
                    assert_eq!(default_bitmap.data.len(), expected);

                    let mut bitmap = TestBitmap::empty(10usize);
                    assert!(!bitmap.contains(5u32));

                    bitmap.insert(5u32).unwrap();
                    assert!(bitmap.contains(5u32));
                    assert!(!bitmap.contains(6u32));

                    bitmap.remove(5u32);
                    assert!(!bitmap.contains(5u32));
                    assert_eq!(bitmap.count_ones::<u32>(), 0);

                    let mut bitmap_to_nullify = TestBitmap::empty(30usize);
                    bitmap_to_nullify.insert(14u32).unwrap();
                    bitmap_to_nullify.insert(15u32).unwrap();

                    bitmap_to_nullify.nullify();
                    assert_eq!(bitmap_to_nullify.count_ones::<u32>(), 0);
                    assert_eq!(bitmap_to_nullify.active_bits_count(), 30);
                    for element in bitmap_to_nullify.data.iter() {
                        assert_eq!(*element, <$inner_type>::zero());
                    }
                }

                #[test]
                fn test_count_zeros() {
                    let bits_per_element = inner_bits::<$inner_type>();
                    let bitmap = TestBitmap::empty(bits_per_element + 10);

                    let total_zeros: u32 = bitmap.count_zeros();
                    assert_eq!(total_zeros, (bits_per_element + 10) as u32);

                    let mut mut_bitmap = TestBitmap::empty(10usize);
                    mut_bitmap.insert(3u32).unwrap();
                    mut_bitmap.insert(7u32).unwrap();

                    let zeros: u32 = mut_bitmap.count_zeros();
                    assert_eq!(zeros, 8);
                }

                #[test]
                fn test_empty_from() {
                    let mut original = TestBitmap::empty(25usize);
                    original.insert(10u32).unwrap();

                    let brand_new = TestBitmap::empty_from(&original);
                    assert_eq!(brand_new.active_bits_count(), 25);
                    assert_eq!(brand_new.count_ones::<u32>(), 0);
                }

                #[test]
                fn test_from_vec() {
                    let values_vec: Vec<u8> = (0..10).filter(|i| i % 2 == 1).collect();
                    let bitmap = TestBitmap::from_vec(&values_vec);

                    (0..10).for_each(|i| {
                        if i % 2 == 0 {
                            assert!(!bitmap.contains(i));
                        } else {
                            assert!(bitmap.contains(i));
                        }
                    });

                    assert_eq!(bitmap.count_ones::<usize>(), values_vec.len());
                }

                #[test]
                fn test_from_bitmask_where_each_u8_is_a_bit() {
                    let byte1 = 77u8;
                    let byte2 = 42u8;
                    let byte3 = 69u8;

                    let raw_bits: Vec<u8> = vec![byte1, byte2, byte3];
                    let bitmap = TestBitmap::from_bitmask(&raw_bits);

                    let count_ones = raw_bits.iter().map(|b| b.count_ones()).sum::<u32>();
                    let filled_bits = (raw_bits.len() as u32 - 1) * 8;
                    let expected_bits = filled_bits + (8 - byte3.leading_zeros());

                    assert_eq!(bitmap.active_bits_count(), expected_bits);
                    assert_eq!(bitmap.count_ones::<u32>(), count_ones);

                    // 77 - 10110010
                    assert!(bitmap.contains(0u32));
                    assert!(!bitmap.contains(1u32));
                    assert!(bitmap.contains(2u32));
                    assert!(bitmap.contains(3u32));
                    assert!(!bitmap.contains(4u32));
                    assert!(!bitmap.contains(5u32));
                    assert!(bitmap.contains(6u32));
                    assert!(!bitmap.contains(7u32));

                    // 42 - 01010100
                    assert!(!bitmap.contains(8u32));
                    assert!(bitmap.contains(9u32));
                    assert!(!bitmap.contains(10u32));
                    assert!(bitmap.contains(11u32));
                    assert!(!bitmap.contains(12u32));
                    assert!(bitmap.contains(13u32));
                    assert!(!bitmap.contains(14u32));
                    assert!(!bitmap.contains(15u32));

                    // 69 - 10100010
                    assert!(bitmap.contains(16u32));
                    assert!(!bitmap.contains(17u32));
                    assert!(bitmap.contains(18u32));
                    assert!(!bitmap.contains(19u32));
                    assert!(!bitmap.contains(20u32));
                    assert!(!bitmap.contains(21u32));
                    assert!(bitmap.contains(22u32));
                    assert!(!bitmap.contains(23u32));
                }
            }
        };
    }

    // 1. Matrix rows for u8
    test_bitmap!(matrix_u8_entries4, u8, Entries4);
    test_bitmap!(matrix_u8_entries8, u8, Entries8);
    test_bitmap!(matrix_u8_entries16, u8, Entries16);
    test_bitmap!(matrix_u8_entries128, u8, Entries128);
    test_bitmap!(matrix_u8_entries512, u8, Entries512);
    test_bitmap!(matrix_u8_entries1024, u8, Entries1024);

    // 2. Matrix rows for u16
    test_bitmap!(matrix_u16_entries4, u16, Entries4);
    test_bitmap!(matrix_u16_entries8, u16, Entries8);
    test_bitmap!(matrix_u16_entries16, u16, Entries16);
    test_bitmap!(matrix_u16_entries128, u16, Entries128);
    test_bitmap!(matrix_u16_entries512, u16, Entries512);
    test_bitmap!(matrix_u16_entries1024, u16, Entries1024);

    // 3. Matrix rows for u32
    test_bitmap!(matrix_u32_entries4, u32, Entries4);
    test_bitmap!(matrix_u32_entries8, u32, Entries8);
    test_bitmap!(matrix_u32_entries16, u32, Entries16);
    test_bitmap!(matrix_u32_entries128, u32, Entries128);
    test_bitmap!(matrix_u32_entries512, u32, Entries512);
    test_bitmap!(matrix_u32_entries1024, u32, Entries1024);

    // 4. Matrix rows for u64
    test_bitmap!(matrix_u64_entries4, u64, Entries4);
    test_bitmap!(matrix_u64_entries8, u64, Entries8);
    test_bitmap!(matrix_u64_entries16, u64, Entries16);
    test_bitmap!(matrix_u64_entries128, u64, Entries128);
    test_bitmap!(matrix_u64_entries512, u64, Entries512);
    test_bitmap!(matrix_u64_entries1024, u64, Entries1024);

    // 5. Matrix rows for u128
    test_bitmap!(matrix_u128_entries4, u128, Entries4);
    test_bitmap!(matrix_u128_entries8, u128, Entries8);
    test_bitmap!(matrix_u128_entries16, u128, Entries16);
    test_bitmap!(matrix_u128_entries128, u128, Entries128);
    test_bitmap!(matrix_u128_entries512, u128, Entries512);
    test_bitmap!(matrix_u128_entries1024, u128, Entries1024);
}
