///|
/// Integration boundary supported by the planning library.
pub(all) enum AdapterKind {
  Cmm
  Vision
  Laser
  OpcUa
} derive(Eq, Debug)

///|
/// Capability contract for a future measurement adapter.
pub(all) struct AdapterContract {
  kind : AdapterKind
  supported_kinds : Array[FeatureKind]
  required_fields : Array[String]
}

///|
/// Construct an adapter contract.
pub fn adapter_contract(
  kind : AdapterKind,
  supported_kinds : Array[FeatureKind],
) -> AdapterContract {
  let required_fields = match kind {
    Cmm => ["device_id", "probe_id", "coordinate_system"]
    Vision => ["camera_id", "calibration_id", "image_reference"]
    Laser => ["scanner_id", "calibration_id", "point_cloud_reference"]
    OpcUa => ["endpoint", "node_id", "timestamp"]
  }
  { kind, supported_kinds, required_fields }
}

///|
/// Choose a conservative default adapter for a feature kind.
pub fn adapter_for_kind(kind : FeatureKind) -> AdapterContract {
  let adapter = match kind {
    Hole | Shaft | Slot | Circle => AdapterKind::Vision
    Plane | Cylinder | Datum => AdapterKind::Cmm
  }
  let kinds = []
  for candidate in all_feature_kinds() {
    if candidate == kind {
      kinds.push(candidate)
    }
  }
  adapter_contract(adapter, kinds)
}

///|
/// Return a stable adapter name.
pub fn adapter_name(kind : AdapterKind) -> String {
  match kind {
    Cmm => "cmm"
    Vision => "vision"
    Laser => "laser"
    OpcUa => "opcua"
  }
}

///|
/// Parse an adapter name.
pub fn adapter_kind_from_name(name : String) -> AdapterKind? {
  match name.trim().to_lower() {
    "cmm" => Some(AdapterKind::Cmm)
    "vision" => Some(AdapterKind::Vision)
    "laser" => Some(AdapterKind::Laser)
    "opcua" => Some(AdapterKind::OpcUa)
    _ => None
  }
}

///|
/// Return all adapter kinds in stable order.
pub fn all_adapter_kinds() -> Array[AdapterKind] {
  [
    AdapterKind::Cmm,
    AdapterKind::Vision,
    AdapterKind::Laser,
    AdapterKind::OpcUa,
  ]
}

///|
/// Return whether an adapter supports a feature kind.
pub fn adapter_can_measure(
  contract : AdapterContract,
  kind : FeatureKind,
) -> Bool {
  let mut result = false
  for supported in contract.supported_kinds {
    if supported == kind {
      result = true
    }
  }
  result
}

///|
/// Return whether a contract has a non-empty integration boundary.
pub fn adapter_contract_is_valid(contract : AdapterContract) -> Bool {
  contract.supported_kinds.length() > 0 && contract.required_fields.length() > 0
}

///|
/// Explain an adapter/feature mismatch.
pub fn adapter_selection_reason(
  contract : AdapterContract,
  kind : FeatureKind,
) -> String {
  if adapter_can_measure(contract, kind) {
    "supported"
  } else {
    "unsupported-kind"
  }
}

///|
/// Return the number of required integration fields.
pub fn adapter_required_field_count(contract : AdapterContract) -> Int {
  contract.required_fields.length()
}

///|
/// Return whether an adapter normally connects to a live system.
pub fn adapter_is_online(kind : AdapterKind) -> Bool {
  match kind {
    OpcUa => true
    _ => false
  }
}

///|
/// Return whether an adapter requires calibration metadata.
pub fn adapter_requires_calibration(kind : AdapterKind) -> Bool {
  match kind {
    Cmm | Vision | Laser => true
    OpcUa => false
  }
}

///|
/// Return whether an adapter can process batches offline.
pub fn adapter_supports_batch(kind : AdapterKind) -> Bool {
  match kind {
    Cmm | Vision | Laser => true
    OpcUa => false
  }
}

///|
/// Return a stable adapter priority for selection UI.
pub fn adapter_priority(kind : AdapterKind) -> Int {
  match kind {
    Cmm => 0
    Vision => 1
    Laser => 2
    OpcUa => 3
  }
}

///|
/// Return the number of feature kinds supported by a contract.
pub fn adapter_supported_kind_count(contract : AdapterContract) -> Int {
  contract.supported_kinds.length()
}

///|
/// Render an adapter contract summary.
pub fn adapter_contract_summary(contract : AdapterContract) -> String {
  adapter_name(contract.kind) +
  " kinds=" +
  contract.supported_kinds.length().to_string() +
  " fields=" +
  contract.required_fields.length().to_string()
}

///|
/// Return the intersection of two adapter feature-kind lists.
pub fn adapter_kind_intersection(
  left : AdapterContract,
  right : AdapterContract,
) -> Array[FeatureKind] {
  let result = []
  for kind in left.supported_kinds {
    if adapter_can_measure(right, kind) {
      result.push(kind)
    }
  }
  result
}

///|
/// Return whether two contracts can serve a common feature kind.
pub fn adapters_overlap(
  left : AdapterContract,
  right : AdapterContract,
) -> Bool {
  adapter_kind_intersection(left, right).length() > 0
}