///|
fn needs_quote(s : String) -> Bool {
  if s.length() == 0 {
    return true
  }
  let mut i = 0
  while i < s.length() {
    let ch = s[i].to_int()
    if is_ws(ch) || is_special(ch) || ch == 35 || ch == 34 || ch == 39 {
      return true
    }
    i = i + 1
  }
  false
}

///|
fn quote_arg(s : String) -> String {
  let mut out = "\""
  let mut i = 0
  while i < s.length() {
    let ch = s[i].to_int()
    if ch == 34 {
      out = out + "\\\""
    } else {
      out = out + slice_text(s, i, i + 1)
    }
    i = i + 1
  }
  out + "\""
}

///|
fn render_arg(s : String, quoted : Bool) -> String {
  if quoted || needs_quote(s) {
    quote_arg(s)
  } else {
    s
  }
}

///|
fn dump_directive(d : Directive, indent : Int) -> String {
  let pad = indent_spaces(indent)
  if d.name == "#" {
    match d.comment {
      Some(c) => pad + "#" + c
      None => pad + "#"
    }
  } else {
    let mut line = pad + d.name
    let mut i = 0
    while i < d.args.length() {
      let q = if i < d.quoted.length() { d.quoted[i] } else { false }
      line = line + " " + render_arg(d.args[i], q)
      i = i + 1
    }
    if d.has_block {
      let mut out = line + " {"
      let mut j = 0
      while j < d.children.length() {
        out = out + "\n" + dump_directive(d.children[j], indent + 4)
        j = j + 1
      }
      if d.children.length() == 0 {
        out + "\n" + pad + "}"
      } else {
        out + "\n" + pad + "}"
      }
    } else {
      line + ";"
    }
  }
}

///|
pub fn dump_conf(cfg : Config) -> String {
  let mut out = ""
  let mut i = 0
  while i < cfg.parsed.length() {
    if i > 0 {
      out = out + "\n"
    }
    out = out + dump_directive(cfg.parsed[i], 0)
    i = i + 1
  }
  if out.length() == 0 {
    out
  } else {
    out + "\n"
  }
}

///|
pub fn roundtrip(text : String) -> Result[String, NgxError] {
  match parse_conf(text) {
    Ok(cfg) => Ok(dump_conf(cfg))
    Err(e) => Err(e)
  }
}