///|
pub(all) struct GridSpec {
  columns : Int
  rows : Int
  gap_x : Int
  gap_y : Int
  padding : @core.EdgeInsets
} derive(Debug, Eq)

///|
pub(all) enum HorizontalAlign {
  Start
  Center
  End
} derive(Debug, Eq)

///|
pub(all) enum VerticalAlign {
  Top
  Middle
  Bottom
} derive(Debug, Eq)

///|
pub fn stack_vertical(
  region~ : @scene.Rect,
  item_heights~ : Array[Int],
  gap~ : Int,
) -> Array[@scene.Rect] {
  let frames : Array[@scene.Rect] = []
  let mut current_y = region.y
  item_heights.each(height => {
    frames.push({ x: region.x, y: current_y, width: region.width, height })
    current_y = current_y + height + gap
  })
  frames
}

///|
pub fn grid(region~ : @scene.Rect, spec~ : GridSpec) -> Array[@scene.Rect] {
  if spec.columns <= 0 || spec.rows <= 0 {
    []
  } else {
    let inner = region.inset(spec.padding)
    let cell_width = (inner.width - spec.gap_x * (spec.columns - 1)) /
      spec.columns
    let cell_height = (inner.height - spec.gap_y * (spec.rows - 1)) / spec.rows
    Array::makei(spec.columns * spec.rows, index => {
      let row = index / spec.columns
      let column = index % spec.columns
      {
        x: inner.x + column * (cell_width + spec.gap_x),
        y: inner.y + row * (cell_height + spec.gap_y),
        width: cell_width,
        height: cell_height,
      }
    })
  }
}

///|
fn resolve_x(
  region : @scene.Rect,
  width : Int,
  h_align : HorizontalAlign,
) -> Int {
  match h_align {
    Start => region.x
    Center => region.x + (region.width - width) / 2
    End => region.x + region.width - width
  }
}

///|
fn resolve_y(
  region : @scene.Rect,
  height : Int,
  v_align : VerticalAlign,
) -> Int {
  match v_align {
    Top => region.y
    Middle => region.y + (region.height - height) / 2
    Bottom => region.y + region.height - height
  }
}

///|
pub fn anchor(
  region~ : @scene.Rect,
  size~ : @core.Size,
  h_align~ : HorizontalAlign,
  v_align~ : VerticalAlign,
  margin~ : @core.EdgeInsets,
) -> @scene.Point {
  let inner = region.inset(margin)
  @scene.Point::at(
    resolve_x(inner, size.width, h_align),
    resolve_y(inner, size.height, v_align),
  )
}

///|
pub fn rect_overflow(
  rect : @scene.Rect,
  region : @scene.Rect,
) -> @core.RectOverflow {
  {
    left: Int::max(region.x - rect.x, 0),
    top: Int::max(region.y - rect.y, 0),
    right: Int::max(rect.x + rect.width - region.x - region.width, 0),
    bottom: Int::max(rect.y + rect.height - region.y - region.height, 0),
  }
}