///|
/// SVG XML Parser
/// Parses SVG markup into SVGNode tree

///|
/// SVG Parser state
priv struct SVGParser {
  data : String
  mut pos : Int
  len : Int
}

///|
fn SVGParser::new(data : String) -> SVGParser {
  { data, pos: 0, len: data.length() }
}

///|
fn SVGParser::is_end(self : SVGParser) -> Bool {
  self.pos >= self.len
}

///|
fn SVGParser::peek(self : SVGParser) -> Char {
  if self.pos < self.len {
    Int::unsafe_to_char(self.data[self.pos].to_int())
  } else {
    '\u0000'
  }
}

///|
fn SVGParser::advance(self : SVGParser) -> Char {
  if self.pos < self.len {
    let c = Int::unsafe_to_char(self.data[self.pos].to_int())
    self.pos = self.pos + 1
    c
  } else {
    '\u0000'
  }
}

///|
fn SVGParser::skip_whitespace(self : SVGParser) -> Unit {
  while !self.is_end() {
    let c = self.peek()
    if c == ' ' || c == '\t' || c == '\n' || c == '\r' {
      let _ = self.advance()
    } else {
      break
    }
  }
}

///|
fn SVGParser::expect_char(self : SVGParser, expected : Char) -> Bool {
  if self.peek() == expected {
    let _ = self.advance()
    true
  } else {
    false
  }
}

///|
fn SVGParser::read_until(self : SVGParser, stop : Char) -> String {
  let buf = StringBuilder::new()
  while !self.is_end() && self.peek() != stop {
    buf.write_char(self.advance())
  }
  buf.to_string()
}

///|
fn SVGParser::read_name(self : SVGParser) -> String {
  let buf = StringBuilder::new()
  while !self.is_end() {
    let c = self.peek()
    if is_name_char(c) {
      buf.write_char(self.advance())
    } else {
      break
    }
  }
  buf.to_string()
}

///|
fn is_name_char(c : Char) -> Bool {
  (c >= 'a' && c <= 'z') ||
  (c >= 'A' && c <= 'Z') ||
  (c >= '0' && c <= '9') ||
  c == '-' ||
  c == '_' ||
  c == ':'
}

///|
fn SVGParser::read_attr_value(self : SVGParser) -> String {
  self.skip_whitespace()
  if !self.expect_char('=') {
    return ""
  }
  self.skip_whitespace()
  let quote = self.peek()
  if quote != '"' && quote != '\'' {
    return ""
  }
  let _ = self.advance()
  let value = self.read_until(quote)
  let _ = self.advance() // Skip closing quote
  value
}

///|
/// Parse SVG markup string into SVGNode
pub fn parse_svg(svg_str : String) -> SVGNode? {
  let parser = SVGParser::new(svg_str)
  parse_element(parser)
}

///|
fn parse_element(parser : SVGParser) -> SVGNode? {
  parser.skip_whitespace()
  // Skip comments and declarations
  while !parser.is_end() {
    if parser.peek() == '<' {
      let _ = parser.advance()
      if parser.peek() == '!' || parser.peek() == '?' {
        // Skip comment or declaration
        skip_comment_or_decl(parser)
        parser.skip_whitespace()
        continue
      }
      break
    } else {
      let _ = parser.advance()
    }
  }
  if parser.is_end() {
    return None
  }
  // Read tag name
  let tag_name = parser.read_name()
  if tag_name.length() == 0 {
    return None
  }
  // Create node based on tag
  let node = create_node_for_tag(tag_name)
  // Parse attributes
  parse_attributes(parser, node)
  parser.skip_whitespace()
  // Check for self-closing tag
  if parser.peek() == '/' {
    let _ = parser.advance()
    let _ = parser.expect_char('>')
    return Some(node)
  }
  // Expect closing '>'
  if !parser.expect_char('>') {
    return Some(node)
  }
  // Parse children (for container elements)
  match node.shape {
    Group => parse_children(parser, node, tag_name)
    _ =>
      // Skip to end tag for non-container elements
      skip_to_end_tag(parser, tag_name)
  }
  Some(node)
}

///|
fn skip_comment_or_decl(parser : SVGParser) -> Unit {
  // Skip until we find '>'
  while !parser.is_end() {
    if parser.advance() == '>' {
      break
    }
  }
}

