use std::collections::VecDeque;
use std::task::{RawWaker, RawWakerVTable, Waker};

use dambi::LocalCell;

use crate::SlotId;

const _: () = assert!(std::mem::size_of::<SlotId>() == std::mem::size_of::<*const ()>());

thread_local! {
    static READY_QUEUE: LocalCell<VecDeque<SlotId>> =
        const { LocalCell::new(VecDeque::new()) };
}

pub(crate) struct WakeQueue;

impl WakeQueue {
    #[inline]
    pub(crate) fn push(id: SlotId) {
        READY_QUEUE.with(|q| q.get_mut().push_back(id));
    }

    #[inline]
    pub(crate) fn next() -> Option<SlotId> {
        READY_QUEUE.with(|q| q.get_mut().pop_front())
    }

    #[inline]
    pub(crate) fn is_pending() -> bool {
        READY_QUEUE.with(|q| !q.get().is_empty())
    }

    #[inline]
    pub(crate) fn reserve(cap: usize) {
        READY_QUEUE.with(|q| q.get_mut().reserve(cap));
    }

    pub(crate) fn make_waker(id: SlotId) -> Waker {
        let data: *const () = unsafe { std::mem::transmute(id) };
        let raw = RawWaker::new(data, &Self::VTABLE);

        unsafe { Waker::from_raw(raw) }
    }

    const VTABLE: RawWakerVTable = RawWakerVTable::new(
        Self::vt_clone,
        Self::vt_wake,
        Self::vt_wake_by_ref,
        Self::vt_drop,
    );

    unsafe fn vt_clone(data: *const ()) -> RawWaker {
        RawWaker::new(data, &Self::VTABLE)
    }

    unsafe fn vt_wake(data: *const ()) {
        let id: SlotId = unsafe { std::mem::transmute(data) };
        Self::push(id);
    }

    unsafe fn vt_wake_by_ref(data: *const ()) {
        let id: SlotId = unsafe { std::mem::transmute(data) };
        Self::push(id);
    }

    unsafe fn vt_drop(_data: *const ()) {}
}
