///|
/// Styling options for SVG elements
pub(all) struct SvgStyle {
  fill : String
  stroke : String
  stroke_width : Double
  opacity : Double
} derive(Eq, Debug, ToJson)

///|
/// Create a new SVG Style
pub fn SvgStyle::new(
  fill? : String = "none",
  stroke? : String = "black",
  stroke_width? : Double = 1.0,
  opacity? : Double = 1.0,
) -> SvgStyle {
  { fill, stroke, stroke_width, opacity }
}

///|
/// Single SVG graphic element or nested group
pub(all) enum SvgElement {
  SvgPath(Path, SvgStyle)
  SvgGroup(Array[SvgElement], transform~ : Matrix2D)
} derive(Eq, Debug, ToJson)

///|
/// Container for the complete SVG Document
pub(all) struct SvgDocument {
  width : Double
  height : Double
  elements : Array[SvgElement]
} derive(Eq, Debug, ToJson)

///|
/// Create a new empty SVG document
pub fn SvgDocument::new(width : Double, height : Double) -> SvgDocument {
  { width, height, elements: [] }
}

///|
/// Add an element to the SVG document
pub fn SvgDocument::add(self : SvgDocument, element : SvgElement) -> Unit {
  self.elements.push(element)
}

///|
/// Render SvgElement to its XML string representation
pub fn SvgElement::to_svg_string(self : SvgElement) -> String {
  let sb = StringBuilder()
  match self {
    SvgPath(path, style) => {
      let d = path.to_svg_d()
      sb.write_string(
        "\n",
      )
    }
    SvgGroup(children, transform~) => {
      let matrix_str = "matrix(\{transform.a} \{transform.b} \{transform.c} \{transform.d} \{transform.tx} \{transform.ty})"
      sb.write_string("\n")
      for child in children {
        sb.write_string(child.to_svg_string())
      }
      sb.write_string("\n")
    }
  }
  sb.to_string()
}

///|
/// Render complete SvgDocument to a valid SVG XML string
pub fn SvgDocument::to_svg_string(self : SvgDocument) -> String {
  let sb = StringBuilder()
  sb.write_string("\n")
  sb.write_string(
    "\n",
  )
  for element in self.elements {
    sb.write_string(element.to_svg_string())
  }
  sb.write_string("\n")
  sb.to_string()
}