///|
fn scuttle_demo_doubles(values : Array[Double]) -> String {
  values.map(fn(value) { value.to_string() }).join(", ")
}

///|
fn main {
  println("=== Bioconductor scuttle 1.23.1 Demo ===")

  let outlier_config = @bio.ScuttleOutlierConfig::create(
    nmads=2.0,
    direction=@bio.scuttle_outlier_higher(),
    batches=["A", "A", "A", "B", "B", "B"],
  ) catch {
    _ => abort("failed to create the outlier configuration")
  }
  let outliers = @bio.scuttle_is_outlier(
    [9.0, 10.0, 11.0, 98.0, 100.0, 150.0],
    config=outlier_config,
  ) catch {
    _ => abort("batch-aware outlier detection failed")
  }
  println("\n1. Batch-aware MAD outlier detection")
  println("  batches: " + outliers.batch_names.join(", "))
  println("  upper thresholds: " + scuttle_demo_doubles(outliers.higher))
  println("  discarded observations: " + outliers.discarded_count().to_string())

  let counts = [
    [8.0, 4.0, 16.0, 8.0],
    [2.0, 6.0, 4.0, 12.0],
    [0.0, 2.0, 0.0, 4.0],
    [10.0, 8.0, 20.0, 16.0],
  ]
  let controls = @bio.ScuttleNamedCellSubset::create("controls", [0, 1]) catch {
    _ => abort("failed to create the control-cell subset")
  }
  let qc = @bio.scuttle_per_feature_qc(counts, subsets=[controls]) catch {
    _ => abort("per-feature QC failed")
  }
  println("\n2. Per-feature QC with a named cell subset")
  println("  means: " + scuttle_demo_doubles(qc.means))
  println(
    "  detected percentages: " + scuttle_demo_doubles(qc.detected_percent),
  )
  println(
    "  control/global ratios: " + scuttle_demo_doubles(qc.subset_ratios[0]),
  )

  let pathway_a = @bio.ScuttleFeatureSet::create("PathwayA", [0, 1]) catch {
    _ => abort("failed to create PathwayA")
  }
  let pathway_b = @bio.ScuttleFeatureSet::create("PathwayB", [1, 2, 3]) catch {
    _ => abort("failed to create PathwayB")
  }
  let aggregated = @bio.scuttle_aggregate_feature_sets(counts, [
    pathway_a, pathway_b,
  ]) catch {
    _ => abort("feature-set aggregation failed")
  }
  println("\n3. Overlapping feature-set aggregation")
  for index in 0.. abort("batch coverage equalization failed")
  }
  println("\n4. Exact batch coverage equalization")
  println("  batch summaries: " + scuttle_demo_doubles(balanced.summaries))
  println("  proportions: " + scuttle_demo_doubles(balanced.proportions))

  let experiment = @bio.SingleCellExperiment::new(
    counts,
    ["G1", "G2", "G3", "G4"],
    ["C1", "C2", "C3", "C4"],
  )
  experiment.col_data["batch"] = ["A", "A", "B", "B"]
  experiment.metadata["source"] = "scuttle_demo"
  let qc_output = @bio.scuttle_per_feature_qc_sce(experiment, subsets=[controls]) catch {
    _ => abort("SCE per-feature QC failed")
  }
  let downsampled = @bio.scuttle_downsample_sce(
    qc_output.experiment,
    0.5,
    output_assay="half",
    seed=42,
  ) catch {
    _ => abort("SCE downsampling failed")
  }
  let pathway_sce = @bio.scuttle_aggregate_feature_sets_sce(
    downsampled,
    [pathway_a, pathway_b],
    assay_names=["counts", "half"],
  ) catch {
    _ => abort("SCE feature aggregation failed")
  }
  println("\n5. Immutable SingleCellExperiment integration")
  println(
    "  QC rows written: " +
    qc_output.experiment.row_data["scuttle.mean"].length().to_string(),
  )
  println(
    "  downsampled assay rows: " +
    downsampled.assays["half"].length().to_string(),
  )
  println("  aggregated rows: " + pathway_sce.row_names.join(", "))
  println(
    "  original unchanged: " +
    (!experiment.row_data.contains("scuttle.mean") &&
    !experiment.assays.contains("half")).to_string(),
  )
  println("\nscuttle demo completed.")
}