use std::iter::FusedIterator;
use crate::types::ChunkLength;
use super::{index::RawIndex, RingBuffer};
pub struct Iter<'a, A, N>
where
    N: ChunkLength<A>,
{
    pub(crate) buffer: &'a RingBuffer<A, N>,
    pub(crate) left_index: RawIndex<A, N>,
    pub(crate) right_index: RawIndex<A, N>,
    pub(crate) remaining: usize,
}
impl<'a, A, N> Iterator for Iter<'a, A, N>
where
    N: ChunkLength<A>,
{
    type Item = &'a A;
    fn next(&mut self) -> Option<Self::Item> {
        if self.remaining == 0 {
            None
        } else {
            self.remaining -= 1;
            Some(unsafe { &*self.buffer.ptr(self.left_index.inc()) })
        }
    }
    #[inline]
    #[must_use]
    fn size_hint(&self) -> (usize, Option<usize>) {
        (self.remaining, Some(self.remaining))
    }
}
impl<'a, A, N> DoubleEndedIterator for Iter<'a, A, N>
where
    N: ChunkLength<A>,
{
    fn next_back(&mut self) -> Option<Self::Item> {
        if self.remaining == 0 {
            None
        } else {
            self.remaining -= 1;
            Some(unsafe { &*self.buffer.ptr(self.right_index.dec()) })
        }
    }
}
impl<'a, A, N> ExactSizeIterator for Iter<'a, A, N> where N: ChunkLength<A> {}
impl<'a, A, N> FusedIterator for Iter<'a, A, N> where N: ChunkLength<A> {}
pub struct IterMut<'a, A, N>
where
    N: ChunkLength<A>,
{
    pub(crate) buffer: &'a mut RingBuffer<A, N>,
    pub(crate) left_index: RawIndex<A, N>,
    pub(crate) right_index: RawIndex<A, N>,
    pub(crate) remaining: usize,
}
impl<'a, A, N> Iterator for IterMut<'a, A, N>
where
    N: ChunkLength<A>,
{
    type Item = &'a mut A;
    fn next(&mut self) -> Option<Self::Item> {
        if self.remaining == 0 {
            None
        } else {
            self.remaining -= 1;
            Some(unsafe { &mut *self.buffer.mut_ptr(self.left_index.inc()) })
        }
    }
    #[inline]
    #[must_use]
    fn size_hint(&self) -> (usize, Option<usize>) {
        (self.remaining, Some(self.remaining))
    }
}
impl<'a, A, N> DoubleEndedIterator for IterMut<'a, A, N>
where
    N: ChunkLength<A>,
{
    fn next_back(&mut self) -> Option<Self::Item> {
        if self.remaining == 0 {
            None
        } else {
            self.remaining -= 1;
            Some(unsafe { &mut *self.buffer.mut_ptr(self.right_index.dec()) })
        }
    }
}
impl<'a, A, N> ExactSizeIterator for IterMut<'a, A, N> where N: ChunkLength<A> {}
impl<'a, A, N> FusedIterator for IterMut<'a, A, N> where N: ChunkLength<A> {}
pub struct Drain<'a, A, N: ChunkLength<A>> {
    pub(crate) buffer: &'a mut RingBuffer<A, N>,
}
impl<'a, A: 'a, N: ChunkLength<A> + 'a> Iterator for Drain<'a, A, N> {
    type Item = A;
    #[inline]
    fn next(&mut self) -> Option<Self::Item> {
        self.buffer.pop_front()
    }
    #[inline]
    #[must_use]
    fn size_hint(&self) -> (usize, Option<usize>) {
        (self.buffer.len(), Some(self.buffer.len()))
    }
}
impl<'a, A: 'a, N: ChunkLength<A> + 'a> DoubleEndedIterator for Drain<'a, A, N> {
    #[inline]
    fn next_back(&mut self) -> Option<Self::Item> {
        self.buffer.pop_back()
    }
}
impl<'a, A: 'a, N: ChunkLength<A> + 'a> ExactSizeIterator for Drain<'a, A, N> {}
impl<'a, A: 'a, N: ChunkLength<A> + 'a> FusedIterator for Drain<'a, A, N> {}
pub struct OwnedIter<A, N: ChunkLength<A>> {
    pub(crate) buffer: RingBuffer<A, N>,
}
impl<A, N: ChunkLength<A>> Iterator for OwnedIter<A, N> {
    type Item = A;
    #[inline]
    fn next(&mut self) -> Option<Self::Item> {
        self.buffer.pop_front()
    }
    #[inline]
    #[must_use]
    fn size_hint(&self) -> (usize, Option<usize>) {
        (self.buffer.len(), Some(self.buffer.len()))
    }
}
impl<A, N: ChunkLength<A>> DoubleEndedIterator for OwnedIter<A, N> {
    #[inline]
    fn next_back(&mut self) -> Option<Self::Item> {
        self.buffer.pop_back()
    }
}
impl<A, N: ChunkLength<A>> ExactSizeIterator for OwnedIter<A, N> {}
impl<A, N: ChunkLength<A>> FusedIterator for OwnedIter<A, N> {}