// Convenience printers, a port of OCaml's `Format.pp_print_list` & co.
//
// Copyright 1996 Institut National de Recherche en Informatique et en
// Automatique (OCaml), distributed under the terms of the GNU Lesser
// General Public License version 2.1, with the special exception on
// linking described in the file LICENSE.

///|
/// The default separator of `print_list`, `print_iter` and `print_array`:
/// a cut break hint.
fn default_sep(ppf : Formatter) -> Unit {
  ppf.print_cut()
}

///|
/// Print the elements of a list with `pp_v`, separated by `pp_sep` (a cut
/// by default), like OCaml's `pp_print_list`.
pub fn[T] Formatter::print_list(
  self : Formatter,
  pp_sep? : (Formatter) -> Unit = default_sep,
  pp_v : (Formatter, T) -> Unit,
  l : @list.List[T],
) -> Unit {
  let mut first = true
  for v in l {
    if !first {
      pp_sep(self)
    }
    first = false
    pp_v(self, v)
  }
}

///|
/// Print the elements of an array, like `print_list`.
pub fn[T] Formatter::print_array(
  self : Formatter,
  pp_sep? : (Formatter) -> Unit = default_sep,
  pp_v : (Formatter, T) -> Unit,
  a : ArrayView[T],
) -> Unit {
  for i, v in a {
    if i > 0 {
      pp_sep(self)
    }
    pp_v(self, v)
  }
}

///|
/// Print the elements of an iterator, like OCaml's `pp_print_seq`.
pub fn[T] Formatter::print_iter(
  self : Formatter,
  pp_sep? : (Formatter) -> Unit = default_sep,
  pp_v : (Formatter, T) -> Unit,
  seq : Iter[T],
) -> Unit {
  let mut first = true
  for v in seq {
    if !first {
      pp_sep(self)
    }
    first = false
    pp_v(self, v)
  }
}

///|
/// Print free-flowing text, like OCaml's `pp_print_text`: spaces are
/// break hints and newlines are forced newlines.
pub fn Formatter::print_text(self : Formatter, s : String) -> Unit {
  let len = s.length()
  let mut left = 0
  let mut right = 0
  let flush = () => {
    self.print_string(s.view(start_offset=left, end_offset=right).to_owned())
    right += 1
    left = right
  }
  while right != len {
    match s[right] {
      '\n' => {
        flush()
        self.force_newline()
      }
      ' ' => {
        flush()
        self.print_space()
      }
      // there is no specific support for '\t' as it is unclear what a
      // right semantics would be
      _ => right += 1
    }
  }
  if left != len {
    flush()
  }
}

///|
/// Print an optional value with `pp_v`; `None` is printed with `none`,
/// which prints nothing by default, like OCaml's `pp_print_option`.
pub fn[T] Formatter::print_option(
  self : Formatter,
  none? : (Formatter) -> Unit = Formatter::print_nothing,
  pp_v : (Formatter, T) -> Unit,
  o : T?,
) -> Unit {
  match o {
    None => none(self)
    Some(v) => pp_v(self, v)
  }
}

///|
/// Print a result with `ok` or `error`, like OCaml's `pp_print_result`.
pub fn[T, E] Formatter::print_result(
  self : Formatter,
  ok~ : (Formatter, T) -> Unit,
  error~ : (Formatter, E) -> Unit,
  r : Result[T, E],
) -> Unit {
  match r {
    Ok(v) => ok(self, v)
    Err(e) => error(self, e)
  }
}