use std::sync::atomic::AtomicUsize;
use std::sync::atomic::Ordering::SeqCst;
use std::sync::Arc;
use tokio::sync::Notify;

/// NotifyCounter is a simple increment/decrement that notifies listeners waiting
/// on `Notify::notified()` when the inner value is changed. Similar to an async CondVar.
#[derive(Default, Debug, Clone)]
pub(crate) struct NotifyCounter {
    inner: Arc<Inner>,
}

#[derive(Default, Debug)]
struct Inner {
    value: AtomicUsize,
    notify: Notify,
}

impl NotifyCounter {
    /// Increment the counter by one and notify any listeners
    pub fn inc(&self) {
        self.inner.value.fetch_add(1, SeqCst);
        self.inner.notify.notify_waiters();
    }

    /// Decrement the counter by one and notify any listeners
    pub fn dec(&self) {
        self.inner.value.fetch_sub(1, SeqCst);
        self.inner.notify.notify_waiters();
    }

    /// Waits until the value has changed
    pub async fn notified(&self) {
        self.inner.notify.notified().await
    }

    /// Check if the value provided equals the inner value
    pub fn equals(&self, value: usize) -> bool {
        self.inner.value.load(SeqCst) == value
    }
}
