///|
pub fn ProductQuery::new() -> ProductQuery {
  {
    kind: None,
    ifc_type: None,
    name_contains: None,
    container: None,
    has_properties: false,
  }
}

///|
pub fn ProductQuery::of_kind(
  self : ProductQuery,
  kind : ProductKind,
) -> ProductQuery {
  { ..self, kind: Some(kind) }
}

///|
pub fn ProductQuery::of_type(
  self : ProductQuery,
  ifc_type : String,
) -> ProductQuery {
  { ..self, ifc_type: Some(ifc_type) }
}

///|
pub fn ProductQuery::named(
  self : ProductQuery,
  fragment : String,
) -> ProductQuery {
  { ..self, name_contains: Some(fragment) }
}

///|
pub fn ProductQuery::in_container(
  self : ProductQuery,
  id : Int,
) -> ProductQuery {
  { ..self, container: Some(id) }
}

///|
pub fn ProductQuery::with_properties(self : ProductQuery) -> ProductQuery {
  { ..self, has_properties: true }
}

///|
fn product_matches(query : ProductQuery, product : Product) -> Bool {
  let kind_match = match query.kind {
    Some(kind) => product.kind == kind
    None => true
  }
  let type_match = match query.ifc_type {
    Some(name) => product.ifc_type.to_upper() == name.to_upper()
    None => true
  }
  let name_match = match query.name_contains {
    Some(fragment) =>
      match product.name {
        Some(name) => name.to_lower().contains(fragment.to_lower())
        None => false
      }
    None => true
  }
  let container_match = match query.container {
    Some(id) => product.container == Some(id)
    None => true
  }
  let property_match = !query.has_properties ||
    product.property_sets.length() > 0
  kind_match && type_match && name_match && container_match && property_match
}

///|
pub fn query_products(
  index : ModelIndex,
  query : ProductQuery,
) -> Array[Product] {
  extract_products(index).filter(product => product_matches(query, product))
}

///|
pub fn products_named(index : ModelIndex, fragment : String) -> Array[Product] {
  query_products(index, ProductQuery::new().named(fragment))
}

///|
pub fn products_in_container(index : ModelIndex, id : Int) -> Array[Product] {
  query_products(index, ProductQuery::new().in_container(id))
}