///|
/// Duplicate an ordered source selection into a destination selection.
pub struct Duplicate {
  priv destination : &Object
  priv source : &Object
}

///|
/// Source values are collected before any destination is changed.
/// The source cycles independently for each destination entity.
pub fn Duplicate::new(source~ : &Object, destination~ : &Object) -> Duplicate {
  { source, destination, }
}

///|
/// Copy source contents to selected cells without changing formatting.
///
/// ```mbt check
/// test {
///   let table = @tabular.Table::from_rows([["a", "b"], ["c", "d"]])
///   table.duplicate(
///     @tabular.Duplicate::new(
///       source=@tabular.Rows::one(0),
///       destination=@tabular.Rows::one(1),
///     ),
///   )
///   |> ignore
///   assert_eq(table.rows, [["a", "b"], ["a", "b"]])
/// }
/// ```
pub fn Table::duplicate(self : Table, duplication : Duplicate) -> Table {
  let records = @papergrid.IterRecords::new(self.rows)
  let row_count = records.count_rows()
  let col_count = records.count_cols()
  let valid = (cell : (Int, Int)) => {
    cell.0 >= 0 && cell.0 < row_count && cell.1 >= 0 && cell.1 < col_count
  }
  let source : Array[String] = []
  for group in duplication.source.cells(self) {
    for cell in group {
      if valid(cell) {
        source.push(records.get_cell(cell.0, cell.1))
      }
    }
  }
  guard !source.is_empty() else { return self }
  for group in duplication.destination.cells(self) {
    let mut index = 0
    for cell in group {
      if valid(cell) {
        let row = self.rows[cell.0]
        while row.length() <= cell.1 {
          row.push("")
        }
        row[cell.1] = source[index % source.length()]
        index += 1
      }
    }
  }
  self.finish_legacy_change()
}