///|
fn skip_to_end_tag(parser : SVGParser, _tag_name : String) -> Unit {
  // Skip until we find the closing tag
  while !parser.is_end() {
    if parser.peek() == '<' {
      let _ = parser.advance()
      if parser.peek() == '/' {
        let _ = parser.advance()
        let _ = parser.read_name() // Read the tag name
        parser.skip_whitespace()
        let _ = parser.expect_char('>')
        break
      }
    } else {
      let _ = parser.advance()
    }
  }
}

///|
fn parse_children(
  parser : SVGParser,
  parent : SVGNode,
  parent_tag : String,
) -> Unit {
  while !parser.is_end() {
    parser.skip_whitespace()
    if parser.peek() != '<' {
      // Skip text content
      while !parser.is_end() && parser.peek() != '<' {
        let _ = parser.advance()
      }
      continue
    }
    let _ = parser.advance()
    // Check for end tag
    if parser.peek() == '/' {
      let _ = parser.advance()
      let end_tag = parser.read_name()
      if end_tag == parent_tag {
        parser.skip_whitespace()
        let _ = parser.expect_char('>')
        break
      }
      // Skip malformed end tag
      while !parser.is_end() && parser.advance() != '>' {

      }
      continue
    }
    // Check for comment/declaration
    if parser.peek() == '!' || parser.peek() == '?' {
      skip_comment_or_decl(parser)
      continue
    }
    // Parse child element
    // Backtrack the '<' we consumed
    parser.pos = parser.pos - 1
    match parse_element(parser) {
      Some(child) => parent.children.push(child)
      None => break
    }
  }
}

///|
fn create_node_for_tag(tag_name : String) -> SVGNode {
  let node = match tag_name {
    "rect" =>
      SVGNode::new(Rect(x=0.0, y=0.0, width=0.0, height=0.0, rx=0.0, ry=0.0))
    "circle" => SVGNode::new(Circle(cx=0.0, cy=0.0, r=0.0))
    "ellipse" => SVGNode::new(Ellipse(cx=0.0, cy=0.0, rx=0.0, ry=0.0))
    "line" => SVGNode::new(Line(x1=0.0, y1=0.0, x2=0.0, y2=0.0))
    "polyline" => SVGNode::new(Polyline(points=[]))
    "polygon" => SVGNode::new(Polygon(points=[]))
    "path" => SVGNode::new(Path(commands=[]))
    _ => SVGNode::new(Group) // svg, g, defs, etc. are groups
  }
  node
}

///|
fn parse_attributes(parser : SVGParser, node : SVGNode) -> Unit {
  while !parser.is_end() {
    parser.skip_whitespace()
    let c = parser.peek()
    if c == '>' || c == '/' {
      break
    }
    let attr_name = parser.read_name()
    if attr_name.length() == 0 {
      break
    }
    let attr_value = parser.read_attr_value()
    apply_attribute(node, attr_name, attr_value)
  }
}

