///|
pub fn schema_version(model : Model) -> SchemaVersion {
  match model.header.schema_identifiers.get(0).map(String::to_upper) {
    Some("IFC2X3") => IFC2X3
    Some("IFC4") | Some("IFC4X1") => IFC4
    Some("IFC4X3") => IFC4X3
    Some(name) => UnknownSchema(name)
    None => UnknownSchema("unknown")
  }
}

///|
pub fn supports_schema(model : Model, version : SchemaVersion) -> Bool {
  schema_version(model) == version
}

///|
pub fn capability_report(model : Model) -> CapabilityReport {
  let index = model.index()
  {
    schema: schema_version(model),
    has_products: extract_products(index).length() > 0,
    has_spatial_structure: spatial_roots(index).length() > 0,
    has_properties: index.ids_named("IFCPROPERTYSET").length() > 0,
    has_geometry: geometry_entities(index).length() > 0,
    has_units: index.ids_named("IFCSIUNIT").length() > 0,
  }
}

///|
pub fn is_relationship(entity : Entity) -> Bool {
  entity.name.to_upper().has_prefix("IFCREL")
}

///|
pub fn is_property_entity(entity : Entity) -> Bool {
  entity.name.to_upper().has_prefix("IFCPROPERTY")
}

///|
pub fn is_geometry_entity(entity : Entity) -> Bool {
  let name = entity.name.to_upper()
  name.contains("SOLID") ||
  name.contains("BREP") ||
  name.contains("FACESET") ||
  name == "IFCPRODUCTDEFINITIONSHAPE" ||
  name == "IFCSHAPEREPRESENTATION"
}

///|
pub fn entity_family(entity : Entity) -> String {
  if is_relationship(entity) {
    "relationship"
  } else if is_property_entity(entity) {
    "property"
  } else if is_geometry_entity(entity) {
    "geometry"
  } else if is_product_entity(entity.name) {
    "product"
  } else {
    "other"
  }
}

///|
pub fn family_histogram(index : ModelIndex) -> Map[String, Int] {
  let result : Map[String, Int] = Map([])
  for entity in index.all() {
    result.update_or_default(entity_family(entity), 1, count => count + 1)
  }
  result
}