#![feature(generators)]
#![feature(never_type)]
#![feature(prelude_import)]
#![feature(unsafe_block_in_unsafe_fn)]
#![warn(unsafe_op_in_unsafe_fn)]

#[prelude_import]
#[allow(unused_imports)]
use drone_core::prelude::*;

use core::{
    pin::Pin,
    sync::atomic::{AtomicUsize, Ordering::*},
    task::{Context, Poll, RawWaker, RawWakerVTable, Waker},
};
use drone_core::{sync::spsc::oneshot, thr};
use futures::prelude::*;

static mut THREADS: [Thr; 1] = [Thr::new(0)];

thr! {
    array => THREADS;
    thread => Thr {};
    local => ThrLocal {};
}

struct Counter(AtomicUsize);

impl Counter {
    fn to_waker(&'static self) -> Waker {
        unsafe fn clone(counter: *const ()) -> RawWaker {
            RawWaker::new(counter, &VTABLE)
        }
        unsafe fn wake(counter: *const ()) {
            unsafe { (*(counter as *const Counter)).0.fetch_add(1, Relaxed) };
        }
        static VTABLE: RawWakerVTable = RawWakerVTable::new(clone, wake, wake, drop);
        unsafe { Waker::from_raw(RawWaker::new(self as *const _ as *const (), &VTABLE)) }
    }
}

#[test]
fn test_await() {
    static COUNTER: Counter = Counter(AtomicUsize::new(0));
    let waker = COUNTER.to_waker();
    let mut cx = Context::from_waker(&waker);
    let (tx, rx) = oneshot::channel::<usize>();
    let mut fut = Box::pin(async {
        let number = rx.await?;
        Ok::<usize, oneshot::Canceled>(number + 1)
    });
    assert_eq!(tx.send(1), Ok(()));
    assert_eq!(Pin::new(&mut fut).poll(&mut cx), Poll::Ready(Ok(2)));
}

#[test]
fn test_nested() {
    static COUNTER: Counter = Counter(AtomicUsize::new(0));
    let waker = COUNTER.to_waker();
    let mut cx = Context::from_waker(&waker);
    let (tx, rx) = oneshot::channel::<usize>();
    let mut fut = Box::pin(async {
        Box::pin(async {
            async {
                let number = rx.await?;
                Ok::<usize, oneshot::Canceled>(number + 1)
            }
            .await
        })
        .await
    });
    assert_eq!(Pin::new(&mut fut).poll(&mut cx), Poll::Pending);
    assert_eq!(Pin::new(&mut fut).poll(&mut cx), Poll::Pending);
    assert_eq!(COUNTER.0.load(Relaxed), 0);
    assert_eq!(tx.send(1), Ok(()));
    assert_eq!(COUNTER.0.load(Relaxed), 1);
    assert_eq!(Pin::new(&mut fut).poll(&mut cx), Poll::Ready(Ok(2)));
    assert_eq!(COUNTER.0.load(Relaxed), 1);
}