///|
fn apply_attribute(node : SVGNode, name : String, value : String) -> Unit {
  match name {
    "id" => node.id = value
    "x" =>
      match node.shape {
        Rect(..) as r =>
          node.shape = Rect(
            x=parse_length(value),
            y=r.y,
            width=r.width,
            height=r.height,
            rx=r.rx,
            ry=r.ry,
          )
        _ => ()
      }
    "y" =>
      match node.shape {
        Rect(..) as r =>
          node.shape = Rect(
            x=r.x,
            y=parse_length(value),
            width=r.width,
            height=r.height,
            rx=r.rx,
            ry=r.ry,
          )
        _ => ()
      }
    "width" =>
      match node.shape {
        Rect(..) as r =>
          node.shape = Rect(
            x=r.x,
            y=r.y,
            width=parse_length(value),
            height=r.height,
            rx=r.rx,
            ry=r.ry,
          )
        _ => ()
      }
    "height" =>
      match node.shape {
        Rect(..) as r =>
          node.shape = Rect(
            x=r.x,
            y=r.y,
            width=r.width,
            height=parse_length(value),
            rx=r.rx,
            ry=r.ry,
          )
        _ => ()
      }
    "rx" =>
      match node.shape {
        Rect(..) as r =>
          node.shape = Rect(
            x=r.x,
            y=r.y,
            width=r.width,
            height=r.height,
            rx=parse_length(value),
            ry=r.ry,
          )
        Ellipse(..) as e =>
          node.shape = Ellipse(
            cx=e.cx,
            cy=e.cy,
            rx=parse_length(value),
            ry=e.ry,
          )
        _ => ()
      }
    "ry" =>
      match node.shape {
        Rect(..) as r =>
          node.shape = Rect(
            x=r.x,
            y=r.y,
            width=r.width,
            height=r.height,
            rx=r.rx,
            ry=parse_length(value),
          )
        Ellipse(..) as e =>
          node.shape = Ellipse(
            cx=e.cx,
            cy=e.cy,
            rx=e.rx,
            ry=parse_length(value),
          )
        _ => ()
      }
    "cx" =>
      match node.shape {
        Circle(..) as c =>
          node.shape = Circle(cx=parse_length(value), cy=c.cy, r=c.r)
        Ellipse(..) as e =>
          node.shape = Ellipse(
            cx=parse_length(value),
            cy=e.cy,
            rx=e.rx,
            ry=e.ry,
          )
        _ => ()
      }
    "cy" =>
      match node.shape {
        Circle(..) as c =>
          node.shape = Circle(cx=c.cx, cy=parse_length(value), r=c.r)
        Ellipse(..) as e =>
          node.shape = Ellipse(
            cx=e.cx,
            cy=parse_length(value),
            rx=e.rx,
            ry=e.ry,
          )
        _ => ()
      }
    "r" =>
      match node.shape {
        Circle(..) as c =>
          node.shape = Circle(cx=c.cx, cy=c.cy, r=parse_length(value))
        _ => ()
      }
    "x1" =>
      match node.shape {
        Line(..) as l =>
          node.shape = Line(x1=parse_length(value), y1=l.y1, x2=l.x2, y2=l.y2)
        _ => ()
      }
    "y1" =>
      match node.shape {
        Line(..) as l =>
          node.shape = Line(x1=l.x1, y1=parse_length(value), x2=l.x2, y2=l.y2)
        _ => ()
      }
    "x2" =>
      match node.shape {
        Line(..) as l =>
          node.shape = Line(x1=l.x1, y1=l.y1, x2=parse_length(value), y2=l.y2)
        _ => ()
      }
    "y2" =>
      match node.shape {
        Line(..) as l =>
          node.shape = Line(x1=l.x1, y1=l.y1, x2=l.x2, y2=parse_length(value))
        _ => ()
      }
    "d" =>
      match node.shape {
        Path(..) => node.shape = Path(commands=parse_path(value))
        _ => ()
      }
    "points" =>
      match node.shape {
        Polyline(..) => node.shape = Polyline(points=parse_points(value))
        Polygon(..) => node.shape = Polygon(points=parse_points(value))
        _ => ()
      }
    "fill" => node.fill = parse_paint(value)
    "stroke" =>
      node.stroke = {
        paint: parse_paint(value),
        width: node.stroke.width,
        linecap: node.stroke.linecap,
        linejoin: node.stroke.linejoin,
        miterlimit: node.stroke.miterlimit,
        dasharray: node.stroke.dasharray,
        dashoffset: node.stroke.dashoffset,
      }
    "stroke-width" =>
      node.stroke = {
        paint: node.stroke.paint,
        width: parse_length(value),
        linecap: node.stroke.linecap,
        linejoin: node.stroke.linejoin,
        miterlimit: node.stroke.miterlimit,
        dasharray: node.stroke.dasharray,
        dashoffset: node.stroke.dashoffset,
      }
    "opacity" => node.opacity = parse_number(value)
    "fill-opacity" => node.fill_opacity = parse_number(value)
    "stroke-opacity" => node.stroke_opacity = parse_number(value)
    "transform" => node.transform = parse_transform(value)
    // Stroke detail attributes
    "stroke-linecap" =>
      node.stroke = { ..node.stroke, linecap: parse_linecap(value) }
    "stroke-linejoin" =>
      node.stroke = { ..node.stroke, linejoin: parse_linejoin(value) }
    "stroke-miterlimit" =>
      node.stroke = { ..node.stroke, miterlimit: parse_number(value) }
    "stroke-dasharray" =>
      node.stroke = { ..node.stroke, dasharray: parse_dasharray(value) }
    "stroke-dashoffset" =>
      node.stroke = { ..node.stroke, dashoffset: parse_number(value) }
    // Fill rule
    "fill-rule" => node.fill_rule = parse_fill_rule(value)
    "clip-rule" => node.fill_rule = parse_fill_rule(value)
    _ => () // Ignore unknown attributes
  }
}

