///|
fn phylip_demo_parse(text : String) -> @bio.AlignPhylipAlignment {
  @bio.align_phylip_parse(text) catch {
    AlignPhylipError(message) => abort("PHYLIP parse failed: " + message)
  }
}

///|
fn phylip_demo_write(
  alignment : @bio.AlignPhylipAlignment,
  layout : @bio.AlignPhylipLayout,
) -> String {
  @bio.align_phylip_write(alignment, layout~, block_width=4, group_width=0) catch {
    AlignPhylipError(message) => abort("PHYLIP write failed: " + message)
  }
}

///|
fn phylip_demo_path(path : Array[Int32]) -> String {
  let output = StringBuilder()
  output.write_char('[')
  for index = 0; index < path.length(); index = index + 1 {
    if index > 0 {
      output.write_string(", ")
    }
    output.write_string(path[index].to_string())
  }
  output.write_char(']')
  output.to_string()
}

///|
fn main {
  println("=== Biopython Bio.Align.phylip Offline Demo ===")
  let alignment = phylip_demo_parse(@bio.align_phylip_example_text())

  println("\n1. Automatic layout detection and fixed-width rows")
  println("  " + alignment.summary())
  for sequence in alignment.sequences {
    println(
      "  " +
      sequence.id +
      ": aligned=" +
      sequence.aligned_sequence +
      ", ungapped=" +
      sequence.sequence,
    )
  }

  println("\n2. Biopython-compatible identifier normalization")
  let normalized = @bio.align_phylip_normalize_id("Taxon[1]:extra") catch {
    AlignPhylipError(message) =>
      abort("PHYLIP name normalization failed: " + message)
  }
  println("  Taxon[1]:extra -> " + normalized)

  println("\n3. Coordinate path and cross-row projection")
  let paths = alignment.coordinate_path()
  for row = 0; row < paths.length(); row = row + 1 {
    println(
      "  " + alignment.sequences[row].id + ": " + phylip_demo_path(paths[row]),
    )
  }
  println(
    "  reference residue 3 -> query_one residue " +
    alignment.map_position(0, 1, 3).unwrap_or(-1).to_string(),
  )

  println("\n4. Pairwise statistics and majority consensus")
  let counts = alignment.pair_counts(0, 1) catch {
    AlignPhylipError(message) => abort("PHYLIP counting failed: " + message)
  }
  let consensus = alignment.majority_consensus(minimum_fraction=0.5) catch {
    AlignPhylipError(message) => abort("PHYLIP consensus failed: " + message)
  }
  println(
    "  aligned=" +
    counts.aligned.to_string() +
    ", identities=" +
    counts.identities.to_string() +
    ", gap opens=" +
    counts.gap_opens.to_string() +
    ", identity=" +
    counts.identity().to_string(),
  )
  println("  majority=" + consensus)

  println("\n5. Canonical sequential and interleaved writing")
  let sequential = phylip_demo_write(alignment, @bio.PhylipSequential)
  let interleaved = phylip_demo_write(alignment, @bio.PhylipInterleaved)
  let reparsed = phylip_demo_parse(interleaved)
  let sequential_lines = sequential.split("\n").to_array()
  println("  sequential first row=" + sequential_lines[1].to_owned())
  println(
    "  stable interleaved round-trip=" +
    (phylip_demo_write(reparsed, @bio.PhylipInterleaved) == interleaved).to_string(),
  )
  println(
    "\nThe demo parses and writes existing artifacts; it does not launch an aligner.",
  )
}