#[derive(Debug, Clone, Copy, PartialEq, Eq)]
/// Represents an iterator over u32 cells in a node.
pub struct Cells<'a> {
    data: &'a [u32],
}

impl<'a> Cells<'a> {
    /// Creates a new `Cells` iterator over the given slice of big-endian `u32` values.
    pub const fn new(data_be: &'a [u32]) -> Self {
        Self { data: data_be }
    }

    /// Returns the number of remaining cells in the iterator.
    pub const fn len(&self) -> usize {
        self.data.len()
    }

    /// Returns `true` if the iterator has no remaining cells.
    pub const fn is_empty(&self) -> bool {
        self.data.is_empty()
    }

    /// To interpret each value you have to call `u32::from_be` on it.
    pub const fn as_u32be(&self) -> &'a [u32] {
        self.data
    }

    /// Attempts to get the `n`th cell as a `u32`.
    pub const fn get(&self, n: usize) -> Option<u32> {
        if n >= self.len() {
            return None;
        }
        Some(u32::from_be(self.data[n]))
    }

    /// Returns and consumes the next `u32` cell from the iterator.
    pub fn next_cell(&mut self) -> Option<u32> {
        let cell = self.get(0)?;
        self.data = &self.data[1..];
        Some(cell)
    }

    /// Returns the next `usize` value by reading `cells` cells from the iterator.
    ///
    /// If `cells` is greater than the number of remaining cells or is zero or greater than the number of cells that can fit in a `usize` (i.e. 2 in 64-bit systems), returns `None`.
    pub fn next_usize(&mut self, cells: u32) -> Option<usize> {
        let cells = cells as usize;
        if cells > self.len() || cells > (size_of::<usize>() / size_of::<u32>()) || cells == 0 {
            return None;
        }

        let (cells, remaining) = self.data.split_at(cells);
        let mut result = 0;
        for cell in cells {
            result = (result << 32) | u32::from_be(*cell) as usize;
        }
        self.data = remaining;
        Some(result)
    }

    /// Attempts to split the iterator at the given index, returning two new `Cells` instances.
    ///
    /// Returns `None` if `n` is greater than the number of remaining cells.
    pub fn try_spilt_at(&self, n: usize) -> Option<(Self, Self)> {
        let (left, right) = self.data.split_at_checked(n)?;
        Some((Self::new(left), Self::new(right)))
    }

    /// Takes `n` cells from the iterator and returns them as a `Cells` instance.
    ///
    /// Returns `None` if `n` is greater than the number of remaining cells.
    pub fn take_n(&mut self, n: u32) -> Option<Self> {
        let (cells, remaining) = self.try_spilt_at(n as usize)?;
        *self = remaining;
        Some(cells)
    }

    /// Reads `N` cells from the iterator and returns them as an array, converting them from big-endian to host byte order.
    ///
    /// Returns `None` if `N` is greater than the number of remaining cells.
    pub fn next_array<const N: usize>(&mut self) -> Option<[u32; N]> {
        let mut array = [0u32; N];

        self.read(&mut array)?;
        Some(array)
    }

    /// Takes `buf.len()` cells from the iterator and stores them in `buf`, converting them from big-endian to host byte order.
    ///
    /// Returns `None` if `buf.len()` is greater than the number of remaining cells.
    pub fn read<'b>(&mut self, buf: &'b mut [u32]) -> Option<&'b mut [u32]> {
        if buf.len() > self.len() {
            return None;
        }

        let (cells, remaining) = self.data.split_at(buf.len());
        for (cell, buf_cell) in cells.iter().zip(buf.iter_mut()) {
            *buf_cell = u32::from_be(*cell);
        }
        self.data = remaining;
        Some(buf)
    }
}

impl ExactSizeIterator for Cells<'_> {
    fn len(&self) -> usize {
        self.len()
    }
}
impl<'a> Iterator for Cells<'a> {
    type Item = u32;

    fn nth(&mut self, n: usize) -> Option<Self::Item> {
        self.get(n)
    }
    fn next(&mut self) -> Option<Self::Item> {
        self.next_cell()
    }
}
