use ifc_model::guid::Guid;
use ifc_model::{Entity, EntityId, Transaction, Value};
use crate::authoring::{invalid, SpatialAuthoringResult};
use crate::relation::slots::{
RelSlots, CONNECTS_ELEMENTS, CONNECTS_WITH_REALIZING, INTERFERES_ELEMENTS,
};
pub(super) fn check_realizing(realizing: &[EntityId]) -> SpatialAuthoringResult<()> {
if realizing.is_empty() {
return Err(invalid(
CONNECTS_WITH_REALIZING.type_name,
"RealizingElements",
"empty",
));
}
Ok(())
}
pub(super) fn distinct(
rel: RelSlots,
global_id: &str,
relating: EntityId,
related: EntityId,
) -> SpatialAuthoringResult<()> {
if Guid::parse(global_id).is_none() {
return Err(invalid(rel.type_name, "GlobalId", global_id));
}
if relating == related {
return Err(invalid(
rel.type_name,
"RelatedElement",
"an element cannot connect to itself",
));
}
Ok(())
}
fn pair(
tx: &mut Transaction,
rel: RelSlots,
global_id: &str,
relating: EntityId,
related: EntityId,
width: usize,
) -> SpatialAuthoringResult<EntityId> {
distinct(rel, global_id, relating, related)?;
let mut attributes = vec![Value::Null; width];
attributes[0] = Value::Text(global_id.into());
attributes[rel.relating] = Value::Ref(relating);
attributes[rel.related] = Value::Ref(related);
Ok(tx.create(Entity::new(rel.type_name, attributes)))
}
pub fn connect_elements(
tx: &mut Transaction,
global_id: &str,
relating: EntityId,
related: EntityId,
) -> SpatialAuthoringResult<EntityId> {
pair(tx, CONNECTS_ELEMENTS, global_id, relating, related, 7)
}
pub fn connect_with_realizing_elements(
tx: &mut Transaction,
global_id: &str,
relating: EntityId,
related: EntityId,
realizing: &[EntityId],
) -> SpatialAuthoringResult<EntityId> {
check_realizing(realizing)?;
let id = pair(tx, CONNECTS_WITH_REALIZING, global_id, relating, related, 8)?;
tx.set_attribute(
id,
REALIZING_SLOT,
Value::List(realizing.iter().copied().map(Value::Ref).collect()),
);
Ok(id)
}
const REALIZING_SLOT: usize = 7;
pub fn interfere_elements(
tx: &mut Transaction,
global_id: &str,
relating: EntityId,
related: EntityId,
implied_order: Option<bool>,
) -> SpatialAuthoringResult<EntityId> {
let id = pair(tx, INTERFERES_ELEMENTS, global_id, relating, related, 10)?;
tx.set_attribute(
id,
IMPLIED_ORDER_SLOT,
implied_order.map_or(Value::LogicalUnknown, Value::Bool),
);
Ok(id)
}
const IMPLIED_ORDER_SLOT: usize = 8;