use std::collections::HashSet;

use crate::{counter::Counter, hash_index::HashIndex};

#[derive(Debug, Default, Clone, PartialEq, Eq)]
pub struct RemovableCounter<T: HashIndex> {
    released_set: HashSet<T>,
    released_vec: Vec<T>,
    counter: Counter<T>,
}
impl<T: HashIndex> RemovableCounter<T> {
    pub fn new() -> Self {
        Self::default()
    }

    pub unsafe fn new_unsafe(
        released_set: HashSet<T>,
        released_vec: Vec<T>,
        counter: Counter<T>,
    ) -> Self {
        Self {
            released_set,
            released_vec,
            counter,
        }
    }

    pub fn get_count(&self) -> &T {
        self.counter.get_count()
    }

    pub fn released_set(&self) -> &HashSet<T> {
        &self.released_set
    }

    pub fn released_vec(&self) -> &Vec<T> {
        &self.released_vec
    }

    pub fn free(&mut self, index: T) -> Result<(), T> {
        if &index < self.counter.get_count() && !self.released_set.contains(&index) {
            self.released_set.insert(index.clone());
            self.released_vec.push(index);
            Ok(())
        } else {
            Err(index)
        }
    }

    pub unsafe fn free_unchecked(&mut self, index: T) -> Result<(), T> {
        if self.released_set.contains(&index) {
            self.released_set.insert(index.clone());
            self.released_vec.push(index);
            Ok(())
        } else {
            Err(index)
        }
    }
}
impl<T: HashIndex> Iterator for RemovableCounter<T> {
    type Item = T;
    fn next(&mut self) -> Option<Self::Item> {
        if let Some(released) = self.released_vec.pop() {
            self.released_set.remove(&released);
            Some(released)
        } else {
            self.counter.next()
        }
    }
}
