///|
pub(all) struct RadarChart {
  title : String
  axis_labels : Array[String]
  series_list : Array[Series]
  width : Float
  height : Float
  config : ChartConfig
  levels : Int
}

///|
pub fn RadarChart::new() -> RadarChart {
  {
    title: "",
    axis_labels: [],
    series_list: [],
    width: 500.0,
    height: 500.0,
    config: ChartConfig::default(),
    levels: 5,
  }
}

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

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

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

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

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

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

///|
pub fn RadarChart::levels(self : RadarChart, l : Int) -> RadarChart {
  { ..self, levels: l }
}

// Find max value across all series (for scaling data polygons)

///|
fn find_radar_max(
  series_list : Array[Series],
  idx : Int,
  max_val : Float,
  found : Bool,
) -> Float {
  if idx >= series_list.length() {
    max_val
  } else {
    let new_max = find_value_max(series_list[idx].values, 0, max_val, found)
    find_radar_max(
      series_list,
      idx + 1,
      new_max,
      if found {
        true
      } else {
        new_max > 0.0 || max_val > 0.0
      },
    )
  }
}

///|
fn find_value_max(
  values : Array[Float],
  j : Int,
  max_val : Float,
  found : Bool,
) -> Float {
  if j >= values.length() {
    max_val
  } else {
    let v = values[j]
    let new_max = if found { if v > max_val { v } else { max_val } } else { v }
    find_value_max(values, j + 1, new_max, true)
  }
}

// Build polygon points for a given radius (used for concentric grid rings)

///|
fn build_grid_polygon_points(
  result : Array[(Float, Float)],
  cx : Float,
  cy : Float,
  r : Float,
  n : Int,
  angle_step : Float,
  start_angle : Float,
  i : Int,
) -> Array[(Float, Float)] {
  if i >= n {
    result
  } else {
    let angle = start_angle + angle_step * Float::from_int(i)
    let px = cx + r * cos_approx(angle)
    let py = cy + r * sin_approx(angle)
    result.push((px, py))
    build_grid_polygon_points(
      result,
      cx,
      cy,
      r,
      n,
      angle_step,
      start_angle,
      i + 1,
    )
  }
}

// Build polygon points for a data series (radius varies per axis)

///|
fn build_series_polygon_points(
  result : Array[(Float, Float)],
  cx : Float,
  cy : Float,
  values : Array[Float],
  max_val : Float,
  chart_r : Float,
  n : Int,
  angle_step : Float,
  start_angle : Float,
  i : Int,
) -> Array[(Float, Float)] {
  if i >= n {
    result
  } else {
    let val = if i < values.length() { values[i] } else { 0.0 }
    let r = if max_val > 0.0 { val / max_val * chart_r } else { 0.0 }
    let angle = start_angle + angle_step * Float::from_int(i)
    let px = cx + r * cos_approx(angle)
    let py = cy + r * sin_approx(angle)
    result.push((px, py))
    build_series_polygon_points(
      result,
      cx,
      cy,
      values,
      max_val,
      chart_r,
      n,
      angle_step,
      start_angle,
      i + 1,
    )
  }
}

// Render concentric grid polygons

///|
fn render_radar_grid(
  result : String,
  cx : Float,
  cy : Float,
  chart_r : Float,
  n : Int,
  angle_step : Float,
  start_angle : Float,
  levels : Int,
  current_level : Int,
) -> String {
  if current_level > levels {
    result
  } else {
    let r = chart_r * Float::from_int(current_level) / Float::from_int(levels)
    let points : Array[(Float, Float)] = []
    let grid_points = build_grid_polygon_points(
      points, cx, cy, r, n, angle_step, start_angle, 0,
    )
    let poly = polygon(grid_points, "none", "#cccccc", 1.0)
    render_radar_grid(
      result + poly,
      cx,
      cy,
      chart_r,
      n,
      angle_step,
      start_angle,
      levels,
      current_level + 1,
    )
  }
}

