use std::any::{Any, TypeId};
use std::cell::{Ref, RefCell, RefMut};
use crate::people::context_extension::{ContextPeopleExt, ContextPeopleExtInternal};
use crate::people::index::{BxIndex, Index};
use crate::people::methods::Methods;
use crate::people::{HashValueType, InitializationList};
use crate::{
Context, HashMap, HashSet, HashSetExt, IxaError, PersonId, PersonProperty,
PersonPropertyChangeEvent,
};
type ContextCallback = dyn FnOnce(&mut Context);
pub(super) struct StoredPeopleProperties {
is_required: bool,
values: Box<dyn Any>,
}
impl StoredPeopleProperties {
fn new<T: PersonProperty>() -> Self {
StoredPeopleProperties {
is_required: T::is_required(),
values: Box::<Vec<Option<T::Value>>>::default(),
}
}
}
pub(super) struct PeopleData {
pub(super) is_initializing: bool,
pub(super) current_population: usize,
pub(super) methods: RefCell<HashMap<TypeId, Methods>>,
pub(super) properties_map: RefCell<HashMap<TypeId, StoredPeopleProperties>>,
pub(super) registered_properties: RefCell<HashSet<TypeId>>,
pub(super) dependency_map: RefCell<HashMap<TypeId, Vec<Box<dyn PersonPropertyHolder>>>>,
pub(super) property_indexes: RefCell<HashMap<TypeId, BxIndex>>,
pub(super) people_types: RefCell<HashMap<String, TypeId>>,
}
impl PeopleData {
pub(crate) fn get_people_for_id_hash(
&self,
type_id: TypeId,
hash: HashValueType,
) -> (bool, Option<Ref<HashSet<PersonId>>>) {
let mut is_indexed = false;
let people_for_id_hash = Ref::filter_map(self.property_indexes.borrow(), |map| {
map.get(&type_id).and_then(|index| {
if index.is_indexed() {
is_indexed = true;
index.get_with_hash(hash)
} else {
None
}
})
})
.ok();
(is_indexed, people_for_id_hash)
}
pub(super) fn remove_person_if_indexed<T: PersonProperty>(
&self,
value: T::CanonicalValue,
person_id: PersonId,
) {
let mut indexes = self.property_indexes.borrow_mut();
indexes.entry(T::type_id()).and_modify(|index| {
if index.is_indexed() {
let hash = T::hash_property_value(&value);
index.remove_person_with_hash(hash, person_id);
}
});
}
pub(super) fn add_person_if_indexed<T: PersonProperty>(
&self,
value: T::CanonicalValue,
person_id: PersonId,
) {
let mut indexes = self.property_indexes.borrow_mut();
indexes.entry(T::type_id()).and_modify(|index| {
if index.is_indexed() {
let index = index.as_any_mut().downcast_mut::<Index<T>>().unwrap();
index.add_person(&value, person_id);
}
});
}
pub(super) fn register_index<T: PersonProperty>(&self) {
let mut indexes = self.property_indexes.borrow_mut();
indexes
.entry(T::type_id())
.or_insert_with(|| Index::<T>::new());
}
pub(super) fn index_unindexed_people_for_type_id(&self, context: &Context, type_id: TypeId) {
let mut indexes = self.property_indexes.borrow_mut();
let Some(index) = indexes.get_mut(&type_id) else {
return;
};
index.index_unindexed_people(context);
}
}
#[doc(hidden)]
pub trait PersonPropertyHolder {
fn dependency_changed(
&self,
context: &mut Context,
person: PersonId,
callback_vec: &mut Vec<Box<ContextCallback>>,
);
fn is_derived(&self) -> bool;
fn dependencies(&self) -> Vec<Box<dyn PersonPropertyHolder>>;
fn non_derived_dependencies(&self) -> Vec<TypeId>;
fn collect_non_derived_dependencies(&self, result: &mut HashSet<TypeId>);
fn property_type_id(&self) -> TypeId;
}
impl<T> PersonPropertyHolder for T
where
T: PersonProperty,
{
fn dependency_changed(
&self,
context: &mut Context,
person: PersonId,
callback_vec: &mut Vec<Box<ContextCallback>>,
) {
let previous = context.get_person_property(person, T::get_instance());
context.remove_from_index_maybe(person, T::get_instance());
callback_vec.push(Box::new(move |ctx| {
let current = ctx.get_person_property(person, T::get_instance());
let change_event: PersonPropertyChangeEvent<T> = PersonPropertyChangeEvent {
person_id: person,
current,
previous,
};
ctx.add_to_index_maybe(person, T::get_instance());
ctx.emit_event(change_event);
}));
}
fn is_derived(&self) -> bool {
T::is_derived()
}
fn dependencies(&self) -> Vec<Box<dyn PersonPropertyHolder>> {
T::dependencies()
}
fn property_type_id(&self) -> TypeId {
T::type_id()
}
fn non_derived_dependencies(&self) -> Vec<TypeId> {
let mut result = HashSet::new();
self.collect_non_derived_dependencies(&mut result);
result.into_iter().collect()
}
fn collect_non_derived_dependencies(&self, result: &mut HashSet<TypeId>) {
if !self.is_derived() {
return;
}
for dependency in self.dependencies() {
if dependency.is_derived() {
dependency.collect_non_derived_dependencies(result);
} else {
result.insert(dependency.property_type_id());
}
}
}
}
impl PeopleData {
#[allow(unused)]
pub(super) fn with_index(
&self,
type_id: TypeId,
value_hash: HashValueType,
callback: &mut dyn FnMut(&HashSet<PersonId>),
) -> Result<(), ()> {
if let Some(index) = self.property_indexes.borrow().get(&type_id) {
if let Some(people_set) = index.get_with_hash(value_hash) {
(callback)(people_set);
} else {
let empty = HashSet::default();
(callback)(&empty);
}
return Ok(());
}
Err(())
}
pub(super) fn add_person(&mut self) -> PersonId {
let id = self.current_population;
self.current_population += 1;
PersonId(id)
}
#[allow(clippy::needless_pass_by_value)]
pub(super) fn get_person_property_ref<T: PersonProperty>(
&self,
person: PersonId,
_property: T,
) -> RefMut<Option<T::Value>> {
let properties_map = self.properties_map.borrow_mut();
let index = person.0;
RefMut::map(properties_map, |properties_map| {
let properties = properties_map
.entry(TypeId::of::<T>())
.or_insert_with(|| StoredPeopleProperties::new::<T>());
let values: &mut Vec<Option<T::Value>> = properties
.values
.downcast_mut()
.expect("Type mismatch in properties_map");
if index >= values.len() {
values.resize(index + 1, None);
}
&mut values[index]
})
}
#[allow(clippy::needless_pass_by_value)]
pub(super) fn set_person_property<T: PersonProperty>(
&self,
person_id: PersonId,
property: T,
value: T::Value,
) {
let mut property_ref = self.get_person_property_ref(person_id, property);
*property_ref = Some(value);
}
pub(super) fn set_property_indexed<T: PersonProperty>(&self, is_indexed: bool, property: T) {
self.property_indexes
.borrow_mut()
.entry(property.property_type_id())
.or_insert_with(|| Index::<T>::new())
.set_indexed(is_indexed);
}
pub(super) fn set_property_indexed_by_type_id(&self, is_indexed: bool, type_id: TypeId) {
self.property_indexes
.borrow_mut()
.get_mut(&type_id)
.expect("Index instance not found for type")
.set_indexed(is_indexed);
}
pub(super) fn get_index_ref_mut(&self, t: TypeId) -> Option<RefMut<BxIndex>> {
let index_map = self.property_indexes.borrow_mut();
if index_map.contains_key(&t) {
Some(RefMut::map(index_map, |map| map.get_mut(&t).unwrap()))
} else {
None
}
}
pub(super) fn people_iterator(&self) -> PeopleIterator {
PeopleIterator {
population: self.current_population,
person_id: 0,
}
}
pub(super) fn check_initialization_list<T: InitializationList>(
&self,
initialization: &T,
) -> Result<(), IxaError> {
let properties_map = self.properties_map.borrow();
for (t, property) in properties_map.iter() {
if property.is_required && !initialization.has_property(*t) {
return Err(IxaError::IxaError(String::from("Missing initial value")));
}
}
Ok(())
}
pub(super) fn get_methods(&self, t: TypeId) -> RefMut<'_, Methods> {
let x = self.methods.borrow_mut();
RefMut::map(x, |a| a.get_mut(&t).unwrap())
}
}
pub(super) struct PeopleIterator {
population: usize,
person_id: usize,
}
impl Iterator for PeopleIterator {
type Item = PersonId;
fn next(&mut self) -> Option<Self::Item> {
let ret = if self.person_id < self.population {
Some(PersonId(self.person_id))
} else {
None
};
self.person_id += 1;
ret
}
}