///|
/// A nested figure region backed by a parent Figure and a SubplotSpec span.
///
/// SubFigure axes are regular scene Axes owned by the parent Figure, so they
/// participate in the same SVG render and retain the parent GridSpec's live
/// ratio updates.
pub struct SubFigure {
  figure : Figure
  data : @ref.Ref[SubFigureData]
}

///|
/// Add a nested figure region at a GridSpec span.
pub fn Figure::add_subfigure(
  self : Figure,
  spec : @gridspec.SubplotSpec,
) -> SubFigure {
  let data = @ref.new({ spec, title: None, axes: [], subfigures: [], })
  self.subfigures.push(data)
  { figure: self, data, }
}

///|
/// Return all nested figure regions registered on this Figure.
pub fn Figure::subfigures(self : Figure) -> Array[SubFigure] {
  let result = Array(capacity=self.subfigures.length())
  for data in self.subfigures {
    result.push({ figure: self, data, })
  }
  result
}

///|
/// Return the parent Figure.
pub fn SubFigure::figure(self : SubFigure) -> Figure {
  self.figure
}

///|
/// Return the GridSpec span occupied by this nested figure.
pub fn SubFigure::subplot_spec(self : SubFigure) -> @gridspec.SubplotSpec {
  self.data.val.spec
}

///|
/// Return the current display rectangle of this nested figure.
pub fn SubFigure::rect(self : SubFigure) -> @gridspec.Rect {
  self.figure.subplot_rect(self.data.val.spec)
}

///|
/// Set the nested figure title.
pub fn SubFigure::set_title(self : SubFigure, title : String) -> Unit {
  self.data.val.title = Some(title)
}

///|
/// Matplotlib-compatible alias for `set_title`.
pub fn SubFigure::suptitle(self : SubFigure, title : String) -> Unit {
  self.set_title(title)
}

///|
/// Return the nested figure title, if one was configured.
pub fn SubFigure::title(self : SubFigure) -> String? {
  self.data.val.title
}

///|
/// Add an Axes owned by the parent Figure to this nested region.
pub fn SubFigure::add_subplot(
  self : SubFigure,
  spec : @gridspec.SubplotSpec,
) -> Axes {
  let axes = self.figure.add_subplot(spec)
  self.data.val.axes.push(axes.state)
  axes
}

///|
/// Create a regular nested grid and add all of its axes.
pub fn SubFigure::subplots(
  self : SubFigure,
  rows : Int32,
  columns : Int32,
  hspace? : Double,
  vspace? : Double,
  share_x~ : Bool = false,
  share_y~ : Bool = false,
  width_ratios~ : Array[Double] = [],
  height_ratios~ : Array[Double] = [],
) -> Array[Axes] raise @gridspec.LayoutError {
  let grid = self.data.val.spec.subgridspec(
    rows,
    columns,
    hspace?,
    vspace?,
    width_ratios~,
    height_ratios~,
  )
  let configured = grid.with_shared_axes(
    if share_x {
      @gridspec.ShareMode::All
    } else {
      @gridspec.ShareMode::None
    },
    if share_y {
      @gridspec.ShareMode::All
    } else {
      @gridspec.ShareMode::None
    },
  )
  self.subplots_with_grid(configured)
}

///|
/// Create nested axes according to a GridSpec's sharing constraints.
pub fn SubFigure::subplots_with_grid(
  self : SubFigure,
  grid : @gridspec.GridSpec,
) -> Array[Axes] raise @gridspec.LayoutError {
  let result : Array[Axes] = []
  for row in 0.. SubFigure {
  let data = @ref.new({ spec, title: None, axes: [], subfigures: [], })
  self.data.val.subfigures.push(data)
  { figure: self.figure, data, }
}

///|
/// Return child nested figures registered on this SubFigure.
pub fn SubFigure::subfigures(self : SubFigure) -> Array[SubFigure] {
  let result = Array(capacity=self.data.val.subfigures.length())
  for data in self.data.val.subfigures {
    result.push({ figure: self.figure, data, })
  }
  result
}

///|
/// Return attached axes created directly through this SubFigure handle.
pub fn SubFigure::axes_list(self : SubFigure) -> Array[Axes] {
  let result = Array(capacity=self.data.val.axes.length())
  for state in self.data.val.axes {
    let axes = { figure: self.figure, state, }
    if axes.is_attached() {
      result.push(axes)
    }
  }
  result
}

///|
/// Matplotlib-compatible alias for `axes_list`.
pub fn SubFigure::get_axes(self : SubFigure) -> Array[Axes] {
  self.axes_list()
}

///|
/// Clear this nested region's axes and title.
pub fn SubFigure::clear(self : SubFigure) -> Unit {
  try! self.clear_checked()
}

///|
/// Clear each panel in this retained nested region through the same checked path.
pub fn SubFigure::clear_checked(self : SubFigure) -> Unit raise @core.DataError {
  self.data.val.clear_axes_checked(self.figure)
}

///|
fn SubFigureData::clear_axes_checked(
  self : SubFigureData,
  figure : Figure,
) -> Unit raise @core.DataError {
  for state in self.axes.copy() {
    ({ figure, state, } : Axes).clear_checked()
  }
  for child in self.subfigures {
    child.val.clear_axes_checked(figure)
  }
  self.title = None
}