///|
fn json_string(s : String) -> String {
  s.escape()
}

///|
fn feature_name(feature : Feature) -> String {
  match feature.attribute("Name") {
    Some(name) => name
    None =>
      match feature.attribute("ID") {
        Some(id) => id
        None =>
          match feature.attribute("gene_id") {
            Some(gene_id) => gene_id
            None => feature.feature_type
          }
      }
  }
}

///|
fn feature_attributes_json(feature : Feature) -> String {
  let b = StringBuilder()
  b.write_string("{")
  for index, attr in feature.attributes {
    if index > 0 {
      b.write_string(",")
    }
    b.write_string(json_string(attr.key))
    b.write_string(":")
    b.write_string(json_string(attr.value))
  }
  b.write_string("}")
  b.to_string()
}

///|
fn feature_json(feature : Feature) -> String {
  let b = StringBuilder()
  b.write_string("{")
  b.write_string("\"seqid\":")
  b.write_string(json_string(feature.seqid))
  b.write_string(",\"source\":")
  b.write_string(json_string(feature.source))
  b.write_string(",\"feature_type\":")
  b.write_string(json_string(feature.feature_type))
  b.write_string(",\"start\":\{feature.start}")
  b.write_string(",\"end\":\{feature.end}")
  b.write_string(",\"strand\":")
  b.write_string(json_string(feature.strand.to_string()))
  b.write_string(",\"attributes\":")
  b.write_string(feature_attributes_json(feature))
  b.write_string("}")
  b.to_string()
}

///|
pub fn Feature::to_bed_line(self : Feature) -> String {
  [
    self.seqid,
    "\{self.bed_start()}",
    "\{self.end}",
    feature_name(self),
    "0",
    self.strand.to_string(),
  ].join("\t")
}

///|
pub fn Annotation::to_bed(self : Annotation) -> String {
  let lines : Array[String] = []
  for feature in self.features {
    lines.push(feature.to_bed_line())
  }
  lines.join("\n") + "\n"
}

///|
pub fn Annotation::to_json(self : Annotation) -> String {
  let b = StringBuilder()
  b.write_string("{\"format\":")
  b.write_string(json_string(self.format.to_string()))
  b.write_string(",\"features\":[")
  for index, feature in self.features {
    if index > 0 {
      b.write_string(",")
    }
    b.write_string(feature_json(feature))
  }
  b.write_string("],\"diagnostics\":[]}")
  b.to_string()
}

///|
pub fn Annotation::summary(self : Annotation) -> Summary {
  {
    feature_count: self.features.length(),
    gene_count: self.filter_by_type("gene").length(),
    transcript_count: self.filter_by_type("transcript").length() +
    self.filter_by_type("mRNA").length(),
    exon_count: self.filter_by_type("exon").length(),
    cds_count: self.filter_by_type("CDS").length(),
  }
}