///|
fn reference_term(
  name : String,
  category : String,
  description : String,
) -> ReferenceTerm {
  { name, category, description }
}

///|
pub fn reference_terms() -> Array[ReferenceTerm] {
  [
    reference_term("gene", "gene", "A genomic locus with heritable function"),
    reference_term(
      "pseudogene", "gene", "A gene-like locus lacking ordinary coding function",
    ),
    reference_term(
      "protein_coding_gene", "gene", "A gene expected to produce a protein product",
    ),
    reference_term(
      "lncRNA_gene", "gene", "A gene producing long non-coding RNA",
    ),
    reference_term("miRNA_gene", "gene", "A gene producing micro RNA"),
    reference_term("snRNA_gene", "gene", "A gene producing small nuclear RNA"),
    reference_term(
      "snoRNA_gene", "gene", "A gene producing small nucleolar RNA",
    ),
    reference_term("rRNA_gene", "gene", "A ribosomal RNA gene"),
    reference_term("tRNA_gene", "gene", "A transfer RNA gene"),
    reference_term("mRNA", "transcript", "A messenger RNA transcript"),
    reference_term(
      "transcript", "transcript", "A processed or predicted RNA transcript",
    ),
    reference_term(
      "primary_transcript", "transcript", "The unprocessed transcript product",
    ),
    reference_term("lnc_RNA", "transcript", "A long non-coding RNA transcript"),
    reference_term("ncRNA", "transcript", "A non-coding RNA transcript"),
    reference_term("miRNA", "transcript", "A micro RNA transcript"),
    reference_term("snRNA", "transcript", "A small nuclear RNA transcript"),
    reference_term("snoRNA", "transcript", "A small nucleolar RNA transcript"),
    reference_term("rRNA", "transcript", "A ribosomal RNA transcript"),
    reference_term("tRNA", "transcript", "A transfer RNA transcript"),
    reference_term(
      "exon", "exon", "A region retained in a processed transcript",
    ),
    reference_term(
      "coding_exon", "exon", "An exon with coding sequence contribution",
    ),
    reference_term("noncoding_exon", "exon", "An exon with no CDS contribution"),
    reference_term("CDS", "coding", "Coding sequence translated into protein"),
    reference_term("start_codon", "coding", "The translation initiation codon"),
    reference_term("stop_codon", "coding", "The translation termination codon"),
    reference_term("selenocysteine", "coding", "A recoded amino acid feature"),
    reference_term(
      "five_prime_UTR", "utr", "Untranslated region before the CDS on the transcript",
    ),
    reference_term(
      "three_prime_UTR", "utr", "Untranslated region after the CDS on the transcript",
    ),
    reference_term("UTR", "utr", "Generic untranslated region"),
    reference_term(
      "intron", "intron", "A region removed from the processed transcript",
    ),
    reference_term(
      "splice_donor_site", "splice", "The donor side of a splice junction",
    ),
    reference_term(
      "splice_acceptor_site", "splice", "The acceptor side of a splice junction",
    ),
    reference_term(
      "splice_region", "splice", "A broader region around a splice junction",
    ),
    reference_term(
      "promoter", "regulatory", "A region that initiates transcription",
    ),
    reference_term(
      "enhancer", "regulatory", "A distal regulatory element that can increase transcription",
    ),
    reference_term(
      "silencer", "regulatory", "A regulatory element that can repress transcription",
    ),
    reference_term("insulator", "regulatory", "A regulatory boundary element"),
    reference_term(
      "regulatory_region", "regulatory", "A generic regulatory sequence region",
    ),
    reference_term(
      "TF_binding_site", "regulatory", "A transcription factor binding site",
    ),
    reference_term(
      "open_chromatin_region", "regulatory", "A region with accessible chromatin",
    ),
    reference_term("CpG_island", "regulatory", "A CpG-rich genomic interval"),
    reference_term("repeat_region", "repeat", "A repeated genomic region"),
    reference_term(
      "transposable_element", "repeat", "A mobile or mobile-derived element",
    ),
    reference_term("LINE", "repeat", "Long interspersed nuclear element"),
    reference_term("SINE", "repeat", "Short interspersed nuclear element"),
    reference_term(
      "LTR_retrotransposon", "repeat", "LTR retrotransposon feature",
    ),
    reference_term("DNA_transposon", "repeat", "DNA transposon feature"),
    reference_term("simple_repeat", "repeat", "A simple tandem repeat"),
    reference_term("microsatellite", "repeat", "A short tandem repeat"),
    reference_term("minisatellite", "repeat", "A longer tandem repeat"),
    reference_term(
      "origin_of_replication", "replication", "A replication origin",
    ),
    reference_term(
      "replication_regulatory_region", "replication", "A replication-associated regulatory interval",
    ),
    reference_term(
      "centromere", "chromosome", "A centromeric chromosome region",
    ),
    reference_term("telomere", "chromosome", "A telomeric chromosome region"),
    reference_term("chromosome", "chromosome", "A chromosome feature"),
    reference_term("contig", "assembly", "A contiguous assembled sequence"),
    reference_term(
      "scaffold", "assembly", "An ordered or oriented assembly scaffold",
    ),
    reference_term("supercontig", "assembly", "A large assembly unit"),
    reference_term("match", "alignment", "A generic sequence match"),
    reference_term("match_part", "alignment", "A component of a sequence match"),
    reference_term("EST_match", "alignment", "An EST alignment match"),
    reference_term("cDNA_match", "alignment", "A cDNA alignment match"),
    reference_term("protein_match", "alignment", "A protein alignment match"),
    reference_term(
      "nucleotide_match", "alignment", "A nucleotide alignment match",
    ),
    reference_term(
      "sequence_variant", "variant", "A sequence variation feature",
    ),
    reference_term("SNV", "variant", "A single nucleotide variant"),
    reference_term("SNP", "variant", "A single nucleotide polymorphism"),
    reference_term("insertion", "variant", "An insertion variant"),
    reference_term("deletion", "variant", "A deletion variant"),
    reference_term("indel", "variant", "An insertion-deletion event"),
    reference_term("inversion", "variant", "An inversion event"),
    reference_term("copy_number_variation", "variant", "A copy number variant"),
    reference_term("structural_variant", "variant", "A structural variant"),
    reference_term(
      "polyA_site", "transcript_processing", "A polyadenylation site",
    ),
    reference_term(
      "polyA_signal_sequence", "transcript_processing", "A polyadenylation signal",
    ),
    reference_term(
      "mature_protein_region", "protein", "A mature protein product interval",
    ),
    reference_term(
      "signal_peptide", "protein", "A signal peptide coding interval",
    ),
    reference_term(
      "transmembrane_region", "protein", "A transmembrane protein interval",
    ),
    reference_term("protein_domain", "protein", "A protein domain interval"),
    reference_term("binding_site", "protein", "A molecular binding site"),
    reference_term("active_site", "protein", "A catalytic or active site"),
    reference_term(
      "conserved_region", "comparative", "A conserved sequence interval",
    ),
    reference_term(
      "syntenic_region", "comparative", "A syntenic genomic interval",
    ),
    reference_term(
      "homology_region", "comparative", "A homology-supported interval",
    ),
    reference_term("operon", "prokaryotic", "A co-transcribed gene group"),
    reference_term(
      "ribosome_entry_site", "translation", "An internal ribosome entry site",
    ),
    reference_term(
      "attenuator", "regulatory", "A transcription attenuation feature",
    ),
    reference_term(
      "terminator", "regulatory", "A transcription termination feature",
    ),
    reference_term(
      "matrix_attachment_site", "regulatory", "A matrix attachment site",
    ),
    reference_term(
      "locus_control_region", "regulatory", "A locus control region",
    ),
    reference_term(
      "enhancer_blocking_element", "regulatory", "An element that blocks enhancer action",
    ),
    reference_term(
      "response_element", "regulatory", "A regulatory response element",
    ),
    reference_term(
      "miRNA_binding_site", "regulatory", "A micro RNA binding site",
    ),
    reference_term("guide_RNA", "transcript", "A guide RNA transcript"),
    reference_term("vault_RNA", "transcript", "A vault RNA transcript"),
    reference_term("RNase_P_RNA", "transcript", "An RNase P RNA transcript"),
    reference_term("RNase_MRP_RNA", "transcript", "An RNase MRP RNA transcript"),
    reference_term("antisense_RNA", "transcript", "An antisense RNA transcript"),
    reference_term(
      "sense_intronic_ncRNA", "transcript", "A sense intronic non-coding RNA",
    ),
    reference_term(
      "sense_overlapping_ncRNA", "transcript", "A sense overlapping non-coding RNA",
    ),
    reference_term(
      "processed_transcript", "transcript", "A processed transcript without coding annotation",
    ),
    reference_term(
      "retained_intron", "transcript", "A transcript with retained intron sequence",
    ),
    reference_term(
      "nonsense_mediated_decay", "transcript", "A transcript predicted for NMD",
    ),
  ]
}

///|
pub fn find_reference_term(name : String) -> ReferenceTerm {
  for term in reference_terms() {
    if term.name == name {
      return term
    }
  }
  reference_term(name, "other", "Unrecognized or project-specific feature type")
}

///|
pub fn reference_terms_by_category(category : String) -> Array[ReferenceTerm] {
  let result : Array[ReferenceTerm] = []
  for term in reference_terms() {
    if term.category == category {
      result.push(term)
    }
  }
  result
}

///|
pub fn reference_term_categories() -> Array[String] {
  let categories : Array[String] = []
  for term in reference_terms() {
    if !categories.contains(term.category) {
      categories.push(term.category)
    }
  }
  categories
}

///|
pub fn Feature::reference_term(self : Feature) -> ReferenceTerm {
  find_reference_term(self.feature_type)
}

///|
pub fn Annotation::unknown_reference_terms(self : Annotation) -> Array[String] {
  let unknown : Array[String] = []
  for feature in self.features {
    let term = feature.reference_term()
    if term.category == "other" && !unknown.contains(feature.feature_type) {
      unknown.push(feature.feature_type)
    }
  }
  unknown
}