///|
fn choose_for_shelf(
  rect : Rect,
  x : Int,
  y : Int,
  shelf_h : Int,
  options : PackOptions,
) -> (Int, Int)? {
  let mut chosen : (Int, Int)? = None
  for candidate in orientation_candidates(rect, options.allow_rotate) {
    let (w, h) = candidate
    if x + w <= right_limit(options) &&
      y + shelf_h.max(h) <= bottom_limit(options) {
      match chosen {
        None => chosen = Some(candidate)
        Some(prev) if better_orientation(candidate, prev) =>
          chosen = Some(candidate)
        _ => ()
      }
    }
  }
  chosen
}

///|
fn pack_shelf(rects : ArrayView[Rect], options : PackOptions) -> Layout {
  let placements : Array[Placed] = []
  let unpacked : Array[Rect] = []
  let ordered = sorted_rects(rects, options.sort)
  let start_x = options.padding.outer
  let mut x = start_x
  let mut y = options.padding.outer
  let mut shelf_h = 0
  for rect in ordered {
    let fit_here = choose_for_shelf(rect, x, y, shelf_h, options)
    let candidate = match fit_here {
      Some(size) => Some((x, y, size))
      None => {
        if shelf_h > 0 {
          y = y + shelf_h + options.padding.inner
          x = start_x
          shelf_h = 0
        }
        match choose_for_shelf(rect, x, y, shelf_h, options) {
          Some(size) => Some((x, y, size))
          None => None
        }
      }
    }
    match candidate {
      Some((px, py, (w, h))) => {
        placements.push(make_placed(rect, px, py, w, h))
        x = px + w + options.padding.inner
        shelf_h = shelf_h.max(h)
      }
      None => unpacked.push(rect)
    }
  }
  make_layout(options.bin, placements, unpacked, ShelfFirstFit)
}