///|
fn parse_linecap(value : String) -> LineCap {
  let v = trim_string(value)
  match v {
    "round" => Round
    "square" => Square
    _ => Butt // Default
  }
}

///|
fn parse_linejoin(value : String) -> LineJoin {
  let v = trim_string(value)
  match v {
    "round" => Round
    "bevel" => Bevel
    _ => Miter // Default
  }
}

///|
fn parse_dasharray(value : String) -> Array[Double]? {
  let v = trim_string(value)
  if v == "none" || v.length() == 0 {
    return None
  }
  let result : Array[Double] = []
  let mut current = ""
  for c in v {
    if c == ',' || c == ' ' {
      if current.length() > 0 {
        result.push(parse_number(current))
        current = ""
      }
    } else {
      current = current + c.to_string()
    }
  }
  if current.length() > 0 {
    result.push(parse_number(current))
  }
  if result.is_empty() {
    None
  } else {
    Some(result)
  }
}

///|
fn parse_fill_rule(value : String) -> FillRule {
  let v = trim_string(value)
  match v {
    "evenodd" => EvenOdd
    _ => NonZero // Default
  }
}

///|
fn trim_string(s : String) -> String {
  let mut start = 0
  let mut end = s.length()
  while start < end {
    let c = Int::unsafe_to_char(s[start].to_int())
    if c == ' ' || c == '\t' || c == '\n' || c == '\r' {
      start = start + 1
    } else {
      break
    }
  }
  while end > start {
    let c = Int::unsafe_to_char(s[end - 1].to_int())
    if c == ' ' || c == '\t' || c == '\n' || c == '\r' {
      end = end - 1
    } else {
      break
    }
  }
  build_substring(s, start, end)
}

///|
fn string_starts_with(s : String, prefix : String) -> Bool {
  if prefix.length() > s.length() {
    return false
  }
  for i = 0; i < prefix.length(); i = i + 1 {
    if s[i] != prefix[i] {
      return false
    }
  }
  true
}

///|
fn string_ends_with(s : String, suffix : String) -> Bool {
  if suffix.length() > s.length() {
    return false
  }
  let start = s.length() - suffix.length()
  for i = 0; i < suffix.length(); i = i + 1 {
    if s[start + i] != suffix[i] {
      return false
    }
  }
  true
}

///|
fn parse_length(s : String) -> Double {
  // Simple number parsing (strips common units)
  let s = trim_string(s)
  // Remove units
  let cleaned = if string_ends_with(s, "px") {
    build_substring(s, 0, s.length() - 2)
  } else if string_ends_with(s, "em") || string_ends_with(s, "pt") {
    build_substring(s, 0, s.length() - 2)
  } else if string_ends_with(s, "%") {
    build_substring(s, 0, s.length() - 1)
  } else {
    s
  }
  parse_number(cleaned)
}

///|
fn parse_number(s : String) -> Double {
  let s = trim_string(s)
  if s.length() == 0 {
    return 0.0
  }
  let mut result = 0.0
  let mut sign = 1.0
  let mut i = 0
  let len = s.length()
  fn char_at(str : String, idx : Int) -> Char {
    Int::unsafe_to_char(str[idx].to_int())
  }
  // Sign
  if i < len && char_at(s, i) == '-' {
    sign = -1.0
    i = i + 1
  } else if i < len && char_at(s, i) == '+' {
    i = i + 1
  }
  // Integer part
  while i < len {
    let c = char_at(s, i)
    if c >= '0' && c <= '9' {
      result = result * 10.0 + (c.to_int() - 48).to_double()
      i = i + 1
    } else {
      break
    }
  }
  // Fractional part
  if i < len && char_at(s, i) == '.' {
    i = i + 1
    let mut frac = 0.1
    while i < len {
      let c = char_at(s, i)
      if c >= '0' && c <= '9' {
        result = result + (c.to_int() - 48).to_double() * frac
        frac = frac * 0.1
        i = i + 1
      } else {
        break
      }
    }
  }
  sign * result
}

