///|
pub(all) struct DonutChart {
  title : String
  slices : Array[Slice]
  width : Float
  height : Float
  config : ChartConfig
  hole_ratio : Float
}

///|
pub fn DonutChart::new() -> DonutChart {
  {
    title: "",
    slices: [],
    width: 500.0,
    height: 400.0,
    config: ChartConfig::default(),
    hole_ratio: 0.4,
  }
}

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

///|
pub fn DonutChart::slice(
  self : DonutChart,
  name : String,
  value : Float,
) -> DonutChart {
  let new_slices = self.slices
  new_slices.push(Slice::new(name, value))
  { ..self, slices: new_slices }
}

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

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

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

///|
pub fn DonutChart::hole_ratio(self : DonutChart, hr : Float) -> DonutChart {
  { ..self, hole_ratio: hr }
}

///|
fn donut_slice_path(
  cx : Float,
  cy : Float,
  outer_r : Float,
  inner_r : Float,
  start_angle : Float,
  end_angle : Float,
) -> String {
  let (outer_start_x, outer_start_y) = polar_to_cartesian(
    cx, cy, outer_r, start_angle,
  )
  let (outer_end_x, outer_end_y) = polar_to_cartesian(
    cx, cy, outer_r, end_angle,
  )
  let (inner_start_x, inner_start_y) = polar_to_cartesian(
    cx, cy, inner_r, start_angle,
  )
  let (inner_end_x, inner_end_y) = polar_to_cartesian(
    cx, cy, inner_r, end_angle,
  )
  let pi : Float = 3.1415926535
  let large_arc = if end_angle - start_angle > pi { "1" } else { "0" }

  "M " +
  outer_start_x.to_string() +
  " " +
  outer_start_y.to_string() +
  " A " +
  outer_r.to_string() +
  " " +
  outer_r.to_string() +
  " 0 " +
  large_arc +
  " 0 " +
  outer_end_x.to_string() +
  " " +
  outer_end_y.to_string() +
  " L " +
  inner_end_x.to_string() +
  " " +
  inner_end_y.to_string() +
  " A " +
  inner_r.to_string() +
  " " +
  inner_r.to_string() +
  " 0 " +
  large_arc +
  " 1 " +
  inner_start_x.to_string() +
  " " +
  inner_start_y.to_string() +
  " Z"
}

// Render donut slices recursively

///|
fn render_donut_slices(
  result : String,
  slices : Array[Slice],
  total : Float,
  cx : Float,
  cy : Float,
  outer_r : Float,
  inner_r : Float,
  tau : Float,
  idx : Int,
  start_angle : Float,
) -> String {
  if idx >= slices.length() {
    result
  } else {
    let fraction = slices[idx].value / total
    let sweep = fraction * tau
    let end_angle = start_angle + sweep
    let color = get_color(idx)

    let with_path = result +
      path(
        donut_slice_path(cx, cy, outer_r, inner_r, start_angle, end_angle),
        color,
        "#ffffff",
      )

    // Percentage label at mid-angle, positioned halfway between inner and outer radius
    let mid_angle = start_angle + sweep / 2.0
    let label_r = (outer_r + inner_r) / 2.0
    let (lx, ly) = polar_to_cartesian(cx, cy, label_r, mid_angle)
    let pct = fraction * 100.0
    let pct_str = format_percent(pct)
    let with_label = with_path + text(lx, ly + 4.0, pct_str, 11, "middle")

    render_donut_slices(
      with_label,
      slices,
      total,
      cx,
      cy,
      outer_r,
      inner_r,
      tau,
      idx + 1,
      end_angle,
    )
  }
}

///|
pub fn DonutChart::render(self : DonutChart) -> String {
  let mt : Float = 40.0
  let mb : Float = 20.0
  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, 16, "middle")
  } else {
    base
  }

  let num_slices = self.slices.length()
  if num_slices == 0 {
    with_title + svg_close()
  } else {
    // Calculate total value
    let total = sum_slice_values(self.slices, 0, 0.0)

    // Donut layout: center the donut in the remaining area
    let cx = self.width / 2.0
    let chart_h = self.height - mt - mb - 40.0
    let cy = mt + chart_h / 2.0
    let outer_r = if self.width < self.height {
      self.width / 3.0
    } else {
      chart_h / 2.5
    }
    let inner_r = outer_r * self.hole_ratio

    let tau : Float = 3.1415926535 * 2.0

    // Render slices
    let with_slices = render_donut_slices(
      with_title,
      self.slices,
      total,
      cx,
      cy,
      outer_r,
      inner_r,
      tau,
      0,
      0.0,
    )

    // Center label: show total value
    let center_label = text(cx, cy + 4.0, total.to_string(), 18, "middle")

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

    with_legend + svg_close()
  }
}