///|
/// if contract is violated, behavior is undefined
pub(open) trait Format {
  /// Write the `value` into `buffer` as the `start` position,
  /// and return the number of bytes written.
  ///
  /// The number of bytes written must not exceed count().
  write(value : Self, buffer : Memory, start : Int) -> Int
  /// Upper bound on the number of bytes written
  count(value : Self) -> Int
}

///|
fn find_first_placeholder(format : Bytes) -> Int? {
  loop (0, format[:]) {
    (_, []) => None
    (index, ['{', '}', ..]) => Some(index)
    (index, [_, .. cs]) => continue (index + 1, cs)
  }
}

///|
fn split_format(format : BytesView) -> (Array[BytesView], Int) {
  let mut count = 1
  let snippets = []
  let mut start = 0
  let mut index = 0
  loop format[:] {
    [] => snippets.push(format[start:index])
    ['{', '}', .. cs] => {
      snippets.push(format[start:index])
      start = index + 2
      index += 2
      count += 1
      continue cs
    }
    [_, .. cs] => {
      index += 1
      continue cs
    }
  }
  (snippets, count)
}

///|
pub fn format_write(
  format : Bytes,
  data : Array[&Format],
  buffer : Memory,
  start : Int,
) -> Int raise {
  match find_first_placeholder(format) {
    None => {
      guard format.length() <= buffer.length() else { fail("out of bound") }
      guard data.length() == 0 else {
        raise DebugError("data.length() mismatch with placeholder's count")
      }
      Format::write(format, buffer, start)
    }
    Some(prefix_fst_end) => {
      let prefix = format[0:prefix_fst_end]
      let rest = format[prefix_fst_end + 2:]
      let (snippets, count) = split_format(rest)
      guard data.length() == count else {
        raise DebugError("data.length() mismatch with placeholder's count")
      }
      let upper_bound_count = snippets.fold(init=0, (acc, x) => acc + x.length()) +
        data.fold(init=0, (acc, x) => acc + x.count())
      guard upper_bound_count < buffer.length() else { fail("out of bound") }
      let mut offset = start
      offset += Format::write(prefix, buffer, offset)
      guard data.length() == snippets.length() else {
        raise DebugError("internal error")
      }
      for i in 0.. Int raise {
  match find_first_placeholder(format) {
    None => {
      guard data.length() == 0 else {
        raise DebugError("data.length() mismatch with placeholder's count")
      }
      format.length()
    }
    Some(prefix_fst_end) => {
      let prefix = format[0:prefix_fst_end]
      let rest = format[prefix_fst_end + 2:]
      let (snippets, count) = split_format(rest)
      guard data.length() == count else {
        raise DebugError("data.length() mismatch with placeholder's count")
      }
      let snippets_total = snippets.fold(init=0, (acc, x) => acc + x.length())
      let data_total = data.fold(init=0, (acc, x) => acc + x.count())
      prefix.length() + snippets_total + data_total
    }
  }
}

///|
priv suberror DebugError String derive(Show)