///|
/// Phylogenetic Tree Metrics Demo - Advanced phylogenetic analysis.
///
/// Demonstrates tree balance, patristic distances, bipartitions,
/// internal node counting, and Robinson-Foulds distance comparison.
///
/// Run: moon run examples/phylo_metrics_demo/main.mbt
fn format_leaves(leaves : Array[String]) -> String {
  let mut result = "["
  let mut i = 0
  while i < leaves.length() {
    if i > 0 {
      result = result + ", "
    }
    result = result + leaves[i]
    i = i + 1
  }
  result + "]"
}

///|
fn main {
  println("=== Phylogenetic Tree Metrics Demo ===\n")

  // === 1. Create demo trees ===
  println("1. Create Demo Trees:")
  println("=====================")
  let tree1 = @src.create_demo_tree()
  let tree2 = @src.create_demo_tree2()
  println("  Tree 1 (demo): ((A:1, B:1):0.5, (C:1, D:1):0.5)")
  println("  Tree 2 (demo2): ((A:1, C:1):0.5, (B:1, D:1):0.5)")
  let tree1_root = tree1.get_root()
  let tree2_root = tree2.get_root()
  println("  Tree 1 terminals: " + tree1_root.count_terminals().to_string())
  println("  Tree 2 terminals: " + tree2_root.count_terminals().to_string())

  // === 2. Total branch length ===
  println("\n2. Total Branch Length:")
  println("========================")
  let total1 = tree1.total_branch_length()
  let total2 = tree2.total_branch_length()
  println("  Tree 1 total branch length: " + total1.to_string())
  println("  Tree 2 total branch length: " + total2.to_string())

  // === 3. Maximum depth ===
  println("\n3. Maximum Depth (root to deepest leaf):")
  println("=========================================")
  let depth1 = tree1.max_depth()
  let depth2 = tree2.max_depth()
  println("  Tree 1 max depth: " + depth1.to_string())
  println("  Tree 2 max depth: " + depth2.to_string())

  // === 4. Leaf names ===
  println("\n4. Leaf Names:")
  println("==============")
  let leaves1 = tree1_root.get_leaf_names()
  let leaves2 = tree2_root.get_leaf_names()
  println("  Tree 1 leaves: " + format_leaves(leaves1))
  println("  Tree 2 leaves: " + format_leaves(leaves2))

  // === 5. Colless index (tree balance) ===
  println("\n5. Colless Index (tree balance, lower = more balanced):")
  println("========================================================")
  let colless1 = tree1.colless_index()
  let colless2 = tree2.colless_index()
  println("  Tree 1 Colless index: " + colless1.to_string())
  println("  Tree 2 Colless index: " + colless2.to_string())
  if colless1 == 0 {
    println("  Tree 1 is perfectly balanced.")
  }

  // === 6. Patristic distance ===
  println("\n6. Patristic Distance (between pairs of leaves):")
  println("=================================================")
  let pairs = [("A", "B"), ("A", "C"), ("A", "D"), ("B", "C"), ("C", "D")]
  let mut pi = 0
  while pi < pairs.length() {
    let (n1, n2) = pairs[pi]
    let dist = tree1.patristic_distance(n1, n2)
    match dist {
      Some(d) => println("  Tree1 " + n1 + " - " + n2 + ": " + d.to_string())
      None => println("  Tree1 " + n1 + " - " + n2 + ": N/A")
    }
    pi = pi + 1
  }

  // === 7. Patristic distance matrix ===
  println("\n7. Patristic Distance Matrix:")
  println("==============================")
  let matrix = tree1.patristic_distance_matrix()
  println("  Number of pairwise distances: " + matrix.length().to_string())
  let mut mi = 0
  while mi < matrix.length() {
    let (n1, n2, d) = matrix[mi]
    println("  " + n1 + " - " + n2 + ": " + d.to_string())
    mi = mi + 1
  }

  // === 8. Internal node count ===
  println("\n8. Internal Node Count:")
  println("=======================")
  let internal1 = tree1.count_internal()
  let internal2 = tree2.count_internal()
  println("  Tree 1 internal nodes: " + internal1.to_string())
  println("  Tree 2 internal nodes: " + internal2.to_string())

  // === 9. Bipartitions ===
  println("\n9. Bipartitions (tree splits):")
  println("===============================")
  let biparts1 = tree1_root.get_bipartitions()
  let biparts2 = tree2_root.get_bipartitions()
  println("  Tree 1 bipartitions: " + biparts1.length().to_string())
  let mut bi = 0
  while bi < biparts1.length() {
    let (left, right) = biparts1[bi]
    println(
      "  [" +
      bi.to_string() +
      "] " +
      format_leaves(left) +
      " | " +
      format_leaves(right),
    )
    bi = bi + 1
  }
  println("  Tree 2 bipartitions: " + biparts2.length().to_string())
  let mut bi2 = 0
  while bi2 < biparts2.length() {
    let (left, right) = biparts2[bi2]
    println(
      "  [" +
      bi2.to_string() +
      "] " +
      format_leaves(left) +
      " | " +
      format_leaves(right),
    )
    bi2 = bi2 + 1
  }

  // === 10. Robinson-Foulds distance ===
  println("\n10. Robinson-Foulds Distance:")
  println("=============================")
  // Same tree comparison
  let rf_same = @src.robinson_foulds_distance(tree1, tree1)
  println("  Tree1 vs Tree1 (same): " + rf_same.to_string())
  // Different tree comparison
  let rf_diff = @src.robinson_foulds_distance(tree1, tree2)
  println("  Tree1 vs Tree2 (different topology): " + rf_diff.to_string())
  if rf_same == 0 {
    println("  RF=0 confirms identical tree topologies.")
  }
  if rf_diff > 0 {
    println("  RF>0 confirms different tree topologies.")
  }

  println("\n=== Phylogenetic Tree Metrics Demo Complete ===")
}