///|
struct SourceSpan {
  start : Int
  end : Int
} derive(Eq, Debug)

///|
#warnings("-unused_constructor")
enum CssNode {
  Declaration(
    name~ : String,
    value~ : String,
    important~ : Bool,
    span~ : SourceSpan
  )
  Rule(selector~ : String, nodes~ : Array[CssNode], span~ : SourceSpan)
  AtRule(
    name~ : String,
    params~ : String,
    nodes~ : Array[CssNode]?,
    span~ : SourceSpan
  )
  Comment(value~ : String, license~ : Bool, span~ : SourceSpan)
  Context(
    values~ : Map[String, String],
    nodes~ : Array[CssNode],
    span~ : SourceSpan
  )
  AtRoot(nodes~ : Array[CssNode], span~ : SourceSpan)
} derive(Eq, Debug)

///|
enum CustomVariantTemplate {
  CustomVariantSelector(String)
  CustomVariantNodes(Array[CssNode])
}

///|
fn render_css_nodes(nodes : ArrayView[CssNode], indent? : Int = 0) -> String {
  let out = StringBuilder()
  for node in nodes {
    render_css_node(node, out, indent~)
  }
  out.to_string()
}

///|
fn write_indent(out : StringBuilder, indent : Int) -> Unit {
  for _ in 0.. Unit {
  // Write each fragment directly to the builder instead of interpolating a
  // throwaway string per node (`"\{name}: \{value}"` allocates before appending).
  // This function renders every node of the output tree, so for large stylesheets
  // those intermediate strings were a per-node hot-path allocation.
  match node {
    Declaration(name~, value~, important~, ..) => {
      write_indent(out, indent)
      out.write_string(name)
      out.write_string(": ")
      out.write_string(value)
      if important {
        out.write_string(" !important")
      }
      out.write_string(";\n")
    }
    Rule(selector~, nodes~, ..) => {
      write_indent(out, indent)
      out.write_string(selector)
      out.write_string(" {\n")
      for child in nodes {
        render_css_node(child, out, indent=indent + 1)
      }
      write_indent(out, indent)
      out.write_string("}\n")
    }
    AtRule(name~, params~, nodes~, ..) =>
      match nodes {
        None => {
          write_indent(out, indent)
          out.write_string(name)
          if params != "" {
            out.write_string(" ")
            out.write_string(params)
          }
          out.write_string(";\n")
        }
        Some(children) => {
          write_indent(out, indent)
          out.write_string(name)
          if params != "" {
            out.write_string(" ")
            out.write_string(params)
          }
          out.write_string(" {\n")
          for child in children {
            render_css_node(child, out, indent=indent + 1)
          }
          write_indent(out, indent)
          out.write_string("}\n")
        }
      }
    Comment(value~, ..) => {
      write_indent(out, indent)
      out.write_string("/*")
      out.write_string(value)
      out.write_string("*/\n")
    }
    Context(nodes~, ..) =>
      for child in nodes {
        render_css_node(child, out, indent~)
      }
    AtRoot(nodes~, ..) =>
      for child in nodes {
        render_css_node(child, out, indent=0)
      }
  }
}