// Render axis lines and labels

///|
fn render_radar_axes(
  result : String,
  cx : Float,
  cy : Float,
  chart_r : Float,
  n : Int,
  angle_step : Float,
  start_angle : Float,
  axis_labels : Array[String],
  i : Int,
  config : ChartConfig,
) -> String {
  if i >= n {
    result
  } else {
    let angle = start_angle + angle_step * Float::from_int(i)
    let ex = cx + chart_r * cos_approx(angle)
    let ey = cy + chart_r * sin_approx(angle)
    let axis_line = line(cx, cy, ex, ey, "#cccccc", 1.0)

    // Place label slightly beyond the end point
    let label_r = chart_r + 18.0
    let lx = cx + label_r * cos_approx(angle)
    let ly = cy + label_r * sin_approx(angle) + 4.0
    let label = text(lx, ly, axis_labels[i], config.axis_font_size, "middle")

    render_radar_axes(
      result + axis_line + label,
      cx,
      cy,
      chart_r,
      n,
      angle_step,
      start_angle,
      axis_labels,
      i + 1,
      config,
    )
  }
}

// Render data series (filled polygons)

///|
fn render_radar_series(
  result : String,
  cx : Float,
  cy : Float,
  chart_r : Float,
  n : Int,
  angle_step : Float,
  start_angle : Float,
  series_list : Array[Series],
  max_val : 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 points : Array[(Float, Float)] = []
    let series_points = build_series_polygon_points(
      points, cx, cy, values, max_val, chart_r, n, angle_step, start_angle, 0,
    )
    let poly = polygon(series_points, color, color, 2.0)
    render_radar_series(
      result + poly,
      cx,
      cy,
      chart_r,
      n,
      angle_step,
      start_angle,
      series_list,
      max_val,
      si + 1,
      config,
    )
  }
}

///|
pub fn RadarChart::render(self : RadarChart) -> String {
  let n = self.axis_labels.length()
  if n == 0 {
    let base = svg_open(self.width, self.height)
    if self.title != "" {
      return base +
        text(
          self.width / 2.0,
          20.0,
          self.title,
          self.config.title_font_size,
          "middle",
        ) +
        svg_close()
    } else {
      return base + svg_close()
    }
  }

  let pi : Float = 3.1415926535
  let angle_step : Float = 2.0 * pi / Float::from_int(n)
  let start_angle : Float = -pi / 2.0

  // Layout: center the radar chart
  let title_h : Float = 40.0
  let label_margin : Float = 60.0
  let legend_h : Float = 30.0
  let cx = self.width / 2.0
  let cy = title_h + (self.height - title_h - label_margin - legend_h) / 2.0
  let max_r = if self.width / 2.0 - label_margin < cy - title_h * 0.5 {
    self.width / 2.0 - label_margin
  } else {
    cy - title_h * 0.5
  }
  let chart_r = if max_r > 20.0 { max_r } else { 20.0 }

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

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

  // Grid rings
  let with_grid = render_radar_grid(
    with_title,
    cx,
    cy,
    chart_r,
    n,
    angle_step,
    start_angle,
    self.levels,
    1,
  )

  // Axes and labels
  let with_axes = render_radar_axes(
    with_grid,
    cx,
    cy,
    chart_r,
    n,
    angle_step,
    start_angle,
    self.axis_labels,
    0,
    self.config,
  )

  // Data series
  let max_val = find_radar_max(self.series_list, 0, 0.0, false)
  let with_series = if self.series_list.length() > 0 && max_val > 0.0 {
    render_radar_series(
      with_axes,
      cx,
      cy,
      chart_r,
      n,
      angle_step,
      start_angle,
      self.series_list,
      max_val,
      0,
      self.config,
    )
  } else {
    with_axes
  }

  // 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_series +
    render_legend(names, self.width, self.height - 10.0, self.config)

  with_legend + svg_close()
}