///|
/// Return the stable lowercase name of a feature kind.
pub fn feature_kind_name(kind : FeatureKind) -> String {
  match kind {
    Hole => "hole"
    Shaft => "shaft"
    Plane => "plane"
    Slot => "slot"
    Circle => "circle"
    Cylinder => "cylinder"
    Datum => "datum"
  }
}

///|
/// Parse a stable feature kind name.
pub fn feature_kind_from_name(name : String) -> FeatureKind? {
  match name.trim().to_lower() {
    "hole" => Some(FeatureKind::Hole)
    "shaft" => Some(FeatureKind::Shaft)
    "plane" => Some(FeatureKind::Plane)
    "slot" => Some(FeatureKind::Slot)
    "circle" => Some(FeatureKind::Circle)
    "cylinder" => Some(FeatureKind::Cylinder)
    "datum" => Some(FeatureKind::Datum)
    _ => None
  }
}

///|
/// Return all feature kinds in public contract order.
pub fn all_feature_kinds() -> Array[FeatureKind] {
  [
    FeatureKind::Hole,
    FeatureKind::Shaft,
    FeatureKind::Plane,
    FeatureKind::Slot,
    FeatureKind::Circle,
    FeatureKind::Cylinder,
    FeatureKind::Datum,
  ]
}

///|
/// Return whether a feature kind describes a rotational feature.
pub fn feature_kind_is_rotational(kind : FeatureKind) -> Bool {
  match kind {
    Hole | Shaft | Circle | Cylinder => true
    _ => false
  }
}

///|
/// Return whether a feature kind describes a surface or planar feature.
pub fn feature_kind_is_surface(kind : FeatureKind) -> Bool {
  match kind {
    Plane | Slot => true
    _ => false
  }
}

///|
/// Return whether a feature kind is itself a datum.
pub fn feature_kind_is_datum(kind : FeatureKind) -> Bool {
  kind == FeatureKind::Datum
}

///|
/// Return the nominal degrees of freedom represented by a kind.
pub fn feature_kind_dimension(kind : FeatureKind) -> Int {
  match kind {
    Hole | Shaft | Circle => 1
    Plane | Slot => 2
    Cylinder => 3
    Datum => 0
  }
}

///|
/// Return the default strategy for a feature kind.
pub fn default_strategy_for_kind(kind : FeatureKind) -> Strategy {
  match kind {
    Cylinder => Strategy::Scan(count=5)
    Hole | Shaft | Circle => Strategy::RepeatedPoint(count=3)
    Plane | Slot | Datum => Strategy::SinglePoint
  }
}

///|
/// Return whether the kind normally requires scanning.
pub fn kind_requires_scan(kind : FeatureKind) -> Bool {
  kind == FeatureKind::Cylinder
}

///|
/// Return whether a touch probe is a suitable default adapter.
pub fn kind_supports_touch_probe(kind : FeatureKind) -> Bool {
  match kind {
    Hole | Shaft | Plane | Slot | Circle | Cylinder | Datum => true
  }
}

///|
/// Return whether a vision adapter is a suitable default adapter.
pub fn kind_supports_vision(kind : FeatureKind) -> Bool {
  match kind {
    Hole | Shaft | Slot | Circle | Cylinder => true
    Plane | Datum => false
  }
}

///|
/// Return a broad semantic category.
pub fn feature_kind_category(kind : FeatureKind) -> String {
  if feature_kind_is_datum(kind) {
    "reference"
  } else if feature_kind_is_rotational(kind) {
    "rotational"
  } else if feature_kind_is_surface(kind) {
    "surface"
  } else {
    "other"
  }
}

///|
/// Return a stable short code suitable for reports.
pub fn feature_kind_code(kind : FeatureKind) -> String {
  match kind {
    Hole => "HOL"
    Shaft => "SHA"
    Plane => "PLN"
    Slot => "SLT"
    Circle => "CIR"
    Cylinder => "CYL"
    Datum => "DAT"
  }
}

///|
/// Return a stable fingerprint for an input feature.
pub fn feature_fingerprint(feature : Feature) -> String {
  feature.id +
  "|" +
  feature_kind_name(feature.kind) +
  "|" +
  feature.nominal.to_string() +
  "|" +
  feature.lower.to_string() +
  "|" +
  feature.upper.to_string() +
  "|" +
  feature.datum.to_string()
}

///|
/// Return a default direction label for a feature kind.
pub fn default_direction_for_kind(kind : FeatureKind) -> String {
  if feature_kind_is_rotational(kind) {
    "radial"
  } else if feature_kind_is_surface(kind) {
    "normal"
  } else {
    "reference"
  }
}

///|
/// Return a default review risk for a feature kind.
pub fn default_risk_for_kind(kind : FeatureKind) -> RiskLevel {
  match kind {
    Cylinder => RiskLevel::High
    Hole | Shaft | Circle => RiskLevel::Medium
    Plane | Slot | Datum => RiskLevel::Low
  }
}

///|
/// Count rotational kinds in an array.
pub fn rotational_kind_count(kinds : Array[FeatureKind]) -> Int {
  let mut result = 0
  for kind in kinds {
    if feature_kind_is_rotational(kind) {
      result += 1
    }
  }
  result
}