///|
fn parse_paint(value : String) -> Paint {
  let value = trim_string(value)
  if value == "none" {
    return None
  }
  // Parse color
  let color = parse_color(value)
  SolidColor(color)
}

///|
fn parse_color(value : String) -> Color {
  let value = trim_string(value)
  if value.length() == 0 {
    return Color::black()
  }
  // Named colors
  match value {
    "black" => return Color::black()
    "white" => return Color::white()
    "red" => return Color::rgb(255, 0, 0)
    "green" => return Color::rgb(0, 128, 0)
    "blue" => return Color::rgb(0, 0, 255)
    "yellow" => return Color::rgb(255, 255, 0)
    "cyan" => return Color::rgb(0, 255, 255)
    "magenta" => return Color::rgb(255, 0, 255)
    "gray" | "grey" => return Color::rgb(128, 128, 128)
    "orange" => return Color::rgb(255, 165, 0)
    "purple" => return Color::rgb(128, 0, 128)
    "pink" => return Color::rgb(255, 192, 203)
    "brown" => return Color::rgb(165, 42, 42)
    _ => ()
  }
  // Hex color
  if value.length() > 0 && Int::unsafe_to_char(value[0].to_int()) == '#' {
    return parse_hex_color(value)
  }
  // rgb(r, g, b)
  if string_starts_with(value, "rgb") {
    return parse_rgb_color(value)
  }
  Color::black()
}

///|
fn parse_hex_color(value : String) -> Color {
  // #RGB or #RRGGBB
  fn hex_digit(c : Char) -> Int {
    if c >= '0' && c <= '9' {
      c.to_int() - 48
    } else if c >= 'a' && c <= 'f' {
      c.to_int() - 87
    } else if c >= 'A' && c <= 'F' {
      c.to_int() - 55
    } else {
      0
    }
  }

  fn char_at(s : String, i : Int) -> Char {
    Int::unsafe_to_char(s[i].to_int())
  }

  if value.length() == 4 {
    // #RGB
    let r = hex_digit(char_at(value, 1))
    let g = hex_digit(char_at(value, 2))
    let b = hex_digit(char_at(value, 3))
    Color::rgb(r * 17, g * 17, b * 17)
  } else if value.length() == 7 {
    // #RRGGBB
    let r = hex_digit(char_at(value, 1)) * 16 + hex_digit(char_at(value, 2))
    let g = hex_digit(char_at(value, 3)) * 16 + hex_digit(char_at(value, 4))
    let b = hex_digit(char_at(value, 5)) * 16 + hex_digit(char_at(value, 6))
    Color::rgb(r, g, b)
  } else {
    Color::black()
  }
}

///|
fn parse_rgb_color(value : String) -> Color {
  // rgb(r, g, b) or rgba(r, g, b, a)
  // Find numbers between parentheses
  let mut start = 0
  while start < value.length() &&
        Int::unsafe_to_char(value[start].to_int()) != '(' {
    start = start + 1
  }
  start = start + 1
  let mut end = start
  while end < value.length() && Int::unsafe_to_char(value[end].to_int()) != ')' {
    end = end + 1
  }
  if start >= end {
    return Color::black()
  }
  // Parse comma-separated numbers
  let nums : Array[Int] = []
  let mut num_start = start
  for i = start; i <= end; i = i + 1 {
    let c = if i < value.length() {
      Int::unsafe_to_char(value[i].to_int())
    } else {
      ')'
    }
    if c == ',' || c == ')' {
      if num_start < i {
        let num_str = build_substring(value, num_start, i)
        nums.push(parse_number(num_str).to_int())
      }
      num_start = i + 1
    }
  }
  if nums.length() >= 3 {
    Color::rgb(nums[0], nums[1], nums[2])
  } else {
    Color::black()
  }
}

///|
fn build_substring(s : String, start : Int, end : Int) -> String {
  let buf = StringBuilder::new()
  for i = start; i < end; i = i + 1 {
    buf.write_char(Int::unsafe_to_char(s[i].to_int()))
  }
  buf.to_string()
}

