///|
fn baseml_demo_control() -> @bio.BasemlControl {
  @bio.baseml_parse_control(@bio.baseml_example_control_text()) catch {
    BasemlError(message) => abort("failed to parse BASEML control: " + message)
  }
}

///|
fn baseml_demo_results() -> @bio.BasemlResults {
  @bio.baseml_parse_results(@bio.baseml_example_results_text()) catch {
    BasemlError(message) => abort("failed to parse BASEML results: " + message)
  }
}

///|
fn baseml_demo_null_model() -> @bio.BasemlResults {
  @bio.baseml_parse_results(
    "BASEML (in paml version 4.7, January 2013) alignment.phylip JC69 dGamma\n" +
    "lnL(ntime: 7 np: 8): -330.000000 +0.000000\n" +
    "0.01 0.01 0.01 0.01 0.01 0.01 0.01 1.0\n" +
    "tree length = 0.018\n" +
    "((A:0.004,B:0.004):0.002,C:0.008);\n",
  ) catch {
    BasemlError(message) => abort("failed to parse null model: " + message)
  }
}

///|
fn main {
  println("=== Biopython Bio.Phylo.PAML.baseml Offline Demo ===")
  let control = baseml_demo_control()
  let model = match control.model_number() {
    Some(value) => value
    None => abort("control has no model")
  }
  println("\n1. Strict BASEML control parsing")
  println("  sequence file: " + control.sequence_file())
  println("  tree file: " + control.tree_file())
  println(
    "  model: " +
    model.to_string() +
    " (" +
    (@bio.baseml_model_name(model) catch {
      BasemlError(message) => abort(message)
    }) +
    ")",
  )
  let canonical = @bio.baseml_write_control(control)
  let reparsed = @bio.baseml_parse_control(canonical) catch {
    BasemlError(message) => abort("control round-trip failed: " + message)
  }
  println("  canonical round-trip: " + (reparsed == control).to_string())

  let result = baseml_demo_results()
  println("\n2. PAML result, parameters, and standard errors")
  println(
    "  PAML=" +
    result.version() +
    ", model=" +
    result.model_description() +
    ", lnL=" +
    result.ln_likelihood().to_string(),
  )
  println(
    "  parameters=" +
    result.parameter_count().to_string() +
    ", SE values=" +
    result.standard_errors().length().to_string() +
    ", tree length=" +
    result.tree_length().to_string(),
  )

  println("\n3. REV rate matrix and discrete gamma")
  match result.q_matrix() {
    Some(matrix) => {
      let average = match matrix.average_ts_tv() {
        Some(value) => value.to_string()
        None => "not reported"
      }
      println(
        "  Q dimensions=" +
        matrix.rows().length().to_string() +
        "x" +
        matrix.rows()[0].length().to_string() +
        ", average Ts/Tv=" +
        average,
      )
    }
    None => abort("result has no Q matrix")
  }
  let alpha_text = match result.alpha() {
    Some(value) => value.to_string()
    None => "not reported"
  }
  println(
    "  rate categories=" +
    result.rates().length().to_string() +
    ", alpha=" +
    alpha_text,
  )

  let comparison = @bio.baseml_likelihood_ratio(
    baseml_demo_null_model(),
    result,
  ) catch {
    BasemlError(message) => abort("likelihood-ratio test failed: " + message)
  }
  let bic = result.bic(222) catch {
    BasemlError(message) => abort("BIC calculation failed: " + message)
  }
  println("\n4. Information criteria and nested-model LRT")
  println("  REV AIC=" + result.aic().to_string() + ", BIC=" + bic.to_string())
  println(
    "  statistic=" +
    comparison.statistic().to_string() +
    ", df=" +
    comparison.degrees_of_freedom().to_string() +
    ", p=" +
    comparison.p_value().to_string() +
    ", significant=" +
    comparison.significant().to_string(),
  )

  println(
    "\nThe demo parses existing BASEML artifacts; it does not launch PAML.",
  )
}