use std::{
    any::type_name,
    ops::{Index, IndexMut},
};

use hashbrown::HashMap;

use super::entity::Ent;

#[derive(Debug, Clone)]
pub struct Entry<T> {
    pub id: Ent,
    pub value: T,
}

#[derive(Debug)]
pub struct Component<T> {
    id_to_index: HashMap<Ent, usize>,
    store: Vec<Entry<T>>,
}

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

impl<T> Index<Ent> for Component<T> {
    type Output = T;

    fn index(&self, index: Ent) -> &Self::Output {
        self.get(index).expect("component not found")
    }
}

impl<T> IndexMut<Ent> for Component<T> {
    fn index_mut(&mut self, index: Ent) -> &mut Self::Output {
        self.get_mut(index).expect("component not found")
    }
}

impl<T> Component<T> {
    pub fn new() -> Self {
        Self {
            id_to_index: HashMap::new(),
            store: Vec::new(),
        }
    }

    pub fn get(&self, id: Ent) -> Option<&T> {
        let index = self.id_to_index.get(&id)?;
        let entry = self.store.get(*index)?;
        Some(&entry.value)
    }

    pub fn get_mut(&mut self, id: Ent) -> Option<&mut T> {
        let index = self.id_to_index.get(&id)?;
        let entry = self.store.get_mut(*index)?;
        Some(&mut entry.value)
    }

    pub fn add(&mut self, id: Ent, data: T) {
        let index = self.store.len();
        let existed = self.id_to_index.insert(id, index);
        assert!(
            existed.is_none(),
            "component {:?}@{} already exists",
            id,
            type_name::<T>()
        );
        let entry = Entry { id, value: data };
        self.store.push(entry);
    }

    pub fn remove(&mut self, id: Ent) -> Option<T> {
        let index = self.id_to_index.remove(&id)?;
        let entry = self.store.swap_remove(index);
        if let Some(dirty) = self.store.get(index) {
            self.id_to_index.insert(dirty.id, index);
        }
        Some(entry.value)
    }

    pub fn iter(&self) -> impl Iterator<Item = &Entry<T>> {
        self.store.iter()
    }

    pub fn iter_mut(&mut self) -> impl Iterator<Item = &mut Entry<T>> {
        self.store.iter_mut()
    }

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

    pub fn is_empty(&self) -> bool {
        self.store.is_empty()
    }

    pub fn contains(&self, id: Ent) -> bool {
        self.id_to_index.contains_key(&id)
    }
}
