use ifc_model::guid::Guid;
use ifc_model::{Entity, EntityId, Transaction, Value};
use crate::authoring::{invalid, SpatialAuthoringResult};
use crate::relation::slots::{
RelSlots, ADHERES_TO_ELEMENT, ASSIGNS_TO_ACTOR, ASSIGNS_TO_GROUP_BY_FACTOR, ASSIGNS_TO_PROCESS,
ASSIGNS_TO_PRODUCT, ASSIGNS_TO_RESOURCE, ASSOCIATES_PROFILE_DEF, CONNECTS_ELEMENTS,
CONNECTS_WITH_REALIZING, COVERS_ELEMENTS, COVERS_SPACES, DECLARES, DEFINES_BY_OBJECT,
FILLS_ELEMENT, FLOW_CONTROL_ELEMENTS, INTERFERES_ELEMENTS, POSITIONS, PROJECTS_ELEMENT,
SERVICES_BUILDINGS, VOIDS_ELEMENT,
};
pub fn cover_elements(
tx: &mut Transaction,
global_id: &str,
element: EntityId,
coverings: &[EntityId],
) -> SpatialAuthoringResult<EntityId> {
super::relate(tx, COVERS_ELEMENTS, global_id, element, coverings)
}
pub fn cover_spaces(
tx: &mut Transaction,
global_id: &str,
space: EntityId,
coverings: &[EntityId],
) -> SpatialAuthoringResult<EntityId> {
super::relate(tx, COVERS_SPACES, global_id, space, coverings)
}
pub fn declare(
tx: &mut Transaction,
global_id: &str,
context: EntityId,
definitions: &[EntityId],
) -> SpatialAuthoringResult<EntityId> {
super::relate(tx, DECLARES, global_id, context, definitions)
}
pub fn define_by_object(
tx: &mut Transaction,
global_id: &str,
defining: EntityId,
defined: &[EntityId],
) -> SpatialAuthoringResult<EntityId> {
super::relate(tx, DEFINES_BY_OBJECT, global_id, defining, defined)
}
pub fn serve_buildings(
tx: &mut Transaction,
global_id: &str,
system: EntityId,
buildings: &[EntityId],
) -> SpatialAuthoringResult<EntityId> {
super::relate(tx, SERVICES_BUILDINGS, global_id, system, buildings)
}
pub fn control_flow_element(
tx: &mut Transaction,
global_id: &str,
flow_element: EntityId,
controls: &[EntityId],
) -> SpatialAuthoringResult<EntityId> {
super::relate(tx, FLOW_CONTROL_ELEMENTS, global_id, flow_element, controls)
}
pub fn assign_to_actor(
tx: &mut Transaction,
global_id: &str,
actor: EntityId,
objects: &[EntityId],
) -> SpatialAuthoringResult<EntityId> {
super::relate(tx, ASSIGNS_TO_ACTOR, global_id, actor, objects)
}
pub fn assign_to_product(
tx: &mut Transaction,
global_id: &str,
product: EntityId,
objects: &[EntityId],
) -> SpatialAuthoringResult<EntityId> {
super::relate(tx, ASSIGNS_TO_PRODUCT, global_id, product, objects)
}
pub fn assign_to_process(
tx: &mut Transaction,
global_id: &str,
process: EntityId,
objects: &[EntityId],
) -> SpatialAuthoringResult<EntityId> {
super::relate(tx, ASSIGNS_TO_PROCESS, global_id, process, objects)
}
pub fn assign_to_group_by_factor(
tx: &mut Transaction,
global_id: &str,
group: EntityId,
members: &[EntityId],
factor: f64,
) -> SpatialAuthoringResult<EntityId> {
if !factor.is_finite() {
return Err(invalid(
ASSIGNS_TO_GROUP_BY_FACTOR.type_name,
"Factor",
format!("expected a finite ratio, got {factor}"),
));
}
let id = super::relate(tx, ASSIGNS_TO_GROUP_BY_FACTOR, global_id, group, members)?;
tx.set_attribute(id, FACTOR_SLOT, Value::Real(factor));
Ok(id)
}
const FACTOR_SLOT: usize = 7;
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> {
if realizing.is_empty() {
return Err(invalid(
CONNECTS_WITH_REALIZING.type_name,
"RealizingElements",
"empty",
));
}
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;
pub fn void_element(
tx: &mut Transaction,
global_id: &str,
element: EntityId,
opening: EntityId,
) -> SpatialAuthoringResult<EntityId> {
super::relate_one(tx, VOIDS_ELEMENT, global_id, element, opening)
}
pub fn fill_element(
tx: &mut Transaction,
global_id: &str,
opening: EntityId,
filling: EntityId,
) -> SpatialAuthoringResult<EntityId> {
super::relate_one(tx, FILLS_ELEMENT, global_id, opening, filling)
}
pub fn project_element(
tx: &mut Transaction,
global_id: &str,
element: EntityId,
feature: EntityId,
) -> SpatialAuthoringResult<EntityId> {
super::relate_one(tx, PROJECTS_ELEMENT, global_id, element, feature)
}
pub fn adhere_to_element(
tx: &mut Transaction,
global_id: &str,
element: EntityId,
features: &[EntityId],
) -> SpatialAuthoringResult<EntityId> {
super::relate(tx, ADHERES_TO_ELEMENT, global_id, element, features)
}
pub fn position_products(
tx: &mut Transaction,
global_id: &str,
positioning: EntityId,
products: &[EntityId],
) -> SpatialAuthoringResult<EntityId> {
super::relate(tx, POSITIONS, global_id, positioning, products)
}
pub fn assign_to_resource(
tx: &mut Transaction,
global_id: &str,
resource: EntityId,
objects: &[EntityId],
) -> SpatialAuthoringResult<EntityId> {
super::relate(tx, ASSIGNS_TO_RESOURCE, global_id, resource, objects)
}
pub fn associate_profile_def(
tx: &mut Transaction,
global_id: &str,
profile: EntityId,
objects: &[EntityId],
) -> SpatialAuthoringResult<EntityId> {
super::relate(tx, ASSOCIATES_PROFILE_DEF, global_id, profile, objects)
}