///|
/// PhyloXML Demo
/// Demonstrates Bio.Phylo.PhyloXML functionality:
///   - Parse PhyloXML text
///   - Display tree names, node structures, branch lengths
///   - Show taxon annotations and sequence annotations
///   - Demonstrate XML serialization (round-trip)
///   - Convert Newick to PhyloXML and back
///   - Get all tips, node counts
///
/// Run: moon run examples/phylo_xml_demo/
fn main {
  println("=== PhyloXML Demo ===")

  // =========================================================================
  // 1. Create sample PhyloXML text
  // =========================================================================
  println("")
  println("--- 1. Sample PhyloXML Input ---")

  let phyloxml_text = "\n" +
    "\n" +
    "  \n" +
    "    Species Tree\n" +
    "    A phylogenetic tree of species\n" +
    "    \n" +
    "      root\n" +
    "      0.0\n" +
    "      \n" +
    "        root_taxon\n" +
    "        Life\n" +
    "        Root\n" +
    "      \n" +
    "      \n" +
    "        mammals\n" +
    "        0.12\n" +
    "        \n" +
    "          Human\n" +
    "          0.05\n" +
    "          \n" +
    "            9606\n" +
    "            Homo sapiens\n" +
    "            Human\n" +
    "            species\n" +
    "          \n" +
    "          \n" +
    "            human_seq\n" +
    "            DNA\n" +
    "            Hs\n" +
    "          \n" +
    "          \n" +
    "            gc_content\n" +
    "            0.41\n" +
    "          \n" +
    "        \n" +
    "        \n" +
    "          Mouse\n" +
    "          0.08\n" +
    "          \n" +
    "            10090\n" +
    "            Mus musculus\n" +
    "            Mouse\n" +
    "          \n" +
    "        \n" +
    "      \n" +
    "      \n" +
    "        yeast\n" +
    "        0.30\n" +
    "        \n" +
    "          4932\n" +
    "          Saccharomyces cerevisiae\n" +
    "          Baker's yeast\n" +
    "        \n" +
    "      \n" +
    "    \n" +
    "  \n" +
    "  \n" +
    "    Gene Tree\n" +
    "    \n" +
    "      GeneA\n" +
    "      0.01\n" +
    "    \n" +
    "  \n" +
    "\n"

  println(phyloxml_text)

  // =========================================================================
  // 2. Parse using parse_phyloxml()
  // =========================================================================
  println("")
  println("--- 2. Parse PhyloXML ---")

  let result = @bio.parse_phyloxml(phyloxml_text)
  println("Parse complete!")
  println("Number of trees: \{result.trees.length()}")

  // =========================================================================
  // 3. Display tree names
  // =========================================================================
  println("")
  println("--- 3. Tree Names ---")

  let tree_names = @bio.get_tree_names(result)
  for i = 0; i < tree_names.length(); i = i + 1 {
    println("  Tree \{i + 1}: \{tree_names[i]}")
  }

  // =========================================================================
  // 4. Display tree structure and node details
  // =========================================================================
  println("")
  println("--- 4. Tree Structure Details ---")

  for tree_idx = 0; tree_idx < result.trees.length(); tree_idx = tree_idx + 1 {
    let tree = result.trees[tree_idx]
    println("")
    println("Tree \{tree_idx + 1}: '\{tree.name}'")
    println("  ID: '\{tree.tree_id}'")
    println("  Description: '\{tree.description}'")

    let rooted = tree.phylogeny_metadata.get("rooted").unwrap_or("false")
    println("  Rooted: \{rooted}")

    let node_count = @bio.phyloxml_get_node_count(tree)
    println("  Total nodes: \{node_count}")

    let tips = @bio.get_all_tips(tree)
    println("  Tips (\{tips.length()}):")
    for tip in tips {
      println("    - \{tip}")
    }

    println("  Root node name: '\{tree.root.name}'")
    println("  Root branch length: \{tree.root.branch_length}")
    println("  Root is leaf: \{tree.root.is_leaf}")
    println("  Root children count: \{tree.root.children.length()}")

    display_node(tree.root, "  ")
  }

  // =========================================================================
  // 5. Show taxon and sequence annotations
  // =========================================================================
  println("")
  println("--- 5. Taxon & Sequence Annotations ---")

  let tree1 = result.trees[0]
  println("Examining '\{tree1.name}':")
  display_annotations(tree1.root, "")

  // =========================================================================
  // 6. XML serialization (round-trip)
  // =========================================================================
  println("")
  println("--- 6. XML Serialization (Round-Trip) ---")

  let serialized = @bio.phyloxml_to_xml(result)
  println("Serialized XML:")
  println(serialized)

  let reparsed = @bio.parse_phyloxml(serialized)
  println("Re-parsed tree count: \{reparsed.trees.length()}")
  println("Re-parsed tree names: \{@bio.get_tree_names(reparsed).join(", ")}")

  // =========================================================================
  // 7. Convert Newick to PhyloXML and back
  // =========================================================================
  println("")
  println("--- 7. Newick <-> PhyloXML Conversion ---")

  let newick = "((Human:0.05,Mouse:0.08)mammals:0.12,Yeast:0.30)root:0.0;"
  println("Input Newick: \{newick}")

  let newick_result = @bio.newick_to_phyloxml(newick, tree_id="newick_tree")
  println("")
  println("Converted to PhyloXML:")
  println("  Trees: \{newick_result.trees.length()}")
  println("  Tree name: '\{newick_result.trees[0].name}'")
  println("  Tree ID: '\{newick_result.trees[0].tree_id}'")

  let newick_xml = @bio.phyloxml_to_xml(newick_result)
  println("")
  println("Generated PhyloXML:")
  println(newick_xml)

  let back_to_newick = @bio.phyloxml_to_newick(newick_result)
  println("")
  println("Converted back to Newick: \{back_to_newick}")

  // =========================================================================
  // 8. Get all tips and node counts
  // =========================================================================
  println("")
  println("--- 8. Tree Utility Functions ---")

  for tree_idx = 0; tree_idx < result.trees.length(); tree_idx = tree_idx + 1 {
    let tree = result.trees[tree_idx]
    let tips = @bio.get_all_tips(tree)
    let node_count = @bio.phyloxml_get_node_count(tree)
    println("Tree '\{tree.name}':")
    println("  Tips: \{tips.length()}")
    println("  Nodes: \{node_count}")
  }

  // =========================================================================
  // 9. Construct tree programmatically with annotations
  // =========================================================================
  println("")
  println("--- 9. Programmatically Constructed Tree ---")

  let human_node = @bio.PhyloXMLNode::new(
    name="Human",
    branch_length=0.05,
    taxon=Some(
      @bio.TaxonInfo::new(
        taxon_id="9606",
        scientific_name="Homo sapiens",
        common_name="Human",
      ),
    ),
    sequence_annotation=Some(
      @bio.SequenceAnnotation::new(
        sequence_id="human_seq",
        molecular_type="DNA",
        symbol="Hs",
      ),
    ),
  )

  let chimp_node = @bio.PhyloXMLNode::new(
    name="Chimpanzee",
    branch_length=0.06,
    taxon=Some(
      @bio.TaxonInfo::new(
        taxon_id="9598",
        scientific_name="Pan troglodytes",
        common_name="Chimpanzee",
      ),
    ),
  )

  let mammals_node = @bio.PhyloXMLNode::new(
    name="Mammalia",
    branch_length=0.10,
    children=[human_node, chimp_node],
    annotations=Map(
      [("group", "mammals"), ("description", "Primate clade")],
      capacity=2,
    ),
  )

  let tree_root = @bio.PhyloXMLNode::new(name="root", branch_length=0.0, children=[
    mammals_node,
  ])

  let constructed_tree = @bio.PhyloXMLTree::new(
    tree_id="constructed",
    name="Constructed Tree",
    description="Tree built programmatically",
    root=tree_root,
  )

  let constructed_result = @bio.PhyloXMLResult::new(trees=[constructed_tree])

  println("Constructed tree XML:")
  let constructed_xml = @bio.phyloxml_to_xml(constructed_result)
  println(constructed_xml)

  // =========================================================================
  // 10. Edge cases
  // =========================================================================
  println("")
  println("--- 10. Edge Cases ---")

  let empty = @bio.parse_phyloxml("")
  println("Empty text: \{empty.trees.length()} trees")

  let simple = @bio.parse_phyloxml(
    "\n\n  Empty\n",
  )
  println("Tree with no clades: '\{simple.trees[0].name}'")
  println("  Root name: '\{simple.trees[0].root.name}'")
  println("  Root is leaf: \{simple.trees[0].root.is_leaf}")

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

///|
/// Recursively display node details including children.
fn display_node(node : @bio.PhyloXMLNode, indent : String) -> Unit {
  println("\{indent}Node: '\{node.name}'")
  println("\{indent}  branch_length: \{node.branch_length}")
  println("\{indent}  is_leaf: \{node.is_leaf}")
  println("\{indent}  children: \{node.children.length()}")

  match node.taxon {
    Some(t) => {
      println("\{indent}  taxon:")
      println("\{indent}    ID: '\{t.taxon_id}'")
      println("\{indent}    Scientific: '\{t.scientific_name}'")
      println("\{indent}    Common: '\{t.common_name}'")
      println("\{indent}    Rank: '\{t.taxon_rank}'")
    }
    None => ()
  }

  match node.sequence_annotation {
    Some(s) => {
      println("\{indent}  sequence:")
      println("\{indent}    ID: '\{s.sequence_id}'")
      println("\{indent}    Type: '\{s.molecular_type}'")
      println("\{indent}    Symbol: '\{s.symbol}'")
    }
    None => ()
  }

  let anns = node.annotations
  if anns.length() > 0 {
    println("\{indent}  annotations (\{anns.length()}):")
    for key in anns.keys() {
      println("\{indent}    \{key} = '\{anns[key]}'")
    }
  }

  for child in node.children {
    display_node(child, indent + "  ")
  }
}

///|
/// Recursively display annotations on nodes.
fn display_annotations(node : @bio.PhyloXMLNode, indent : String) -> Unit {
  if node.name.length() > 0 {
    println("\{indent}'\{node.name}':")
  }
  match node.taxon {
    Some(t) =>
      if t.scientific_name.length() > 0 {
        println("\{indent}  Taxon: \{t.scientific_name} (\{t.common_name})")
      }
    None => ()
  }
  match node.sequence_annotation {
    Some(s) =>
      println("\{indent}  Sequence: \{s.sequence_id} [\{s.molecular_type}]")
    None => ()
  }
  let anns = node.annotations
  for key in anns.keys() {
    println("\{indent}  Annotation: \{key} = '\{anns[key]}'")
  }
  for child in node.children {
    display_annotations(child, indent + "  ")
  }
}