///|
/// QUAL / FASTQ variant example: QUAL format, FASTQ variant encodings,
/// quality score conversion, and PairedFastaQualIterator.
///
/// Run: moon run examples/qual_fastq_variant_demo/main.mbt
fn main {
  // === QUAL Format Parsing ===
  println("=== QUAL Format Parsing ===")
  let qual_content = ">seq1\n10 20 30 40\n>seq2\n50 60 70 80\n"
  let qual_records = @bio.parse_qual(qual_content)
  println("  Parsed \{qual_records.length()} QUAL records")
  for i = 0; i < qual_records.length(); i = i + 1 {
    let r = qual_records[i]
    let quals = r.letter_annotations["phred_quality"]
    println("  [\{r.id}] len=\{r.length()} quals=" + array_to_string(quals))
  }

  // === QUAL Writing (round-trip) ===
  println("\n=== QUAL Write (round-trip) ===")
  try {
    let qual_out = @bio.write_qual(qual_records)
    println("  Output:")
    println(qual_out)
  } catch {
    _ => println("  QUAL write error")
  }

  // === FASTQ Illumina (offset 64) ===
  println("=== FASTQ Illumina (offset 64) ===")
  // '@' = 64 (ASCII), so '@' - 64 = 0, 'A'=65-64=1, 'B'=66-64=2, 'C'=67-64=3
  let illumina_fastq = "@seq1\nACGT\n+\n@ABC\n"
  try {
    let illumina_records = @bio.parse_fastq_illumina(illumina_fastq)
    println("  Parsed \{illumina_records.length()} Illumina FASTQ records")
    for i = 0; i < illumina_records.length(); i = i + 1 {
      let r = illumina_records[i]
      let quals = r.letter_annotations["phred_quality"]
      println(
        "  [\{r.id}] seq=\{r.seq.to_string()} quals=" + array_to_string(quals),
      )
    }

    // Write back to Illumina FASTQ
    let illumina_out = @bio.write_fastq_illumina(illumina_records)
    println("  Written back:")
    println(illumina_out)
  } catch {
    _ => println("  Illumina FASTQ error occurred")
  }

  // === FASTQ Solexa (Solexa scores, offset 64) ===
  println("=== FASTQ Solexa (Solexa scores, offset 64) ===")
  // ';' = 59 (ASCII), so ';' - 64 = -5 (minimum Solexa score)
  let solexa_fastq = "@seq1\nACGT\n+\n;>?@\n"
  try {
    let solexa_records = @bio.parse_fastq_solexa(solexa_fastq)
    println("  Parsed \{solexa_records.length()} Solexa FASTQ records")
    for i = 0; i < solexa_records.length(); i = i + 1 {
      let r = solexa_records[i]
      let quals = r.letter_annotations["phred_quality"]
      println(
        "  [\{r.id}] seq=\{r.seq.to_string()} quals=" + array_to_string(quals),
      )
    }

    // Write back to Solexa FASTQ
    let solexa_out = @bio.write_fastq_solexa(solexa_records)
    println("  Written back:")
    println(solexa_out)
  } catch {
    _ => println("  Solexa FASTQ error occurred")
  }

  // === Quality Score Conversion ===
  println("=== Quality Score Conversion ===")
  let test_phred = [40.0, 30.0, 20.0, 10.0, 5.0, 3.0, 2.0, 1.0, 0.0]
  println("  Phred -> Solexa -> Phred:")
  for q in test_phred {
    let solexa = @bio.solexa_quality_from_phred(q)
    let back = @bio.phred_quality_from_solexa(solexa)
    let q_str = double_to_str(q)
    let s_str = double_to_str(solexa)
    let b_str = double_to_str(back)
    println("  " + q_str + " -> " + s_str + " -> " + b_str)
  }

  // === PairedFastaQualIterator ===
  println("\n=== PairedFastaQualIterator ===")
  let fasta_content = ">seq1\nACGT\n>seq2\nTGCA\n"
  let qual_for_pair = ">seq1\n10 20 30 40\n>seq2\n50 60 70 80\n"
  let paired_records = @bio.paired_fasta_qual_iterator(
    fasta_content, qual_for_pair,
  )
  println("  Paired \{paired_records.length()} records")
  for i = 0; i < paired_records.length(); i = i + 1 {
    let r = paired_records[i]
    let quals = r.letter_annotations["phred_quality"]
    println(
      "  [\{r.id}] seq=\{r.seq.to_string()} quals=" + array_to_string(quals),
    )
  }

  // === SeqIO Unified Interface (QUAL, FASTQ variants) ===
  println("\n=== SeqIO Unified Interface ===")
  try {
    let qual_via_seqio = @bio.seqio_parse(qual_content, "qual")
    println("  seqio_parse(\"qual\"): \{qual_via_seqio.length()} records")
  } catch {
    _ => println("  seqio_parse(\"qual\") error")
  }

  try {
    let illumina_via_seqio = @bio.seqio_parse(illumina_fastq, "fastq-illumina")
    println(
      "  seqio_parse(\"fastq-illumina\"): \{illumina_via_seqio.length()} records",
    )
  } catch {
    _ => println("  seqio_parse(\"fastq-illumina\") error")
  }

  try {
    let solexa_via_seqio = @bio.seqio_parse(solexa_fastq, "fastq-solexa")
    println(
      "  seqio_parse(\"fastq-solexa\"): \{solexa_via_seqio.length()} records",
    )
  } catch {
    _ => println("  seqio_parse(\"fastq-solexa\") error")
  }

  // === SeqIO convert: FASTQ -> QUAL ===
  println("\n=== SeqIO convert: FASTQ -> QUAL ===")
  let fastq_sanger = "@seq1\nACGT\n+\n!\"#$\n"
  try {
    let fastq_to_qual = @bio.seqio_convert(fastq_sanger, "fastq", "qual")
    println("  Converted FASTQ to QUAL:")
    println(fastq_to_qual)
  } catch {
    _ => println("  FASTQ -> QUAL conversion error")
  }

  // === SeqIO convert: QUAL -> FASTQ ===
  println("\n=== SeqIO convert: QUAL -> FASTQ ===")
  try {
    let qual_to_fastq = @bio.seqio_convert(qual_content, "qual", "fastq")
    println("  Converted QUAL to FASTQ:")
    println(qual_to_fastq)
  } catch {
    _ => println("  QUAL -> FASTQ conversion error")
  }
}

///|
/// Helper: convert an Array[Int] to a display string like "[10, 20, 30]"
fn array_to_string(arr : Array[Int32]) -> String {
  let buf = StringBuilder()
  buf.write_char('[')
  for i = 0; i < arr.length(); i = i + 1 {
    if i > 0 {
      buf.write_string(", ")
    }
    buf.write_string(arr[i].to_string())
  }
  buf.write_char(']')
  buf.to_string()
}

///|
/// Helper: format a Double to 4 decimal places
fn double_to_str(v : Double) -> String {
  let s = v.to_string()
  // Find decimal point and truncate to 4 places
  match s.find(".") {
    Some(pos) => {
      let end = (pos + 5).min(s.length())
      if end < s.length() {
        s[0:end].to_owned()
      } else {
        s
      }
    }
    None => s
  }
}