// Value-axis rendering shared by the bar, line, area and scatter charts:
// horizontal gridlines plus right-aligned y-axis tick labels, and x-axis tick
// labels (optionally with vertical gridlines).

///|
/// Render horizontal gridlines and y-axis tick labels for a plot area whose
/// value domain is `[axis_lo, axis_hi]` with the given `ticks`. The zero line
/// (or the bottom of the domain when zero is out of range) is drawn in the
/// theme's stronger axis color.
fn y_axis(
  theme : Theme,
  left : Double,
  top : Double,
  plot_w : Double,
  plot_h : Double,
  axis_lo : Double,
  axis_hi : Double,
  ticks : Array[Double],
) -> String {
  let body = StringBuilder::new()
  let zero_in_range = axis_lo <= 0.0 && axis_hi >= 0.0
  for t in ticks {
    let y = scale_linear(t, axis_lo, axis_hi, top + plot_h, top)
    let is_base = if zero_in_range { t == 0.0 } else { t == axis_lo }
    let stroke = if is_base { theme.axis } else { theme.grid }
    body.write_string(
      elem("line", [
        ("x1", num(left)),
        ("y1", num(y)),
        ("x2", num(left + plot_w)),
        ("y2", num(y)),
        ("stroke", stroke),
        ("stroke-width", "1"),
      ]),
    )
    body.write_string(
      label(left - 6.0, y + 4.0, num(t), anchor="end", fill=theme.axis),
    )
  }
  body.to_string()
}

///|
/// Optional axis titles: `x_title` centered below the tick labels, `y_title`
/// rotated 90° along the left edge. Empty strings render nothing.
fn axis_titles(
  theme : Theme,
  left : Double,
  top : Double,
  plot_w : Double,
  plot_h : Double,
  x_title : String,
  y_title : String,
) -> String {
  let sb = StringBuilder::new()
  if x_title != "" {
    sb.write_string(
      label(
        left + plot_w / 2.0,
        top + plot_h + 34.0,
        x_title,
        size=12,
        fill=theme.axis,
      ),
    )
  }
  if y_title != "" {
    let cy = top + plot_h / 2.0
    sb.write_string(
      text(14.0, cy, y_title, [
        ("text-anchor", "middle"),
        ("font-family", "sans-serif"),
        ("font-size", "12"),
        ("fill", theme.axis),
        ("transform", "rotate(-90 14 " + num(cy) + ")"),
      ]),
    )
  }
  sb.to_string()
}

///|
/// Render x-axis tick labels below the plot area for a horizontal domain
/// `[axis_lo, axis_hi]`. With `grid=true`, vertical gridlines are drawn too
/// (used by the horizontal bar chart).
fn x_axis(
  theme : Theme,
  left : Double,
  top : Double,
  plot_w : Double,
  plot_h : Double,
  axis_lo : Double,
  axis_hi : Double,
  ticks : Array[Double],
  grid? : Bool = false,
) -> String {
  let body = StringBuilder::new()
  for t in ticks {
    let x = scale_linear(t, axis_lo, axis_hi, left, left + plot_w)
    if grid {
      let stroke = if t == axis_lo { theme.axis } else { theme.grid }
      body.write_string(
        elem("line", [
          ("x1", num(x)),
          ("y1", num(top)),
          ("x2", num(x)),
          ("y2", num(top + plot_h)),
          ("stroke", stroke),
          ("stroke-width", "1"),
        ]),
      )
    }
    body.write_string(label(x, top + plot_h + 16.0, num(t), fill=theme.axis))
  }
  body.to_string()
}