// vega-view-transforms `gridLayout`: arrange groups in rows and columns.

///|
priv struct GridOpt {
  /// "each" | "all" | "" for columns / rows
  align_col : String
  align_row : String
  pad_col : Double
  pad_row : Double
  /// number of columns (0 means one per group)
  columns : Int32
  center_col : Bool
  center_row : Bool
  /// measure groups by their size (`bounds: flush`) instead of their bounds
  flush : Bool
  anchor_x : Double
  anchor_y : Double
  /// "start" | "middle" | "end" for the grid anchor
  anchor_col : String
  anchor_row : String
}

///|
fn bbox_of(item : @scene.Item, flush : Bool) -> @scene.Bounds {
  if flush {
    let b = @scene.Bounds::new()
    ignore(b.set(0.0, 0.0, item.num0("width"), item.num0("height")))
    b
  } else {
    let b = item.bounds.copy()
    if b.is_empty() {
      ignore(b.set(0.0, 0.0, 0.0, 0.0))
    } else {
      ignore(b.translate(-item.num0("x"), -item.num0("y")))
    }
    b
  }
}

///|
fn offset_value(v : Double) -> Double {
  if v < 0.0 {
    ceil(-v)
  } else {
    0.0
  }
}

///|
/// Lay out `groups` (whose containing marks are `marks`), returning the
/// bounds of the resulting grid.
fn grid_layout(
  groups : Array[@scene.Item],
  marks : Array[@scene.Mark],
  o : GridOpt,
) -> @scene.Bounds {
  let n = groups.length()
  let ncols = if o.columns > 0 { o.columns } else { n }
  let nrows = if ncols <= 0 { 1 } else { (n + ncols - 1) / ncols }
  let x_offset : Array[Double] = Array::make(n, 0.0)
  let y_offset : Array[Double] = Array::make(n, 0.0)
  let x_extent : Array[Double] = Array::make(ncols, 0.0)
  let y_extent : Array[Double] = Array::make(nrows, 0.0)
  let dx : Array[Double] = Array::make(n, 0.0)
  let dy : Array[Double] = Array::make(n, 0.0)
  let boxes : Array[@scene.Bounds] = []
  let mut x_max = 0.0
  let mut y_max = 0.0
  for i in 0.. 1 {
    for i in 0.. 0.0 {
        dx[i] = dx[i] + v / 2.0
      }
    }
  }
  if align_row != "" && o.center_row && ncols != 1 {
    for i in 0.. 0.0 {
        dy[i] = dy[i] + v / 2.0
      }
    }
  }
  // anchor
  let bounds = @scene.Bounds::new()
  ignore(bounds.set(0.0, 0.0, 0.0, 0.0))
  for i in 0.. ax = ax - bounds.width()
    "middle" => ax = ax - bounds.width() / 2.0
    _ => ()
  }
  match o.anchor_row {
    "end" => ay = ay - bounds.height()
    "middle" => ay = ay - bounds.height() / 2.0
    _ => ()
  }
  ax = round(ax)
  ay = round(ay)
  // apply
  ignore(bounds.clear())
  for i in 0..