///|
fn contains_char(text : String, target : Char) -> Bool {
  let mut i = 0
  while i < text.length() {
    match text.get_char(i) {
      Some(c) => if c == target { return true }
      None => ()
    }
    i = i + 1
  }
  false
}

///|
fn has_edge_space(text : String) -> Bool {
  if text.is_empty() {
    false
  } else {
    let first = text.get_char(0)
    let last = text.get_char(text.length() - 1)
    first == Some(' ') ||
    first == Some('\t') ||
    last == Some(' ') ||
    last == Some('\t')
  }
}

///|
fn needs_quote(text : String, config : ParseConfig) -> Bool {
  text.is_empty() ||
  contains_char(text, config.delimiter) ||
  contains_char(text, config.quote) ||
  contains_char(text, '\n') ||
  contains_char(text, '\r') ||
  has_edge_space(text)
}

///|
fn write_field(text : String, config : ParseConfig) -> String {
  if !needs_quote(text, config) {
    return text
  }
  let out = StringBuilder::StringBuilder()
  out.write_char(config.quote)
  let mut i = 0
  while i < text.length() {
    match text.get_char(i) {
      Some(c) =>
        if c == config.quote {
          out.write_char(config.quote)
          out.write_char(config.quote)
        } else {
          out.write_char(c)
        }
      None => ()
    }
    i = i + 1
  }
  out.write_char(config.quote)
  out.to_string()
}

///|
fn write_row(row : Array[String], config : ParseConfig) -> String {
  let out = StringBuilder::StringBuilder()
  let mut i = 0
  while i < row.length() {
    if i > 0 {
      out.write_char(config.delimiter)
    }
    out.write_string(write_field(row[i], config))
    i = i + 1
  }
  out.to_string()
}

///|
/// Write a table using a custom delimiter configuration.
pub fn write_with(table : Table, config : ParseConfig) -> String {
  let out = StringBuilder::StringBuilder()
  let mut wrote = false
  if table.header.length() > 0 {
    out.write_string(write_row(table.header, config))
    wrote = true
  }
  let mut r = 0
  while r < table.rows.length() {
    if wrote {
      out.write_char('\n')
    }
    out.write_string(write_row(table.rows[r], config))
    wrote = true
    r = r + 1
  }
  out.to_string()
}

///|
/// Write a table as CSV.
pub fn write_csv(table : Table) -> String {
  write_with(table, csv_config())
}

///|
/// Write a table as TSV.
pub fn write_tsv(table : Table) -> String {
  write_with(table, tsv_config())
}