///|
/// transfac module demo - TRANSFAC motif format parsing.
///
/// Demonstrates parsing of TRANSFAC database files containing transcription
/// factor binding site matrices with two-letter field codes, position
/// frequency matrices (PFM), and rich metadata including references.
///
/// Reference: Biopython Bio.motifs.transfac
/// Run: moon run examples/transfac_demo
fn main {
  println("=== transfac Module Demo ===")
  println("TRANSFAC transcription factor binding profile parsing")
  println("Reference: Biopython Bio.motifs.transfac")
  println("")

  // ------------------------------------------------------------------
  // 1. Parse sample TRANSFAC text
  // ------------------------------------------------------------------
  println("=== 1. Parse Sample TRANSFAC Text ===")
  let text = @bio.transfac_sample_text_full()
  let record = @bio.transfac_parse_full(text)
  println("Version: " + record.version)
  println("Number of motifs: " + record.num_motifs().to_string())
  println("")

  // ------------------------------------------------------------------
  // 2. Motif metadata
  // ------------------------------------------------------------------
  println("=== 2. Motif Metadata ===")
  let mut i = 0
  while i < record.motifs.length() {
    let m = record.motifs[i]
    println("Motif " + (i + 1).to_string() + ":")
    println("  Accession (AC): " + m.accession)
    println("  Identifier (ID): " + m.identifier)
    println("  Name (NA): " + m.name)
    println("  Description (DE): " + m.description)
    println("  Species (OS): " + m.species)
    if m.taxonomic_class.length() > 0 {
      println("  Taxonomic class (OC): " + m.taxonomic_class)
    }
    println("  Binding factors (BF): " + m.binding_factors.length().to_string())
    let mut b = 0
    while b < m.binding_factors.length() {
      println("    - " + m.binding_factors[b])
      b = b + 1
    }
    if m.comments.length() > 0 {
      println("  Comments (CC): " + m.comments)
    }
    i = i + 1
  }
  println("")

  // ------------------------------------------------------------------
  // 3. Position Frequency Matrix (PFM)
  // ------------------------------------------------------------------
  println("=== 3. Position Frequency Matrix (PFM) ===")
  let mut mi = 0
  while mi < record.motifs.length() {
    let m = record.motifs[mi]
    println("Motif " + m.accession + " (" + m.identifier + "):")
    println("  Width: " + m.width.to_string())
    println("  Alphabet: " + alphabet_to_string_impl(m.alphabet))
    // Print PFM header
    let header = StringBuilder()
    header.write_string("  pos  ")
    for a in m.alphabet {
      header.write_string(" " + a + " ")
    }
    println(header.to_string())
    // Print PFM rows (transposed: positions are columns)
    let mut col = 0
    while col < m.width {
      let row = StringBuilder()
      let row_num = if col + 1 < 10 {
        "0" + (col + 1).to_string()
      } else {
        (col + 1).to_string()
      }
      row.write_string("  " + row_num + "  ")
      let mut r = 0
      while r < m.pfm.length() {
        if col < m.pfm[r].length() {
          let v = m.pfm[r][col]
          let s = if v < 10 { " " + v.to_string() } else { v.to_string() }
          row.write_string(" " + s + " ")
        }
        r = r + 1
      }
      println(row.to_string())
      col = col + 1
    }
    mi = mi + 1
  }
  println("")

  // ------------------------------------------------------------------
  // 4. Consensus sequence
  // ------------------------------------------------------------------
  println("=== 4. Consensus Sequence ===")
  for m in record.motifs {
    println("  " + m.accession + " (" + m.identifier + "): " + m.consensus())
  }
  println("")

  // ------------------------------------------------------------------
  // 5. Total counts and frequencies
  // ------------------------------------------------------------------
  println("=== 5. Total Counts and Frequencies ===")
  if record.motifs.length() > 0 {
    let m = record.motifs[0]
    let mut pos = 0
    while pos < m.width {
      let total = m.total_at(pos)
      let cons_base = m.consensus()[pos:pos + 1].to_owned()
      let cons_freq = m.frequency(cons_base, pos)
      println(
        "  pos " +
        pos.to_string() +
        ": total=" +
        total.to_string() +
        ", consensus=" +
        cons_base +
        " (" +
        cons_freq.to_string() +
        ")",
      )
      pos = pos + 1
    }
  }
  println("")

  // ------------------------------------------------------------------
  // 6. Find motif by accession
  // ------------------------------------------------------------------
  println("=== 6. Find Motif by Accession ===")
  match record.find("T00001") {
    Some(m) =>
      println("  Found T00001: " + m.identifier + " (" + m.species + ")")
    None => println("  T00001 not found")
  }
  match record.find("nonexistent") {
    Some(_) => println("  Unexpected: found nonexistent")
    None => println("  Correctly did not find 'nonexistent'")
  }
  println("")

  // ------------------------------------------------------------------
  // 7. Alphabet index lookup
  // ------------------------------------------------------------------
  println("=== 7. Alphabet Index Lookup ===")
  if record.motifs.length() > 0 {
    let m = record.motifs[0]
    println("  A index: " + m.alphabet_index("A").to_string())
    println("  C index: " + m.alphabet_index("C").to_string())
    println("  G index: " + m.alphabet_index("G").to_string())
    println("  T index: " + m.alphabet_index("T").to_string())
    println(
      "  N index: " + m.alphabet_index("N").to_string() + " (not in alphabet)",
    )
  }
  println("")

  // ------------------------------------------------------------------
  // 8. Serialize motif back to TRANSFAC format
  // ------------------------------------------------------------------
  println("=== 8. Serialize Motif to TRANSFAC Format ===")
  if record.motifs.length() > 0 {
    let m = record.motifs[0]
    let s = @bio.transfac_motif_full_to_string(m)
    let lines = s.split("\n").to_array()
    println(
      "  Serialized motif " +
      m.accession +
      " (" +
      lines.length().to_string() +
      " lines):",
    )
    let mut l = 0
    while l < lines.length() && l < 10 {
      println("    " + lines[l].to_owned())
      l = l + 1
    }
    if lines.length() > 10 {
      println("    ...")
    }
  }
  println("")

  // ------------------------------------------------------------------
  // 9. References
  // ------------------------------------------------------------------
  println("=== 9. References ===")
  let mut ri = 0
  while ri < record.motifs.length() {
    let m = record.motifs[ri]
    println(
      "  Motif " +
      m.accession +
      " references: " +
      m.references.length().to_string(),
    )
    let mut r = 0
    while r < m.references.length() {
      let ref_ = m.references[r]
      println("    RN: " + ref_.number)
      if ref_.authors.length() > 0 {
        println("    RA: " + ref_.authors)
      }
      if ref_.title.length() > 0 {
        println("    RT: " + ref_.title)
      }
      if ref_.journal.length() > 0 {
        println("    RL: " + ref_.journal)
      }
      r = r + 1
    }
    ri = ri + 1
  }
  println("")

  println("=== Demo complete ===")
}

///|
/// Helper to convert alphabet array to comma-separated string.
fn alphabet_to_string_impl(alphabet : Array[String]) -> String {
  let sb = StringBuilder()
  let mut first = true
  for a in alphabet {
    if !first {
      sb.write_string(", ")
    }
    sb.write_string(a)
    first = false
  }
  sb.to_string()
}