extern crate alloc;

use std::time::Duration;

use mutringbuf::iterators::async_iterators::AsyncIterator;

use crate::common_def;

common_def!(buf);

#[tokio::test]
async fn test_full() {
    #[cfg(not(feature = "vmem"))]
    let buf = mutringbuf::AsyncStackRB::from([0; BUFFER_SIZE]);
    #[cfg(feature = "vmem")]
    let buf = mutringbuf::AsyncHeapRB::from(vec![0; BUFFER_SIZE]);

    let (mut as_prod, mut as_cons) = buf.split();

    let slice: Vec<i32> = (0..BUFFER_SIZE as i32 - 1).collect();
    as_prod.push_slice(&slice).await;

    let clone = slice.clone();
    let pusher = tokio::spawn(async move {
        as_prod.push_slice(&clone).await;
    });

    let popper = tokio::spawn(async move {
        tokio::time::sleep(Duration::from_secs(2)).await;
        let _ = as_cons.peek_available().await;
        unsafe {
            as_cons.advance(BUFFER_SIZE - 1);
        }
        println!("ADVANCED");
    });

    let _ = tokio::join!(pusher, popper);
}

#[tokio::test]
async fn test_push_work_pop_single_and_slice() {
    #[cfg(not(feature = "vmem"))]
    let buf = mutringbuf::AsyncStackRB::from([0; BUFFER_SIZE]);
    #[cfg(feature = "vmem")]
    let buf = mutringbuf::AsyncHeapRB::from(vec![0; BUFFER_SIZE]);

    let (mut as_prod, mut as_work, mut as_cons) = buf.split_mut();

    as_prod.push(1).await;

    if let Some(res) = as_work.get_workable().await {
        *res += 1;
        unsafe {
            as_work.advance(1);
        }
    }

    let res = as_cons.peek_ref().await.unwrap();
    assert_eq!(res, &2);
    unsafe {
        as_cons.advance(1);
    }

    let slice: Vec<i32> = (0..BUFFER_SIZE as i32 / 2).collect();
    as_prod.push_slice(&slice).await;

    #[cfg(not(feature = "vmem"))]
    if let Some((h, t)) = as_work.get_workable_slice_avail().await {
        let len = h.len() + t.len();

        for x in h.iter_mut().chain(t) {
            *x += 1;
        }

        unsafe {
            as_work.advance(len);
        }
    }

    #[cfg(feature = "vmem")]
    if let Some(r) = as_work.get_workable_slice_avail().await {
        let len = r.len();

        for x in r {
            *x += 1;
        }

        unsafe {
            as_work.advance(len);
        }
    }

    #[cfg(not(feature = "vmem"))]
    if let Some((h, t)) = as_cons.peek_available().await {
        for (x, y) in h.iter().chain(t).zip(&slice) {
            assert_eq!(*x, y + 1);
        }
    }

    #[cfg(feature = "vmem")]
    if let Some(r) = as_cons.peek_available().await {
        for (x, y) in r.iter().zip(&slice) {
            assert_eq!(*x, y + 1);
        }
    }

    drop(as_prod);
    assert_eq!(as_cons.alive_iters(), 2);
    drop(as_work);
    assert_eq!(as_cons.alive_iters(), 1);
}
