use std::any::TypeId;
use std::collections::HashMap;
use super::component::Component;

#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct EntityId(u64);

impl EntityId {
    pub fn new(id: u64) -> Self {
        EntityId(id)
    }
    
    pub fn as_u64(&self) -> u64 {
        self.0
    }
}

pub struct Entity {
    pub id: EntityId,
    components: HashMap<TypeId, Box<dyn Component>>,
    pub active: bool,
}

impl Entity {
    pub fn new(id: EntityId) -> Self {
        Self {
            id,
            components: HashMap::new(),
            active: true,
        }
    }
    
    pub fn set_active(&mut self, active: bool) {
        self.active = active;
    }
    
    pub fn is_active(&self) -> bool {
        self.active
    }
    
    pub fn add_component<T: Component + 'static>(&mut self, component: T) {
        let type_id = TypeId::of::<T>();
        self.components.insert(type_id, Box::new(component));
    }
    
    pub fn add_component_boxed(&mut self, component: Box<dyn Component>) {
        let type_id = (*component).as_any().type_id();
        self.components.insert(type_id, component);
    }
    
    pub fn get_component<T: Component + 'static>(&self) -> Option<&T> {
        let type_id = TypeId::of::<T>();
        self.components.get(&type_id)?
            .as_any()
            .downcast_ref::<T>()
    }
    
    pub fn get_component_mut<T: Component + 'static>(&mut self) -> Option<&mut T> {
        let type_id = TypeId::of::<T>();
        self.components.get_mut(&type_id)?
            .as_any_mut()
            .downcast_mut::<T>()
    }
    
    pub fn remove_component<T: Component + 'static>(&mut self) -> bool {
        let type_id = TypeId::of::<T>();
        self.components.remove(&type_id).is_some()
    }
    
    pub fn has_component<T: Component + 'static>(&self) -> bool {
        let type_id = TypeId::of::<T>();
        self.components.contains_key(&type_id)
    }
    
    pub fn component_types(&self) -> Vec<&'static str> {
        self.components.values()
            .map(|component| component.type_name())
            .collect()
    }
    
    pub fn component_count(&self) -> usize {
        self.components.len()
    }
    
    pub fn clear_components(&mut self) {
        self.components.clear();
    }
}