///|
fn parse_points(value : String) -> Array[(Double, Double)] {
  // Parse space/comma separated list of x,y pairs
  let result : Array[(Double, Double)] = []
  let nums : Array[Double] = []
  let mut buf = StringBuilder::new()
  for i = 0; i < value.length(); i = i + 1 {
    let c = Int::unsafe_to_char(value[i].to_int())
    if c == ' ' || c == ',' || c == '\n' || c == '\r' || c == '\t' {
      if buf.to_string().length() > 0 {
        nums.push(parse_number(buf.to_string()))
        buf = StringBuilder::new()
      }
    } else {
      buf.write_char(c)
    }
  }
  if buf.to_string().length() > 0 {
    nums.push(parse_number(buf.to_string()))
  }
  // Pair up the numbers
  let mut i = 0
  while i + 1 < nums.length() {
    result.push((nums[i], nums[i + 1]))
    i = i + 2
  }
  result
}

///|
pub fn parse_transform(value : String) -> Transform {
  // Parse transform attribute: translate(x, y) scale(sx, sy) rotate(angle) etc.
  let mut result = Transform::identity()
  let mut i = 0
  while i < value.length() {
    // Skip whitespace
    while i < value.length() {
      let c = Int::unsafe_to_char(value[i].to_int())
      if c == ' ' || c == ',' {
        i = i + 1
      } else {
        break
      }
    }
    if i >= value.length() {
      break
    }
    // Read function name
    let name_start = i
    while i < value.length() {
      let c = Int::unsafe_to_char(value[i].to_int())
      if c >= 'a' && c <= 'z' {
        i = i + 1
      } else {
        break
      }
    }
    let name = build_substring(value, name_start, i)
    // Find '('
    while i < value.length() && Int::unsafe_to_char(value[i].to_int()) != '(' {
      i = i + 1
    }
    i = i + 1 // Skip '('
    // Find ')' and parse arguments
    let args_start = i
    while i < value.length() && Int::unsafe_to_char(value[i].to_int()) != ')' {
      i = i + 1
    }
    let args_str = build_substring(value, args_start, i)
    i = i + 1 // Skip ')'
    // Parse arguments
    let args = parse_transform_args(args_str)
    // Apply transform
    let t = match name {
      "translate" =>
        if args.length() >= 2 {
          Transform::translate(args[0], args[1])
        } else if args.length() >= 1 {
          Transform::translate(args[0], 0.0)
        } else {
          Transform::identity()
        }
      "scale" =>
        if args.length() >= 2 {
          Transform::scale(args[0], args[1])
        } else if args.length() >= 1 {
          Transform::scale(args[0], args[0])
        } else {
          Transform::identity()
        }
      "rotate" =>
        if args.length() >= 3 {
          Transform::rotate_around(
            degrees_to_radians(args[0]),
            args[1],
            args[2],
          )
        } else if args.length() >= 1 {
          Transform::rotate(degrees_to_radians(args[0]))
        } else {
          Transform::identity()
        }
      "skewX" =>
        if args.length() >= 1 {
          Transform::skew_x(degrees_to_radians(args[0]))
        } else {
          Transform::identity()
        }
      "skewY" =>
        if args.length() >= 1 {
          Transform::skew_y(degrees_to_radians(args[0]))
        } else {
          Transform::identity()
        }
      "matrix" =>
        if args.length() >= 6 {
          Transform::matrix(
            args[0],
            args[1],
            args[2],
            args[3],
            args[4],
            args[5],
          )
        } else {
          Transform::identity()
        }
      _ => Transform::identity()
    }
    result = result.multiply(t)
  }
  result
}

///|
fn parse_transform_args(s : String) -> Array[Double] {
  let result : Array[Double] = []
  let mut buf = StringBuilder::new()
  for i = 0; i < s.length(); i = i + 1 {
    let c = Int::unsafe_to_char(s[i].to_int())
    if c == ' ' || c == ',' {
      if buf.to_string().length() > 0 {
        result.push(parse_number(buf.to_string()))
        buf = StringBuilder::new()
      }
    } else {
      buf.write_char(c)
    }
  }
  if buf.to_string().length() > 0 {
    result.push(parse_number(buf.to_string()))
  }
  result
}