use std::collections::hash_set;
use std::collections::HashSet;
use std::hash::Hash;
use std::iter::FromIterator;
use std::ops::Deref;
use std::slice;
use std::sync::Arc;

pub struct HashSetShallowCloneItems<T> {
    items: Option<Arc<Vec<T>>>,
}

impl<T> HashSetShallowCloneItems<T> {
    pub fn to_vec(&self) -> Vec<T>
    where
        T: Clone,
    {
        match &self.items {
            Some(items) => items.deref().clone(),
            None => Vec::new(),
        }
    }
}

impl<'a, T> IntoIterator for &'a HashSetShallowCloneItems<T> {
    type Item = &'a T;
    type IntoIter = slice::Iter<'a, T>;

    fn into_iter(self) -> slice::Iter<'a, T> {
        match self.items {
            Some(ref vec) => vec.iter(),
            None => [].iter(),
        }
    }
}

#[derive(Default, Debug, Eq, PartialEq)]
pub struct HashSetShallowClone<T: Hash + Eq + Clone> {
    set: HashSet<T>,
    // This field is Arc because tasks need to be Sync in tokio 0.2
    // TODO: make it Rc
    items: Option<Arc<Vec<T>>>,
}

impl<T: Hash + Eq + Clone> HashSetShallowClone<T> {
    pub fn new() -> Self {
        HashSetShallowClone {
            set: HashSet::new(),
            items: None,
        }
    }

    pub fn items(&mut self) -> HashSetShallowCloneItems<T> {
        // TODO: store delta and update shallow map
        if let None = self.items {
            if self.set.is_empty() {
                // Avoid allocation
                return HashSetShallowCloneItems { items: None };
            }

            self.items = Some(Arc::new(self.set.iter().cloned().collect()));
        }
        HashSetShallowCloneItems {
            items: Some(self.items.as_mut().unwrap().clone()),
        }
    }

    pub fn get(&self, value: &T) -> Option<&T> {
        self.set.get(value)
    }

    pub fn remove(&mut self, item: &T) -> bool {
        let removed = self.set.remove(item);
        if removed {
            self.items.take();
        }
        removed
    }

    pub fn insert(&mut self, value: T) -> bool {
        let inserted = self.set.insert(value);
        if inserted {
            self.items.take();
        }
        inserted
    }

    pub fn _iter(&self) -> hash_set::Iter<T> {
        self.set.iter()
    }
}

impl<T: Clone + Eq + Hash> FromIterator<T> for HashSetShallowClone<T> {
    fn from_iter<I: IntoIterator<Item = T>>(iter: I) -> Self {
        HashSetShallowClone {
            set: HashSet::from_iter(iter),
            items: None,
        }
    }
}

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

    #[test]
    fn insert_updates() {
        let mut s = HashSetShallowClone::new();
        s.insert(10);
        let mut items = s.items().to_vec();
        items.sort();
        assert_eq!(&[10], &items[..]);

        s.insert(20);
        let mut items = s.items().to_vec();
        items.sort();
        assert_eq!(&[10, 20], &items[..]);
    }

    #[test]
    fn remove_updates() {
        let mut s = HashSetShallowClone::new();
        s.insert(10);
        let mut items = s.items().to_vec();
        items.sort();
        assert_eq!(&[10], &items[..]);

        s.insert(20);
        let mut items = s.items().to_vec();
        items.sort();
        assert_eq!(&[10, 20], &items[..]);

        s.remove(&10);
        let mut items = s.items().to_vec();
        items.sort();
        assert_eq!(&[20], &items[..]);
    }
}
