///|
fn blast_demo_parse(text : String) -> @bio.BlastXmlDocument {
  @bio.blast_xml_parse(text) catch {
    BlastXmlError(message) => abort("BLAST XML parse failed: " + message)
  }
}

///|
fn blast_demo_write(document : @bio.BlastXmlDocument) -> String {
  @bio.blast_xml_write(document) catch {
    BlastXmlError(message) => abort("BLAST XML write failed: " + message)
  }
}

///|
fn blast_demo_write_xml2(document : @bio.BlastXmlDocument) -> String {
  @bio.blast_xml_write_as(document, @bio.BlastXmlFormat::Xml2) catch {
    BlastXmlError(message) => abort("BLAST XML conversion failed: " + message)
  }
}

///|
fn blast_demo_xml1() -> String {
  "\n" +
  "\n" +
  "" +
  "blastp" +
  "BLASTP 2.15.0+" +
  "offline demo" +
  "swissprot" +
  "Query_1" +
  "protein query" +
  "12" +
  "" +
  "BLOSUM62" +
  "0.001" +
  "11" +
  "1" +
  "" +
  "" +
  "1" +
  "Query_1" +
  "protein query" +
  "12" +
  "" +
  "1sp|DEMO.1|" +
  "demonstration targetDEMO" +
  "20" +
  "142.5" +
  "1002e-12" +
  "17" +
  "18" +
  "00" +
  "77" +
  "18" +
  "MKKL-AAAMKKLTAAA" +
  "MKKL AAA" +
  "482816" +
  "183558113" +
  "72" +
  "4627826878" +
  "0.041" +
  "0.267" +
  "0.14" +
  "" +
  "\n"
}

///|
fn blast_demo_xml2() -> String {
  "\n" +
  "" +
  "blastxBLASTX 2.15.0+" +
  "offline demoswissprot" +
  "BLOSUM62" +
  "0.001111" +
  "2" +
  "translated_querynegative frame query" +
  "301" +
  "sp|PRIMARY.1|PRIMARY" +
  "primary target83333" +
  "sp|SECONDARY.1|SECONDARY" +
  "secondary target224308" +
  "Bacillus subtilis10" +
  "13060" +
  "1e-83" +
  "157-1" +
  "243" +
  "0MKTMKT" +
  "" +
  "\n"
}

///|
fn main {
  println("=== Biopython Bio.Blast XML1/XML2 Offline Demo ===")
  let xml1 = blast_demo_parse(blast_demo_xml1())
  let record = xml1.records()[0]
  let best = record.best_hit().unwrap()
  let hsp = best.best_hsp().unwrap()
  let statistics = record.statistics().unwrap()
  println("\n1. XML1 records, best hit, and statistics")
  println(
    "  program=" +
    xml1.program() +
    ", records=" +
    xml1.records().length().to_string() +
    ", hits=" +
    xml1.total_hits().to_string(),
  )
  println(
    "  best=" +
    best.primary_description().accession() +
    ", E-value=" +
    hsp.evalue().to_string() +
    ", identity=" +
    hsp.identity_fraction().to_string(),
  )
  println(
    "  database sequences=" +
    statistics.database_sequences().to_string() +
    ", letters=" +
    statistics.database_letters().to_string(),
  )

  println("\n2. Compressed alignment path")
  for coordinate in hsp.coordinates() {
    println(
      "  target=" +
      coordinate.target().to_string() +
      ", query=" +
      coordinate.query().to_string(),
    )
  }

  let xml2 = blast_demo_parse(blast_demo_xml2())
  let translated_hit = xml2.records()[0].hits()[0]
  let translated_hsp = translated_hit.hsps()[0]
  println("\n3. XML2 descriptions and translated coordinates")
  println(
    "  descriptions=" +
    translated_hit.descriptions().length().to_string() +
    ", secondary taxon=" +
    translated_hit.descriptions()[1].scientific_name().unwrap_or("unknown"),
  )
  println(
    "  query frame=" +
    translated_hsp.query_frame().unwrap().to_string() +
    ", coded_by=" +
    translated_hsp.query_coded_by().unwrap_or("undefined"),
  )

  let canonical = blast_demo_write(xml2)
  let converted = blast_demo_write_xml2(xml1)
  println("\n4. Canonical writing and dialect conversion")
  println(
    "  XML2 round-trip stable: " +
    (blast_demo_write(blast_demo_parse(canonical)) == canonical).to_string(),
  )
  println(
    "  XML1 -> XML2 records: " +
    blast_demo_parse(converted).records().length().to_string(),
  )
  println(
    "\nThe demo parses and writes existing artifacts; it does not use NCBI or launch BLAST.",
  )
}