#[derive(Debug)]
pub struct CycleQueue<T> {
    capacity: usize,
    array: Vec<Option<T>>,
    head: usize,
    tail: usize,
}

impl<T: ToOwned<Owned = T>> CycleQueue<T> {
    pub fn new(count: usize) -> Self {
        let capacity = count + 1;
        let mut array = Vec::with_capacity(capacity.to_owned());
        for _ in 0..capacity {
            array.push(None);
        }
        Self {
            capacity,
            array,
            head: 0,
            tail: 0,
        }
    }

    fn uidx(&self, i: usize) -> usize {
        if i < self.capacity {
            i
        } else {
            i - self.capacity
        }
    }

    pub fn is_empty(&self) -> bool {
        self.head == self.tail
    }

    pub fn is_full(&self) -> bool {
        self.uidx(self.tail + 1) == self.head
    }

    pub fn pushback(&mut self, item: T) -> Option<T> {
        let cell = self.array.get_mut(self.tail).unwrap();
        *cell = Some(item);
        let is_full = self.is_full();
        self.tail = self.uidx(self.tail + 1);
        if is_full {
            let cell = self.array.get_mut(self.head).unwrap();
            let v = cell.as_ref().map(|e| e.to_owned());
            *cell = None;
            self.head = self.uidx(self.head + 1);
            v
        } else {
            None
        }
    }

    pub fn push(&mut self, item: T) {
        let cell = self.array.get_mut(self.tail).unwrap();
        *cell = Some(item);
        let is_full = self.is_full();
        self.tail = self.uidx(self.tail + 1);
        if is_full {
            let cell = self.array.get_mut(self.head).unwrap();
            *cell = None;
            self.head = self.uidx(self.head + 1);
        }
    }

    pub fn remove_front(&mut self) -> Option<T> {
        if self.is_empty() {
            None
        } else {
            let cell = self.array.get_mut(self.head).unwrap();
            let v = cell.as_ref().map(|e| e.to_owned());
            *cell = None;
            self.head = self.uidx(self.head + 1);
            v
        }
    }

    pub fn len(&self) -> usize {
        if self.tail < self.head {
            self.capacity + self.tail - self.head
        } else {
            self.tail - self.head
        }
    }

    pub fn seek(&self) -> Option<&T> {
        if self.is_empty() {
            return None;
        }
        self.array.get(self.head).unwrap().as_ref()
    }

    pub fn get(&self, index: usize) -> Option<&T> {
        if self.is_empty() {
            return None;
        }
        let idx = self.uidx(self.head + index);
        self.array.get(idx).unwrap().as_ref()
    }
}

#[cfg(test)]
mod tests {
    use super::CycleQueue;

    #[test]
    fn iter() {
        let mut queue: CycleQueue<u32> = CycleQueue::new(50);
        for i in 0..151 {
            queue.push(i);
        }
        assert_eq!(queue.remove_front(), Some(101));
        for i in 0..queue.len() {
            let v = queue.get(i);
            println!("item: {:?}", &v);
        }
    }
}
