///|
pub fn FeatureClass::to_string(self : FeatureClass) -> String {
  match self {
    GeneClass => "gene"
    TranscriptClass => "transcript"
    ExonClass => "exon"
    CdsClass => "cds"
    UtrClass => "utr"
    RegulatoryClass => "regulatory"
    RepeatClass => "repeat"
    OtherClass(name) => name
  }
}

///|
pub fn Feature::feature_class(self : Feature) -> FeatureClass {
  match self.feature_type {
    "gene" => GeneClass
    "pseudogene" => GeneClass
    "mRNA" => TranscriptClass
    "transcript" => TranscriptClass
    "lnc_RNA" => TranscriptClass
    "ncRNA" => TranscriptClass
    "tRNA" => TranscriptClass
    "rRNA" => TranscriptClass
    "exon" => ExonClass
    "CDS" => CdsClass
    "five_prime_UTR" => UtrClass
    "three_prime_UTR" => UtrClass
    "UTR" => UtrClass
    "promoter" => RegulatoryClass
    "enhancer" => RegulatoryClass
    "silencer" => RegulatoryClass
    "repeat_region" => RepeatClass
    "transposable_element" => RepeatClass
    other => OtherClass(other)
  }
}

///|
pub fn Feature::is_gene_feature(self : Feature) -> Bool {
  self.feature_class() == GeneClass
}

///|
pub fn Feature::is_transcript_feature_public(self : Feature) -> Bool {
  self.feature_class() == TranscriptClass
}

///|
pub fn Feature::is_exon_feature(self : Feature) -> Bool {
  self.feature_class() == ExonClass
}

///|
pub fn Feature::is_coding_feature(self : Feature) -> Bool {
  self.feature_class() == CdsClass
}

///|
pub fn Feature::is_utr_feature(self : Feature) -> Bool {
  self.feature_class() == UtrClass
}

///|
pub fn Feature::is_regulatory_feature(self : Feature) -> Bool {
  self.feature_class() == RegulatoryClass
}

///|
pub fn Feature::is_repeat_feature(self : Feature) -> Bool {
  self.feature_class() == RepeatClass
}

///|
pub fn Feature::has_parent(self : Feature) -> Bool {
  self.attribute("Parent") is Some(_) ||
  self.attribute("transcript_id") is Some(_)
}

///|
pub fn Feature::stable_id(self : Feature) -> String {
  match self.attribute("ID") {
    Some(id) => id
    None =>
      match self.attribute("gene_id") {
        Some(id) => id
        None =>
          match self.attribute("transcript_id") {
            Some(id) => id
            None =>
              "\{self.seqid}:\{self.start}-\{self.end}:\{self.feature_type}"
          }
      }
  }
}

///|
pub fn Feature::display_name(self : Feature) -> String {
  match self.attribute("Name") {
    Some(name) => name
    None =>
      match self.attribute("gene_name") {
        Some(name) => name
        None =>
          match self.attribute("transcript_name") {
            Some(name) => name
            None => self.stable_id()
          }
      }
  }
}

///|
pub fn Annotation::features_by_class(
  self : Annotation,
  class : FeatureClass,
) -> Array[Feature] {
  let result : Array[Feature] = []
  for feature in self.features {
    if feature.feature_class() == class {
      result.push(feature)
    }
  }
  result
}

///|
pub fn Annotation::gene_features(self : Annotation) -> Array[Feature] {
  self.features_by_class(GeneClass)
}

///|
pub fn Annotation::transcript_features(self : Annotation) -> Array[Feature] {
  self.features_by_class(TranscriptClass)
}

///|
pub fn Annotation::exon_features(self : Annotation) -> Array[Feature] {
  self.features_by_class(ExonClass)
}

///|
pub fn Annotation::coding_features(self : Annotation) -> Array[Feature] {
  self.features_by_class(CdsClass)
}

///|
pub fn Annotation::utr_features(self : Annotation) -> Array[Feature] {
  self.features_by_class(UtrClass)
}