///|
/// Batchelor Demo - Single-cell batch correction methods.
/// Inspired by Bioconductor batchelor package.

///|
fn main {
  println("=== Batchelor Single-cell Batch Correction Demo ===")
  println("Inspired by Bioconductor batchelor package")
  println("")

  println("1. Generating simulated single-cell data with batch effects...")
  let (batches, batch_names) = @bio.batchelor_create_example_data(
    2, 100, 500, 2.0,
  )

  let mut i = 0
  while i < batches.length() {
    println(
      "   \{batch_names[i]}: \{batches[i].length().to_string()} cells, \{batches[i][0].length().to_string()} genes",
    )
    i = i + 1
  }
  println("")

  println("2. Evaluating batch mixing before correction...")
  let raw_data : Array[Array[Double]] = Array(capacity=0)
  for cell in batches[0] {
    raw_data.push(cell)
  }
  for cell in batches[1] {
    raw_data.push(cell)
  }

  let raw_batch_indices : Array[Int32] = Array(capacity=0)
  let mut idx = 0
  while idx < 100 {
    raw_batch_indices.push(0)
    idx = idx + 1
  }
  idx = 0
  while idx < 100 {
    raw_batch_indices.push(1)
    idx = idx + 1
  }

  let raw_score = @bio.compute_batch_mixing_score(raw_data, raw_batch_indices)
  println(
    "   Batch mixing score (higher = better mixing): \{raw_score.to_string()}",
  )
  println("")

  println("3. Method 1: rescaleBatches (simple scaling correction)...")
  let rescale_result = @bio.rescale_batches(batches, batch_names)
  let rescale_score = @bio.compute_batch_mixing_score(
    rescale_result.corrected,
    rescale_result.batch_indices,
  )
  println(
    "   Corrected dimensions: \{rescale_result.corrected.length().to_string()} cells, \{rescale_result.corrected[0].length().to_string()} genes",
  )
  println(
    "   Batch mixing score after rescaleBatches: \{rescale_score.to_string()}",
  )
  println(
    "   Improvement: +\{((rescale_score - raw_score) / raw_score * 100.0).to_string()}%",
  )
  println("")

  println("4. Method 2: fastMNN (Mutual Nearest Neighbors correction)...")
  println("   Performing PCA and MNN correction...")
  let fastmnn_result = @bio.fast_mnn(batches, batch_names, 5, 50)
  println(
    "   Corrected dimensions: \{fastmnn_result.corrected.length().to_string()} cells, \{fastmnn_result.corrected[0].length().to_string()} dimensions",
  )
  println(
    "   Number of MNN pairs found: \{fastmnn_result.mnn_pairs.length().to_string()}",
  )
  println("   Variance explained by top 5 PCs:")
  let mut j = 0
  while j < 5 && j < fastmnn_result.var_explained.length() {
    println(
      "     PC\{(j + 1).to_string()}: \{(fastmnn_result.var_explained[j] * 100.0).to_string()}%",
    )
    j = j + 1
  }

  let fastmnn_score = @bio.compute_batch_mixing_score(
    fastmnn_result.corrected,
    fastmnn_result.batch_indices,
  )
  println("   Batch mixing score after fastMNN: \{fastmnn_score.to_string()}")
  println(
    "   Improvement: +\{((fastmnn_score - raw_score) / raw_score * 100.0).to_string()}%",
  )
  println("")

  println("5. Comparing batch correction methods:")
  println("   Method               | Batch Mixing Score")
  println("   -------------------------------------------")
  println("   Raw data             | \{raw_score.to_string()}")
  println("   rescaleBatches       | \{rescale_score.to_string()}")
  println("   fastMNN              | \{fastmnn_score.to_string()}")
  println("")

  println("6. Finding mutual nearest neighbors between batches...")
  let mnn_pairs = @bio.find_mutual_nearest_neighbors(batches[0], batches[1], 5)
  println(
    "   Found \{mnn_pairs.length().to_string()} MNN pairs between Batch_0 and Batch_1",
  )
  if mnn_pairs.length() > 0 {
    println("   First 5 MNN pairs:")
    let mut k = 0
    while k < 5 && k < mnn_pairs.length() {
      let pair = mnn_pairs[k]
      println(
        "     (Batch_0 cell \{pair.0.to_string()}, Batch_1 cell \{pair.1.to_string()})",
      )
      k = k + 1
    }
  }
  println("")

  println("=== Demo completed successfully! ===")
  println("")
  println("Key takeaways:")
  println(
    "- rescaleBatches: Simple and fast, good for global scaling differences",
  )
  println("- fastMNN: More sophisticated, handles complex batch effects")
  println(
    "- Batch mixing score quantifies how well batches are mixed after correction",
  )
}