///|
/// Draw maps parent limits through the OLD child context before checking scale.
pub fn SecondaryAxis::prepare_draw(
  self : SecondaryAxis,
) -> Unit raise @core.DataError {
  let (lower, upper) = self.mapped_parent_view_interval()
  self.store_formatter_view(lower, upper)
  self.synchronize_parent_scale()
}

///|
fn SecondaryAxis::synchronize_parent_scale(
  self : SecondaryAxis,
) -> Unit raise @core.DataError {
  let name = match self.parent_axis {
    Some(parent) => parent.coordinate_scale_name()
    None => "linear"
  }
  if self.parent_scale_name == Some(name) {
    return
  }
  // Source snapshots the CURRENT locator before the ordinary scale lifecycle.
  // Keep its dtype and borrowed buffer when restoring FixedLocator afterward.
  let saved = if self.ticks_set {
    Some(self.major_tick_view_checked())
  } else {
    None
  }
  let source_ticks_current = self.source_ticks_current
  let (old_lower, old_upper) = self.numeric_axis.locator_default_limits()
  self.numeric_axis.configure_function_scale(
    self.forward,
    self.inverse,
    logarithmic=name == "log",
  )
  self.legacy_sampling = false
  self.source_ticks_current = false
  let (new_lower, new_upper) = self.numeric_axis.locator_default_limits()
  if old_lower != new_lower || old_upper != new_upper {
    self.autoscale_empty_data()
  }
  if saved is Some(values) {
    self.numeric_axis.set_locator(
      @ticker.FixedLocator::from_view(values).as_locator(),
    )
    self.source_ticks_current = source_ticks_current
  }
  self.parent_scale_name = Some(name)
}

///|
fn SecondaryAxis::autoscale_empty_data(
  self : SecondaryAxis,
) -> Unit raise @core.DataError {
  let margin = if self.horizontal {
    @config.x_margin()
  } else {
    @config.y_margin()
  }
  if self.numeric_axis.autoscale_bounds_checked(
      [],
      margin~,
      stickies=[],
      tight=false,
    )
    is Some(candidate) {
    // set_bound performs both view reads even with two supplied endpoints.
    ignore(self.numeric_axis.lifecycle_view_interval_checked())
    let (first, last) = self.numeric_axis.lifecycle_view_interval_checked()
    let low = Double::min(candidate.lower(), candidate.upper())
    let high = Double::max(candidate.lower(), candidate.upper())
    let (lower, upper) = if first > last { (high, low) } else { (low, high) }
    ignore(self.numeric_axis.set_bound_limits_checked(lower, upper))
  }
}

///|
// This sampler is enabled only by explicit compatibility methods, never defaults.
fn SecondaryAxis::legacy_parent_grid_ticks(
  self : SecondaryAxis,
) -> Array[Double] raise @core.DataError {
  let values = match self.parent_axis {
    Some(parent) => {
      let (lower, upper) = parent.view_interval_checked()
      parent.tick_values_checked(@core.limits(lower, upper))
    }
    None => [0.0, 1.0]
  }
  let count = self.tick_count()
  let ticks = if values.length() < 2 || count < 2 {
    values
  } else {
    let first = values[0]
    let last = values[values.length() - 1]
    Array::makei(count, index => {
      first + (last - first) * index.to_double() / (count - 1).to_double()
    })
  }
  ticks.map(self.forward)
}