// Grid snapping and deterministic child placement helpers.

///|
pub(all) struct SnapGrid {
  spacing : Double
  origin : @math.Vec2
}

///|
pub fn SnapGrid::new(spacing : Double, origin : @math.Vec2) -> SnapGrid {
  {
    spacing: if spacing.abs() < 0.000000000001 {
      1.0
    } else {
      spacing.abs()
    },
    origin,
  }
}

///|
pub fn SnapGrid::snap_value(self : SnapGrid, value : Double) -> Double {
  let normalized = (value - self.origin.x) / self.spacing
  let bucket = if normalized >= 0.0 {
    (normalized + 0.5).to_int()
  } else {
    (normalized - 0.5).to_int()
  }
  self.origin.x + bucket.to_double() * self.spacing
}

///|
pub fn SnapGrid::snap_point(self : SnapGrid, point : @math.Vec2) -> @math.Vec2 {
  let x_grid = self.snap_value(point.x)
  let y_grid = {
    let normalized = (point.y - self.origin.y) / self.spacing
    let bucket = if normalized >= 0.0 {
      (normalized + 0.5).to_int()
    } else {
      (normalized - 0.5).to_int()
    }
    self.origin.y + bucket.to_double() * self.spacing
  }
  @math.Vec2::new(x_grid, y_grid)
}

///|
pub(all) struct GridPlacement {
  node_id : NodeID
  position : @math.Vec2
} derive(Eq, Debug)

///|
pub fn layout_grid(
  document : ArtboardDocument,
  group_id : NodeID,
  columns : Int,
  gap : Double,
  origin : @math.Vec2,
) -> Array[GridPlacement] {
  let safe_columns = if columns < 1 { 1 } else { columns }
  let children = match document.get_node(group_id) {
    Some(node) =>
      match node.node_type {
        GroupNode(ids) => ids
        _ => []
      }
    None => []
  }
  let placements : Array[GridPlacement] = []
  let mut cell_width = 0.0
  let mut cell_height = 0.0
  for i = 0; i < children.length(); i = i + 1 {
    match document.get_node(children[i]) {
      Some(node) => {
        let bounds = node.local_bounds()
        if bounds.width() > cell_width {
          cell_width = bounds.width()
        }
        if bounds.height() > cell_height {
          cell_height = bounds.height()
        }
      }
      None => ()
    }
  }
  for i = 0; i < children.length(); i = i + 1 {
    let column = i % safe_columns
    let row = i / safe_columns
    let position = @math.Vec2::new(
      origin.x + column.to_double() * (cell_width + gap),
      origin.y + row.to_double() * (cell_height + gap),
    )
    let child_id = children[i]
    match document.get_node(child_id) {
      Some(node) => {
        document.update_node({
          ..node,
          transform: @math.Matrix2D::translation(position.x, position.y),
        })
        placements.push({ node_id: child_id, position })
      }
      None => ()
    }
  }
  placements
}