use ifc_model::{Entity, EntityId, Transaction, Value};
use crate::authoring::{invalid, SystemAuthoringResult};
pub(crate) mod distribution_slot {
pub const GLOBAL_ID: usize = 0;
pub const NAME: usize = 2;
pub const DESCRIPTION: usize = 3;
pub const OBJECT_PLACEMENT: usize = 5;
pub const REPRESENTATION: usize = 6;
pub const TAG: usize = 7;
pub const WIDTH: usize = 8;
}
pub(crate) mod zone_slot {
pub const GLOBAL_ID: usize = 0;
pub const NAME: usize = 2;
pub const DESCRIPTION: usize = 3;
pub const LONG_NAME: usize = 5;
pub const WIDTH: usize = 6;
}
pub(crate) mod spatial_zone_slot {
pub const GLOBAL_ID: usize = 0;
pub const NAME: usize = 2;
pub const DESCRIPTION: usize = 3;
pub const OBJECT_PLACEMENT: usize = 5;
pub const REPRESENTATION: usize = 6;
pub const LONG_NAME: usize = 7;
pub const PREDEFINED_TYPE: usize = 8;
pub const WIDTH: usize = 9;
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum DistributionElementKind {
DistributionElement,
EnergyConversionDevice,
FlowController,
FlowFitting,
FlowMovingDevice,
FlowSegment,
FlowStorageDevice,
FlowTerminal,
FlowTreatmentDevice,
}
impl DistributionElementKind {
pub fn type_name(self) -> &'static str {
match self {
Self::DistributionElement => "IFCDISTRIBUTIONELEMENT",
Self::EnergyConversionDevice => "IFCENERGYCONVERSIONDEVICE",
Self::FlowController => "IFCFLOWCONTROLLER",
Self::FlowFitting => "IFCFLOWFITTING",
Self::FlowMovingDevice => "IFCFLOWMOVINGDEVICE",
Self::FlowSegment => "IFCFLOWSEGMENT",
Self::FlowStorageDevice => "IFCFLOWSTORAGEDEVICE",
Self::FlowTerminal => "IFCFLOWTERMINAL",
Self::FlowTreatmentDevice => "IFCFLOWTREATMENTDEVICE",
}
}
}
#[derive(Debug, Clone, Copy, Default)]
pub struct ElementAttributes<'a> {
pub name: Option<&'a str>,
pub description: Option<&'a str>,
pub placement: Option<EntityId>,
pub representation: Option<EntityId>,
pub tag: Option<&'a str>,
}
fn text(value: Option<&str>) -> Value {
value.map_or(Value::Null, |v| Value::Text(v.into()))
}
fn reference(value: Option<EntityId>) -> Value {
value.map_or(Value::Null, Value::Ref)
}
pub fn create_distribution_element(
tx: &mut Transaction,
kind: DistributionElementKind,
global_id: &str,
attributes: ElementAttributes<'_>,
) -> SystemAuthoringResult<EntityId> {
let type_name = kind.type_name();
super::guid(type_name, global_id)?;
let mut attrs = vec![Value::Null; distribution_slot::WIDTH];
attrs[distribution_slot::GLOBAL_ID] = Value::Text(global_id.into());
attrs[distribution_slot::NAME] = text(attributes.name);
attrs[distribution_slot::DESCRIPTION] = text(attributes.description);
attrs[distribution_slot::OBJECT_PLACEMENT] = reference(attributes.placement);
attrs[distribution_slot::REPRESENTATION] = reference(attributes.representation);
attrs[distribution_slot::TAG] = text(attributes.tag);
Ok(tx.create(Entity::new(type_name, attrs)))
}
pub fn create_zone(
tx: &mut Transaction,
global_id: &str,
name: Option<&str>,
description: Option<&str>,
long_name: Option<&str>,
) -> SystemAuthoringResult<EntityId> {
super::guid("IFCZONE", global_id)?;
let mut attrs = vec![Value::Null; zone_slot::WIDTH];
attrs[zone_slot::GLOBAL_ID] = Value::Text(global_id.into());
attrs[zone_slot::NAME] = text(name);
attrs[zone_slot::DESCRIPTION] = text(description);
attrs[zone_slot::LONG_NAME] = text(long_name);
Ok(tx.create(Entity::new("IFCZONE", attrs)))
}
pub fn create_spatial_zone(
tx: &mut Transaction,
global_id: &str,
attributes: ElementAttributes<'_>,
long_name: Option<&str>,
predefined_type: Option<&str>,
object_type: Option<&str>,
) -> SystemAuthoringResult<EntityId> {
super::guid("IFCSPATIALZONE", global_id)?;
if predefined_type == Some("USERDEFINED") && object_type.is_none() {
return Err(invalid(
"IFCSPATIALZONE",
"PredefinedType",
"USERDEFINED requires an ObjectType naming the kind",
));
}
let mut attrs = vec![Value::Null; spatial_zone_slot::WIDTH];
attrs[spatial_zone_slot::GLOBAL_ID] = Value::Text(global_id.into());
attrs[spatial_zone_slot::NAME] = text(attributes.name);
attrs[spatial_zone_slot::DESCRIPTION] = text(attributes.description);
attrs[4] = text(object_type);
attrs[spatial_zone_slot::OBJECT_PLACEMENT] = reference(attributes.placement);
attrs[spatial_zone_slot::REPRESENTATION] = reference(attributes.representation);
attrs[spatial_zone_slot::LONG_NAME] = text(long_name);
attrs[spatial_zone_slot::PREDEFINED_TYPE] =
predefined_type.map_or(Value::Null, |t| Value::Enum(t.into()));
Ok(tx.create(Entity::new("IFCSPATIALZONE", attrs)))
}