///|
pub(all) enum VariantClass {
  Empty
  SNV
  MNV
  Insertion
  Deletion
  Mixed
  Symbolic
} derive(Debug, Eq)

///|
pub fn record_variant_class(record : VcfRecord) -> VariantClass {
  if record.alt_alleles.length() == 0 {
    VariantClass::Empty
  } else if record.alt_alleles.any(is_symbolic_record_alt) {
    VariantClass::Symbolic
  } else {
    let mut first : VariantClass? = None
    for alt in record.alt_alleles {
      let current = classify_allele_pair(record.ref_allele, alt)
      match first {
        None => first = Some(current)
        Some(previous) if previous != current => return VariantClass::Mixed
        Some(_) => ()
      }
    }
    match first {
      Some(value) => value
      None => VariantClass::Empty
    }
  }
}

///|
fn classify_allele_pair(ref_allele : String, alt : String) -> VariantClass {
  if ref_allele.length() == 1 && alt.length() == 1 {
    VariantClass::SNV
  } else if ref_allele.length() == alt.length() {
    VariantClass::MNV
  } else if ref_allele.length() < alt.length() {
    VariantClass::Insertion
  } else {
    VariantClass::Deletion
  }
}

///|
fn is_symbolic_record_alt(alt : String) -> Bool {
  alt.has_prefix("<") || alt == "*"
}

///|
pub fn record_alt_count(record : VcfRecord) -> Int {
  record.alt_alleles.length()
}

///|
pub fn record_is_snv(record : VcfRecord) -> Bool {
  record_variant_class(record) == VariantClass::SNV
}

///|
pub fn record_is_indel(record : VcfRecord) -> Bool {
  let class = record_variant_class(record)
  class == VariantClass::Insertion || class == VariantClass::Deletion
}

///|
pub fn record_has_info(record : VcfRecord, key : String) -> Bool {
  for pair in record.info {
    if pair.0 == key {
      return true
    }
  }
  false
}

///|
pub fn record_info_value(record : VcfRecord, key : String) -> VcfValue? {
  for pair in record.info {
    if pair.0 == key {
      return Some(pair.1)
    }
  }
  None
}

///|
pub fn record_info_int(record : VcfRecord, key : String) -> Int? {
  match record_info_value(record, key) {
    Some(VcfValue::Integer(value)) => Some(value)
    Some(VcfValue::List(values)) if values.length() > 0 =>
      match values[0] {
        VcfValue::Integer(value) => Some(value)
        _ => None
      }
    _ => None
  }
}

///|
pub fn record_info_float(record : VcfRecord, key : String) -> Double? {
  match record_info_value(record, key) {
    Some(VcfValue::Float(value)) => Some(value)
    Some(VcfValue::List(values)) if values.length() > 0 =>
      match values[0] {
        VcfValue::Float(value) => Some(value)
        _ => None
      }
    _ => None
  }
}

///|
pub fn record_sample(record : VcfRecord, name : String) -> SampleCall? {
  for sample in record.samples {
    if sample.name == name {
      return Some(sample)
    }
  }
  None
}

///|
pub fn sample_value(sample : SampleCall, key : String) -> VcfValue? {
  for pair in sample.values {
    if pair.0 == key {
      return Some(pair.1)
    }
  }
  None
}

///|
pub fn record_sort_key(record : VcfRecord) -> String {
  let alts = record.alt_alleles.join(",")
  "\{record.chrom}:\{record.pos}:\{record.ref_allele}>\{alts}"
}

///|
pub fn sort_records(records : Array[VcfRecord]) -> Array[VcfRecord] {
  let sorted : Array[VcfRecord] = []
  for record in records {
    let mut inserted = false
    for index in 0.. Int {
  if left.chrom < right.chrom {
    -1
  } else if left.chrom > right.chrom {
    1
  } else if left.pos < right.pos {
    -1
  } else if left.pos > right.pos {
    1
  } else if record_sort_key(left) < record_sort_key(right) {
    -1
  } else if record_sort_key(left) > record_sort_key(right) {
    1
  } else {
    0
  }
}

///|
pub fn group_record_counts(records : Array[VcfRecord]) -> Array[(String, Int)] {
  let counts : Array[(String, Int)] = []
  for record in records {
    let mut found = false
    for index, pair in counts {
      if pair.0 == record.chrom {
        counts[index] = (pair.0, pair.1 + 1)
        found = true
        break
      }
    }
    if !found {
      counts.push((record.chrom, 1))
    }
  }
  counts
}