///|
/// Return the most recently selected surviving axes.
/// An empty Figure receives a new 1-by-1 subplot using its current bounds.
/// axes() remains the separate first/default-panel convenience API.
///
/// ```mbt check
/// test {
///   let fig = @scene.figure()
///   let panels = try! fig.subplots(1, 2)
///   panels[1].set_title("last")
///   assert_eq(fig.gca().title(), Some("last"))
///   try! panels[1].remove()
///   panels[0].set_title("remaining")
///   assert_eq(fig.gca().title(), Some("remaining"))
/// }
/// ```
pub fn Figure::gca(self : Figure) -> Axes {
  match self.axes_selection.last() {
    Some(state) => { figure: self, state, }
    None => self.axes()
  }
}

///|
/// Select axes in this Figure, returning the selected handle.
/// Foreign or detached handles raise InvalidReference without changing state.
/// Selection does not change creation or SVG drawing order.
///
/// ```mbt check
/// test {
///   let fig = @scene.figure()
///   let panels = try! fig.subplots(1, 2)
///   panels[0].set_title("first")
///   ignore(try! fig.sca(panels[0]))
///   assert_eq(fig.gca().title(), Some("first"))
///   assert_eq(fig.axes_list().length(), 2)
/// }
/// ```
pub fn Figure::sca(self : Figure, axes : Axes) -> Axes raise @core.DataError {
  if !physical_equal(self, axes.figure) || !axes.is_attached() {
    raise @core.DataError::InvalidReference(
      message="cannot select detached or foreign axes",
    )
  }
  self.axes_selection.retain(state => !physical_equal(state, axes.state))
  self.axes_selection.push(axes.state)
  axes
}

///|
fn SubFigureData::forget_axes(
  self : SubFigureData,
  state : @ref.Ref[AxesData],
) -> Unit {
  self.axes.retain(item => !physical_equal(item, state))
  for child in self.subfigures {
    child.val.forget_axes(state)
  }
}

///|
/// Remove axes from this Figure, preserving the order of remaining panels.
/// This also removes membership in nested SubFigures. The current selection
/// falls back to the most recently selected surviving panel. Removing the
/// last panel leaves the Figure empty until axes() or gca() is requested.
/// Foreign or detached handles raise InvalidReference before any state changes.
/// Figure-level colorbars are independent and remain registered.
///
/// ```mbt check
/// test {
///   let fig = @scene.figure()
///   let ax = fig.axes()
///   try! fig.delaxes(ax)
///   assert_eq(fig.axes_list().length(), 0)
///   assert_true(!ax.is_attached())
///   assert_true(fig.gca().is_attached())
/// }
/// ```
pub fn Figure::delaxes(
  self : Figure,
  axes : Axes,
) -> Unit raise @core.DataError {
  if !physical_equal(self, axes.figure) || !axes.is_attached() {
    raise @core.DataError::InvalidReference(
      message="cannot remove axes outside this figure",
    )
  }
  self.axes.retain(state => !physical_equal(state, axes.state))
  self.axes_selection.retain(state => !physical_equal(state, axes.state))
  for data in self.subfigures {
    data.val.forget_axes(axes.state)
  }
  for horizontal in [true, false] {
    let remaining_states = axes.unregister_shared_axis(horizontal)
    if remaining_states.length() > 0 {
      let state = remaining_states[0]
      let axis = if horizontal { state.val.x_axis } else { state.val.y_axis }
      axis.bind_locator_data()
      axis.bind_locator_data(minor=true)
      axis.bind_formatter_axis()
      axis.bind_formatter_axis(minor=true)
    }
  }
}

///|
/// Remove this panel from its Figure and any containing SubFigure.
/// Other handles to this panel become detached; its artists remain inspectable.
/// Repeated removal raises InvalidReference. figure() retains the source Figure
/// as provenance, and does not imply continued attachment.
///
/// ```mbt check
/// test {
///   let fig = @scene.figure()
///   let ax = try! fig.add_axes(0.25, 0.25, 0.5, 0.5)
///   ax.set_title("removed")
///   try! ax.remove()
///   assert_true(!fig.to_svg().contains(">removed<"))
///   assert_eq(ax.title(), Some("removed"))
/// }
/// ```
pub fn Axes::remove(self : Axes) -> Unit raise @core.DataError {
  self.figure.delaxes(self)
}

///|
/// Remove axes created directly through this SubFigure.
/// Axes in another region or a child SubFigure raise InvalidReference.
///
/// ```mbt check
/// test {
///   let fig = @scene.figure()
///   let grid = try! fig.grid_spec(1, 1)
///   let sub = fig.add_subfigure(try! grid.cell_spec(0, 0))
///   let ax = try! sub.subplots(1, 1)[0]
///   try! sub.delaxes(ax)
///   assert_eq(sub.axes_list().length(), 0)
///   assert_eq(fig.axes_list().length(), 0)
/// }
/// ```
pub fn SubFigure::delaxes(
  self : SubFigure,
  axes : Axes,
) -> Unit raise @core.DataError {
  if !self.data.val.axes.any(state => physical_equal(state, axes.state)) {
    raise @core.DataError::InvalidReference(
      message="axes does not belong to this subfigure",
    )
  }
  self.figure.delaxes(axes)
  self.data.val.forget_axes(axes.state)
}