use std::any::{Any, TypeId};
use std::collections::HashMap;

use optional_typeid::OptionalTypeId;



pub trait Component: Any + Send + Sync {
	fn component_id(&self) -> OptionalTypeId {
		OptionalTypeId::of::<Self>()
	}
}

struct A;
impl Component for A {
	fn component_id(&self) -> OptionalTypeId {
		OptionalTypeId::none()
	}
}
struct B;
impl Component for B {}



pub struct ComponentMap {
	map: HashMap<TypeId, Box<dyn Component>>
}

impl ComponentMap {
	pub fn new() -> ComponentMap {
		ComponentMap {
			map: HashMap::new(),
		}
	}
	pub fn contains_type_id(&self, id: &TypeId) -> bool {
		self.map.contains_key(id)
	}
	#[inline]
	pub fn contains_component<T: Component>(&self) -> bool {
		self.contains_type_id(&TypeId::of::<T>())
	}
	#[inline]
	pub fn insert<T: Component>(&mut self, c: T) -> Result<(), T> {
		let type_id = TypeId::of::<T>();
		if self.contains_type_id(&type_id) {
			return Err(c);
		}

		let Some(id) = c.component_id().into_option() else {return Err(c)};

		debug_assert_eq!(type_id, id);

		self.map.insert(id, Box::new(c));

		Ok(())
	}
	fn raw_get(&self, id: &TypeId) -> Option<&(dyn Any + Send + Sync)> {
		Some(self.map.get(id)?.as_ref())
	}
	fn raw_get_mut(&mut self, id: &TypeId) -> Option<&mut (dyn Any + Send + Sync)> {
		Some(self.map.get_mut(id)?.as_mut())
	}
	#[inline]
	pub fn get<T: Component>(&self) -> Option<&T> {
		self.raw_get(&TypeId::of::<T>())?.downcast_ref::<T>()
	}
	#[inline]
	pub fn get_mut<T: Component>(&mut self) -> Option<&mut T> {
		self.raw_get_mut(&TypeId::of::<T>())?.downcast_mut::<T>()
	}
}



fn main() {
	let mut map = ComponentMap::new();
	let _ = map.insert(A);
	let _ = map.insert(B);
	assert!(map.get::<A>().is_none());
	assert!(map.get::<B>().is_some());
}
