use std::hash::Hash;

type FxIndexSet<T> =
    indexmap::set::IndexSet<T, std::hash::BuildHasherDefault<rustc_hash::FxHasher>>;

/// A container meant to be a collection of T with ids from 0 to n-1.
///
/// # Notes
///
/// This structure handles:
/// * Insertion of a new T, returning its id. Either
/// * it was there already, and we modify nothing and return the id.
/// * it wasn't there, so we push it, and we return new id.
/// * Finding an item by its id.
/// * Returning a Vec of T, ordered by id.
#[derive(Debug, Clone)]
pub struct HashMapVec<T: Clone + Eq + Hash> {
    index_set: FxIndexSet<T>,
}

impl<T: Clone + Eq + Hash> Default for HashMapVec<T> {
    fn default() -> Self {
        Self::new()
    }
}

impl<T: Clone + Eq + Hash> HashMapVec<T> {
    /// Constructor.
    pub fn new() -> HashMapVec<T> {
        HashMapVec {
            index_set: FxIndexSet::default(),
        }
    }

    /// Insertion of a new T, returning its id.
    ///
    /// # Note
    ///
    /// Either:
    /// * it was there already, and we modify nothing and return the id.
    /// * it wasn't there, so we push it, and we return new id.
    pub fn push(&mut self, t: T) -> usize {
        let (n, _) = self.index_set.insert_full(t);
        n
    }

    pub fn len(&self) -> usize {
        self.index_set.len()
    }

    pub fn is_empty(&self) -> bool {
        self.index_set.len() == 0
    }

    /// Finding an item by its id.
    pub fn get_val(&self, id: usize) -> &T {
        &self.index_set[id]
    }

    /// Returning a [`Vec`] of T, ordered by id.
    pub fn move_vec(self) -> Vec<T> {
        self.index_set.into_iter().collect()
    }

    /// Returns whether contains id.
    pub fn contains_id(&self, id: usize) -> bool {
        id < self.len()
    }
}
