// XY area chart: a line chart with the region beneath the line filled.

///|
/// Render an area chart for a series of `(x, y)` points as a standalone SVG
/// document string. The region between the line and the baseline is filled
/// with a translucent color; `smooth` curves the top edge; `x_title` /
/// `y_title` add axis captions. `title`, `width`, `height` and `theme` are
/// optional.
pub fn area_chart(
  data : Array[(Double, Double)],
  smooth? : Bool = false,
  x_title? : String = "",
  y_title? : String = "",
  title? : String = "",
  width? : Double = 480.0,
  height? : Double = 320.0,
  theme? : Theme = Theme::light(),
) -> String {
  let left = if y_title == "" { 48.0 } else { 64.0 }
  let right = 16.0
  let top = if title == "" { 16.0 } else { 40.0 }
  let bottom = if x_title == "" { 40.0 } else { 56.0 }
  let plot_w = width - left - right
  let plot_h = height - top - bottom
  let xs = data.map(fn(p) { p.0 })
  let ys = data.map(fn(p) { p.1 })
  let (xlo_raw, xhi_raw) = extent(xs)
  let (ylo_raw, yhi) = extent(ys)
  let ylo = if ylo_raw > 0.0 { 0.0 } else { ylo_raw }
  let (axis_lo, axis_hi, ticks) = nice_ticks(ylo, yhi, 5)
  let (xlo, xhi, x_ticks) = nice_ticks(xlo_raw, xhi_raw, 6)
  let baseline = top + plot_h
  let body = StringBuilder::new()
  body.write_string(background_rect(theme, width, height))
  body.write_string(
    y_axis(theme, left, top, plot_w, plot_h, axis_lo, axis_hi, ticks),
  )
  body.write_string(x_axis(theme, left, top, plot_w, plot_h, xlo, xhi, x_ticks))
  let n = data.length()
  if n > 0 {
    let color = theme.color_at(0)
    let line_pts = StringBuilder::new()
    let px_arr : Array[Double] = []
    let py_arr : Array[Double] = []
    let coords : Array[(Double, Double)] = []
    for i in 0.. 0 {
        line_pts.write_string(" ")
      }
      line_pts.write_string(num(px) + "," + num(py))
    }
    if smooth {
      // One smooth path for the fill (closed to the baseline), one for the
      // top edge.
      let curve = smooth_path(coords)
      let fill_d = curve +
        " L " +
        num(px_arr[n - 1]) +
        " " +
        num(baseline) +
        " L " +
        num(px_arr[0]) +
        " " +
        num(baseline) +
        " Z"
      body.write_string(
        elem("path", [("d", fill_d), ("fill", color), ("fill-opacity", "0.18")]),
      )
      body.write_string(
        elem("path", [
          ("d", curve),
          ("fill", "none"),
          ("stroke", color),
          ("stroke-width", "2"),
          ("stroke-linejoin", "round"),
          ("stroke-linecap", "round"),
        ]),
      )
    } else {
      // Filled polygon: along the line, then down to the baseline and back.
      let poly = StringBuilder::new()
      poly.write_string(num(px_arr[0]) + "," + num(baseline) + " ")
      poly.write_string(line_pts.to_string())
      poly.write_string(" " + num(px_arr[n - 1]) + "," + num(baseline))
      body.write_string(
        elem("polygon", [
          ("points", poly.to_string()),
          ("fill", color),
          ("fill-opacity", "0.18"),
        ]),
      )
      body.write_string(
        elem("polyline", [
          ("points", line_pts.to_string()),
          ("fill", "none"),
          ("stroke", color),
          ("stroke-width", "2"),
          ("stroke-linejoin", "round"),
          ("stroke-linecap", "round"),
        ]),
      )
    }
    for i in 0.. String {
  let left = 48.0
  let right = 16.0
  let title_h = if title == "" { 8.0 } else { 30.0 }
  let legend_h = 22.0
  let top = title_h + legend_h
  let bottom = 40.0
  let plot_w = width - left - right
  let plot_h = height - top - bottom
  let n = xs.length()
  let n_ser = series.length()
  // The tallest stacked total sets the y scale.
  let mut max_total = 0.0
  for i in 0.. max_total {
      max_total = total
    }
  }
  let max_total = if max_total <= 0.0 { 1.0 } else { max_total }
  let (axis_lo, axis_hi, ticks) = nice_ticks(0.0, max_total, 5)
  let (xlo_raw, xhi_raw) = extent(xs)
  let (xlo, xhi, x_ticks) = nice_ticks(xlo_raw, xhi_raw, 6)
  let body = StringBuilder::new()
  body.write_string(background_rect(theme, width, height))
  body.write_string(
    y_axis(theme, left, top, plot_w, plot_h, axis_lo, axis_hi, ticks),
  )
  body.write_string(x_axis(theme, left, top, plot_w, plot_h, xlo, xhi, x_ticks))
  if n > 0 && n_ser > 0 {
    let px = fn(x : Double) -> Double {
      scale_linear(x, xlo, xhi, left, left + plot_w)
    }
    let py = fn(v : Double) -> Double {
      scale_linear(v, axis_lo, axis_hi, top + plot_h, top)
    }
    // Running top of the layers already drawn.
    let prev : Array[Double] = []
    for _i in 0.. 0 {
          poly.write_string(" ")
          line_pts.write_string(" ")
        }
        let pt = num(px(xs[i])) + "," + num(py(cur[i]))
        poly.write_string(pt)
        line_pts.write_string(pt)
      }
      for i = n - 1; i >= 0; i = i - 1 {
        poly.write_string(" " + num(px(xs[i])) + "," + num(py(prev[i])))
      }
      body.write_string(
        elem("polygon", [
          ("points", poly.to_string()),
          ("fill", color),
          ("fill-opacity", "0.4"),
        ]),
      )
      // Crisp line along this layer's top edge.
      body.write_string(
        elem("polyline", [
          ("points", line_pts.to_string()),
          ("fill", "none"),
          ("stroke", color),
          ("stroke-width", "1.5"),
        ]),
      )
      for i in 0..