use alloc::collections::VecDeque;
use super::*;
use crate::thread;
use alloc::sync::Arc;

#[derive(Default)]
pub struct Condvar {
    wait_queue: SpinNoIrqLock<VecDeque<Arc<thread::Thread>>>,
}

impl Condvar {
    pub fn new() -> Self {
        Condvar::default()
    }

    pub fn _wait(&self) {
        self.add_to_wait_queue();
        thread::park();
    }

    pub fn wait_any(condvars: &[&Condvar]) {
        let token = Arc::new(thread::current());
        for condvar in condvars {
            condvar.wait_queue.lock().push_back(token.clone());
        }
        thread::park();
    }

    pub fn add_to_wait_queue(&self) {
        self.wait_queue.lock().push_back(Arc::new(thread::current()));
    }

    pub fn wait<'a, T, S>(&self, guard: MutexGuard<'a, T, S>) -> MutexGuard<'a, T, S>
        where S: MutexSupport
    {
        let mutex = guard.mutex;
        drop(guard);
        self._wait();
        mutex.lock()
    }

    pub fn notify_one(&self) {
        if let Some(t) = self.wait_queue.lock().pop_front() {
            t.unpark();
        }
    }
    pub fn notify_all(&self) {
        while let Some(t) = self.wait_queue.lock().pop_front() {
            t.unpark();
        }
    }
    /// Notify up to `n` waiters.
    /// Return the number of waiters that were woken up.
    pub fn notify_n(&self, mut n: usize) -> usize {
        let mut count  = 0;
        while count < n {
            if let Some(t) = self.wait_queue.lock().pop_front() {
                t.unpark();
                count += 1;
            } else {
                break;
            }
        }
        count
    }
    pub fn _clear(&self) {
        self.wait_queue.lock().clear();
    }
}