use std::collections::HashMap;

use async_std::sync::Arc;

use crate::Error;

/// Container for storing specific services
///
/// Stores all services of the same type (trait, int, ...) into an HashMap container
/// wrapped within async Arc
pub struct Container<T> {
    services: HashMap<String, Arc<T>>,
}

impl<T> Container<T> {
    /// Creates a new Container
    ///
    /// ```
    /// use icee_container_rs::Container;
    ///
    /// let mut container = Container::<usize>::new();
    /// ```
    pub fn new() -> Self {
        Container {
            services: HashMap::new(),
        }
    }

    /// Checks whether or not a given field is registered
    ///
    /// ```
    /// use icee_container_rs::Container;
    ///
    /// let container = Container::<usize>::new();
    /// let exists = container.has("echo");
    /// # assert_eq!(exists, false);
    /// ```
    pub fn has(&self, name: &str) -> bool {
        self.services.contains_key(name)
    }

    /// Receives stored field from container
    ///
    /// ```
    /// use icee_container_rs::Container;
    ///
    /// let container = Container::<usize>::new();
    /// let value = container.get("echo");
    /// # assert_eq!(value.is_err(), true);
    /// ```
    pub fn get(&self, name: &str) -> Result<Arc<T>, Error> {
        if !self.has(name) {
            return Err(Error::NotFound(name.to_string()));
        }

        Ok(self.services.get(name).unwrap().clone())
    }

    /// Stores service into container
    ///
    /// ```
    /// use icee_container_rs::Container;
    ///
    /// let mut container = Container::<usize>::new();
    /// container.set("echo", 666);
    /// ```
    pub fn set(&mut self, name: &str, service: T) {
        self.services.insert(String::from(name), Arc::new(service));
    }
}

impl<T> Default for Container<T> {
    fn default() -> Self {
        Self::new()
    }
}
