///|
/// Serialize a RON value using deterministic multiline formatting.
///
/// `indent` is the number of spaces added for each nesting level. Negative
/// values are treated as zero. Empty containers remain on one line.
pub fn to_string_pretty(value : RonValue, indent~ : Int32 = 2) -> String {
  let buffer = StringBuilder()
  write_pretty_value(buffer, value, 0, indent.max(0))
  buffer.to_string()
}

///|
fn write_pretty_value(
  buffer : StringBuilder,
  value : RonValue,
  level : Int32,
  indent : Int32,
) -> Unit {
  match value {
    Unit
    | Bool(_)
    | Number(_)
    | Char(_)
    | String(_)
    | Bytes(_)
    | Byte(_)
    | Range(_)
    | Identifier(_) => write_value(buffer, value)
    Option(None) => buffer.write_string("None")
    Option(Some(value)) => {
      buffer.write_string("Some(")
      write_pretty_value(buffer, value, level, indent)
      buffer.write_char(')')
    }
    Sequence(values) =>
      write_pretty_values(buffer, values, level, indent, '[', ']')
    Tuple(values) =>
      write_pretty_values(buffer, values, level, indent, '(', ')')
    Map(entries) => write_pretty_map(buffer, entries, level, indent)
    Struct(name, fields) =>
      write_pretty_fields(buffer, name, fields, level, indent)
    TupleStruct(name, values) =>
      write_pretty_named_values(buffer, name, values, level, indent)
    Enum(owner, variant, fields) =>
      write_pretty_fields(buffer, owner + "::" + variant, fields, level, indent)
    EnumTuple(owner, variant, values) =>
      write_pretty_named_values(
        buffer,
        owner + "::" + variant,
        values,
        level,
        indent,
      )
  }
}

///|
fn write_pretty_values(
  buffer : StringBuilder,
  values : Array[RonValue],
  level : Int32,
  indent : Int32,
  open : Char,
  close : Char,
) -> Unit {
  buffer.write_char(open)
  if values.length() == 0 {
    buffer.write_char(close)
    return
  }
  buffer.write_char('\n')
  for i = 0; i < values.length(); i = i + 1 {
    write_indent(buffer, level + 1, indent)
    write_pretty_value(buffer, values[i], level + 1, indent)
    buffer.write_string(",\n")
  }
  write_indent(buffer, level, indent)
  buffer.write_char(close)
}

///|
fn write_pretty_named_values(
  buffer : StringBuilder,
  name : String,
  values : Array[RonValue],
  level : Int32,
  indent : Int32,
) -> Unit {
  buffer.write_string(name)
  if values.length() == 0 {
    return
  }
  write_pretty_values(buffer, values, level, indent, '(', ')')
}

///|
fn write_pretty_map(
  buffer : StringBuilder,
  entries : Array[(RonValue, RonValue)],
  level : Int32,
  indent : Int32,
) -> Unit {
  buffer.write_char('{')
  if entries.length() == 0 {
    buffer.write_char('}')
    return
  }
  buffer.write_char('\n')
  for i = 0; i < entries.length(); i = i + 1 {
    let (key, value) = entries[i]
    write_indent(buffer, level + 1, indent)
    write_pretty_value(buffer, key, level + 1, indent)
    buffer.write_string(": ")
    write_pretty_value(buffer, value, level + 1, indent)
    buffer.write_string(",\n")
  }
  write_indent(buffer, level, indent)
  buffer.write_char('}')
}

///|
fn write_pretty_fields(
  buffer : StringBuilder,
  name : String,
  fields : Array[(String, RonValue)],
  level : Int32,
  indent : Int32,
) -> Unit {
  buffer.write_string(name)
  if fields.length() == 0 {
    return
  }
  buffer.write_string("(\n")
  for i = 0; i < fields.length(); i = i + 1 {
    let (name, value) = fields[i]
    write_indent(buffer, level + 1, indent)
    buffer.write_string(name)
    buffer.write_string(": ")
    write_pretty_value(buffer, value, level + 1, indent)
    buffer.write_string(",\n")
  }
  write_indent(buffer, level, indent)
  buffer.write_char(')')
}

///|
fn write_indent(buffer : StringBuilder, level : Int32, indent : Int32) -> Unit {
  let count = level * indent
  for i = 0; i < count; i = i + 1 {
    buffer.write_char(' ')
  }
}