///|
pub(all) struct AreaChart {
  title : String
  x_labels : Array[String]
  series_list : Array[Series]
  width : Float
  height : Float
  config : ChartConfig
}

///|
pub fn AreaChart::new() -> AreaChart {
  {
    title: "",
    x_labels: [],
    series_list: [],
    width: 800.0,
    height: 400.0,
    config: ChartConfig::default(),
  }
}

///|
pub fn AreaChart::title(self : AreaChart, t : String) -> AreaChart {
  { ..self, title: t }
}

///|
pub fn AreaChart::x_labels(
  self : AreaChart,
  labels : Array[String],
) -> AreaChart {
  { ..self, x_labels: labels }
}

///|
pub fn AreaChart::series(self : AreaChart, s : Series) -> AreaChart {
  let new_list = self.series_list
  new_list.push(s)
  { ..self, series_list: new_list }
}

///|
pub fn AreaChart::width(self : AreaChart, w : Float) -> AreaChart {
  { ..self, width: w }
}

///|
pub fn AreaChart::height(self : AreaChart, h : Float) -> AreaChart {
  { ..self, height: h }
}

///|
pub fn AreaChart::config(self : AreaChart, c : ChartConfig) -> AreaChart {
  { ..self, config: c }
}

// Build area polygon points: line points + (last_x, baseline_y) + (first_x, baseline_y)

///|
fn build_area_polygon(
  pts_list : Array[(Float, Float)],
  baseline_y : Float,
) -> Array[(Float, Float)] {
  let area_pts : Array[(Float, Float)] = []
  for i = 0; i < pts_list.length(); i = i + 1 {
    area_pts.push(pts_list[i])
  }
  if pts_list.length() > 0 {
    let (last_x, _) = pts_list[pts_list.length() - 1]
    let (first_x, _) = pts_list[0]
    area_pts.push((last_x, baseline_y))
    area_pts.push((first_x, baseline_y))
  }
  area_pts
}

// Build points array and render area fill + polyline + circles for a single series

///|
fn render_area_points(
  result : String,
  values : Array[Float],
  num_pts : Int,
  axis : AxisLayout,
  height : Float,
  mt : Float,
  mb : Float,
  width : Float,
  ml : Float,
  mr : Float,
  color : String,
  baseline_y : Float,
  idx : Int,
  pts_list : Array[(Float, Float)],
) -> String {
  if idx >= num_pts {
    let area_pts = build_area_polygon(pts_list, baseline_y)
    let with_fill = result + polygon(area_pts, color, "none", 0.0)
    let with_line = with_fill + polyline(pts_list, color, 2.0, "none")
    render_line_circles(with_line, pts_list, color, 0)
  } else {
    let px = x_to_svg(
      Float::from_int(idx),
      Float::from_int(num_pts),
      width,
      ml,
      mr,
    )
    let py = y_to_svg(values[idx], axis.y_min, axis.y_max, height, mt, mb)
    pts_list.push((px, py))
    render_area_points(
      result,
      values,
      num_pts,
      axis,
      height,
      mt,
      mb,
      width,
      ml,
      mr,
      color,
      baseline_y,
      idx + 1,
      pts_list,
    )
  }
}

// Render all series (outer loop over series_list)

///|
fn render_area_series(
  result : String,
  series_list : Array[Series],
  num_cat : Int,
  axis : AxisLayout,
  height : Float,
  mt : Float,
  mb : Float,
  width : Float,
  ml : Float,
  mr : Float,
  baseline_y : Float,
  si : Int,
  config : ChartConfig,
) -> String {
  if si >= series_list.length() {
    result
  } else {
    let values = series_list[si].values
    let color = get_chart_color(config, si)
    let pts : Array[(Float, Float)] = []
    let with_series = render_area_points(
      result, values, num_cat, axis, height, mt, mb, width, ml, mr, color, baseline_y,
      0, pts,
    )
    render_area_series(
      with_series,
      series_list,
      num_cat,
      axis,
      height,
      mt,
      mb,
      width,
      ml,
      mr,
      baseline_y,
      si + 1,
      config,
    )
  }
}

///|
pub fn AreaChart::render(self : AreaChart) -> String {
  let mt : Float = 40.0
  let mb : Float = 60.0
  let ml : Float = 60.0
  let mr : Float = 30.0

  let (d_min, d_max) = find_line_data_range(self.series_list)
  let data_min = if d_min > 0.0 { Float::from_int(0) } else { d_min }
  let axis = compute_y_axis(data_min, d_max, self.height, mt, mb)

  let base = svg_open(self.width, self.height)

  // Title
  let with_title = if self.title != "" {
    base +
    text(
      self.width / 2.0,
      mt - 12.0,
      self.title,
      self.config.title_font_size,
      "middle",
    )
  } else {
    base
  }

  // Y-axis grid lines and labels
  let with_y_grid = render_line_y_grid(
    with_title,
    axis,
    self.height,
    mt,
    mb,
    self.width,
    ml,
    mr,
    0,
    self.config,
  )

  // X-axis baseline
  let baseline_y = y_to_svg(
    axis.y_min,
    axis.y_min,
    axis.y_max,
    self.height,
    mt,
    mb,
  )
  let with_baseline = with_y_grid +
    line(ml, baseline_y, self.width - mr, baseline_y, "#888888", 1.0)

  // Area fills and lines
  let num_pts = self.x_labels.length()
  let num_cat = min_line_categories(self.series_list, num_pts, 0)

  let with_areas = if num_cat > 0 && self.series_list.length() > 0 {
    render_area_series(
      with_baseline,
      self.series_list,
      num_cat,
      axis,
      self.height,
      mt,
      mb,
      self.width,
      ml,
      mr,
      baseline_y,
      0,
      self.config,
    )
  } else {
    with_baseline
  }

  // X labels
  let with_labels = render_line_x_labels(
    with_areas,
    self.x_labels,
    num_cat,
    self.width,
    ml,
    mr,
    baseline_y,
    0,
    self.config,
  )

  // Legend
  let names : Array[String] = []
  for i = 0; i < self.series_list.length(); i = i + 1 {
    names.push(self.series_list[i].name)
  }
  let with_legend = with_labels +
    render_legend(names, self.width, self.height - 10.0, self.config)

  with_legend + svg_close()
}