///|
pub fn AttributePresence::to_string(self : AttributePresence) -> String {
  match self {
    Required => "required"
    Optional => "optional"
    Rare => "rare"
  }
}

///|
fn schema_presence(count : Int, feature_count : Int) -> AttributePresence {
  if feature_count == 0 || count == feature_count {
    Required
  } else if count * 4 <= feature_count {
    Rare
  } else {
    Optional
  }
}

///|
fn schema_has_key(keys : Array[String], key : String) -> Bool {
  for item in keys {
    if item == key {
      return true
    }
  }
  false
}

///|
fn schema_count_key(features : Array[Feature], key : String) -> Int {
  let mut count = 0
  for feature in features {
    for attr in feature.attributes {
      if attr.key == key {
        count += 1
      }
    }
  }
  count
}

///|
fn schema_example_value(features : Array[Feature], key : String) -> String {
  for feature in features {
    for attr in feature.attributes {
      if attr.key == key {
        return attr.value
      }
    }
  }
  ""
}

///|
pub fn Annotation::infer_attribute_schema(self : Annotation) -> AttributeSchema {
  let keys : Array[String] = []
  let rows : Array[AttributeSchemaRow] = []
  for feature in self.features {
    for attr in feature.attributes {
      if !schema_has_key(keys, attr.key) {
        keys.push(attr.key)
        let count = schema_count_key(self.features, attr.key)
        rows.push({
          key: attr.key,
          count,
          feature_count: self.features.length(),
          presence: schema_presence(count, self.features.length()),
          example_value: schema_example_value(self.features, attr.key),
        })
      }
    }
  }
  { rows, }
}

///|
pub fn AttributeSchema::required_keys(self : AttributeSchema) -> Array[String] {
  let keys : Array[String] = []
  for row in self.rows {
    if row.presence == Required {
      keys.push(row.key)
    }
  }
  keys
}

///|
pub fn AttributeSchema::optional_keys(self : AttributeSchema) -> Array[String] {
  let keys : Array[String] = []
  for row in self.rows {
    if row.presence == Optional {
      keys.push(row.key)
    }
  }
  keys
}

///|
pub fn AttributeSchema::rare_keys(self : AttributeSchema) -> Array[String] {
  let keys : Array[String] = []
  for row in self.rows {
    if row.presence == Rare {
      keys.push(row.key)
    }
  }
  keys
}

///|
pub fn AttributeSchema::to_tsv(self : AttributeSchema) -> String {
  let lines : Array[String] = [
    "attribute\tcount\tfeature_count\tpresence\texample",
  ]
  for row in self.rows {
    lines.push(
      [
        row.key,
        "\{row.count}",
        "\{row.feature_count}",
        row.presence.to_string(),
        row.example_value,
      ].join("\t"),
    )
  }
  lines.join("\n") + "\n"
}