#![forbid(unsafe_code)]

use ::polonius_the_crab::prelude::*;

trait LendingIteratorItem<'n, Bound = &'n mut Self> {
    type T;
}

trait LendingIterator : for<'n> LendingIteratorItem<'n> {
    fn next (self: &'_ mut Self)
      -> Option< <Self as LendingIteratorItem<'_>>::T >
    ;

    fn filter<P> (
        self,
        predicate: P,
    ) -> Filter<Self, P>
    where
        Self : Sized,
        P : FnMut(&'_ <Self as LendingIteratorItem<'_>>::T) -> bool,
    {
        Filter {
            iter: self,
            predicate,
        }
    }
}

pub
struct Filter<I, P> {
    iter: I,
    predicate: P,
}

impl<'n, I, P> LendingIteratorItem<'n> for Filter<I, P>
where
    I : LendingIterator,
    P : FnMut(&'_ <I as LendingIteratorItem<'_>>::T) -> bool,
{
    type T = <I as LendingIteratorItem<'n>>::T;
}

impl<I, P> LendingIterator for Filter<I, P>
where
    I : LendingIterator,
    P : FnMut(&'_ <I as LendingIteratorItem<'_>>::T) -> bool,
{
    fn next (self: &'_ mut Self)
      -> Option< <Self as LendingIteratorItem<'_>>::T >
    {
        let mut iter = &mut self.iter;
        polonius_loop! {
            |iter| -> Option<<Self as LendingIteratorItem<'polonius>>::T> {
                match iter.next() {
                    | Some(item) if !(self.predicate)(&item) => {
                        polonius_continue!()
                    },
                    | mb_item => {
                        polonius_return!(mb_item)
                    },
                }
            }
        }
    }
}

struct WindowsMut<Slice, const WIDTH: usize> {
    slice: Slice,
    start: usize,
}

impl<'slice, T, const WIDTH: usize> WindowsMut<&'slice mut [T], WIDTH> {
    fn new (
        slice: &'slice mut [T],
    ) -> WindowsMut<&'slice mut [T], WIDTH>
    where
        WindowsMut<&'slice mut [T], WIDTH>
            : for<'n> LendingIteratorItem<'n, T = &'n mut [T; WIDTH]>
        ,
    {
        return Self { slice, start: 0 };
        // where:
        impl<'next, 'slice, T, const WIDTH: usize>
            LendingIteratorItem<'next>
        for
            WindowsMut<&'slice mut [T], WIDTH>
        {
            type T = &'next mut [T; WIDTH];
        }

        impl<'slice, T, const WIDTH: usize>
            LendingIterator
        for
            WindowsMut<&'slice mut [T], WIDTH>
        {
            fn next<'next> (
                self: &'next mut WindowsMut<&'slice mut [T], WIDTH>,
            ) -> Option<
                    &'next mut [T; WIDTH],
                >
            {
                let slice =
                    self.slice
                        .get_mut(self.start ..)?
                        .get_mut(.. WIDTH)?
                ;
                self.start += 1;
                Some(slice.try_into().unwrap())
            }
        }
    }
}

#[test]
fn test_windows_mut ()
{
    let slice = &mut [42, 0, 1, 2, 3, 4][..];
    let mut windows =
        WindowsMut::<_, 2>::new(slice)
            .filter(|&&mut [x, _]| x != 42)
    ;
    while let Some(&mut [x, ref mut y]) = windows.next() {
        *y += x;
    }
    assert_eq!(slice.last().unwrap(), &10);
}