// Incremental parsing: feed the text as it arrives, and take the blocks that
// are already settled. A block settles when no later line can change it, which
// is when a following line has closed it.

///|
/// A parser that reads a document in pieces. Text that has arrived is parsed
/// again on every call, but only the part that has not been handed out yet, so
/// the work per chunk is proportional to the unfinished tail rather than to the
/// whole document.
pub struct Stream {
  priv mut pending : String
  priv flavor : Flavor
  priv refs : Map[String, Reference]
  /// Whether a block that may hold a reference link waits for the definitions
  /// at the end of the document before it is handed out.
  priv defer_links : Bool
  priv mut finished : Bool
  /// Whether the last chunk ended in a carriage return, which the following
  /// line ending has to pair with.
  priv mut pending_cr : Bool
}

///|
/// Start a stream.
///
/// `defer_links` keeps a block that mentions `[` back until the whole document
/// has arrived, because a link reference definition written later still applies
/// to it. With it off such a block is handed out as soon as it closes, at the
/// cost of a forward reference not resolving; the blocks are then the same as a
/// whole parse for any document that defines its labels before using them.
pub fn Stream::new(
  flavor~ : Flavor = flavor,
  defer_links~ : Bool = true,
) -> Stream {
  {
    pending: "",
    flavor,
    refs: (Map([]) : Map[String, Reference]),
    defer_links,
    finished: false,
    pending_cr: false,
  }
}

///|
/// Take the next piece of the document and return the blocks it completed.
pub fn Stream::feed(self : Stream, chunk : StringView) -> Array[Block] {
  if self.finished {
    return []
  }
  let normalized = StringBuilder()
  for c in chunk {
    if self.pending_cr {
      self.pending_cr = false
      if c == '\n' {
        continue
      }
    }
    if c == '\r' {
      normalized.write_char('\n')
      self.pending_cr = true
    } else if c == '\u{0}' {
      normalized.write_char('\u{FFFD}')
    } else {
      normalized.write_char(c)
    }
  }
  let text = self.pending + normalized.to_string()
  // Only whole lines can be parsed; a partial line waits for the next chunk.
  let mut complete = 0
  let n = text.length()
  let mut i = 0
  while i < n {
    if cu(text[:], i) == 0x0A {
      complete = i + 1
    }
    i = i + 1
  }
  if complete == 0 {
    self.pending = text
    return []
  }
  let ready = text.clamped_view(start=0, end=complete).to_owned()
  let (blocks, cut) = parse_prefix(
    ready,
    self.flavor,
    self.refs,
    self.defer_links,
  )
  self.pending = text.clamped_view(start=cut, end=n).to_owned()
  blocks
}

///|
/// Close the stream and return whatever was still open.
pub fn Stream::finish(self : Stream) -> Array[Block] {
  if self.finished {
    return []
  }
  self.finished = true
  let mut text = self.pending
  if text.length() == 0 {
    return []
  }
  if cu(text[:], text.length() - 1) != 0x0A {
    text = text + "\n"
  }
  let parser = BlockParser::new(self.flavor, self.refs)
  for line in split_lines(text) {
    parser.process_line(line)
  }
  parser.close_all()
  build_blocks(parser.tree, parser.refs, self.flavor, parser.root, -1)
}

///|
/// Parse the text that is known to be complete, and return the blocks that no
/// later line can change together with the offset the unfinished part starts at.
fn parse_prefix(
  text : String,
  flavor : Flavor,
  refs : Map[String, Reference],
  defer_links : Bool,
) -> (Array[Block], Int32) {
  let lines = split_lines(text)
  let starts : Array[Int32] = []
  let mut offset = 0
  for line in lines {
    starts.push(offset)
    offset = offset + line.length()
  }
  let parser = BlockParser::new(flavor, refs)
  for line in lines {
    parser.process_line(line)
  }
  // The part that is settled ends where the first block that is still open, or
  // the first block that may want a definition from later on, begins.
  let mut stop = -1
  let mut cut = text.length()
  let mut child = parser.tree.nodes[parser.root].first_child
  while child >= 0 {
    let node = parser.tree.nodes[child]
    if node.open {
      stop = child
      cut = starts[node.start_line]
      break
    }
    if defer_links && block_mentions_bracket(text, starts, node, parser) {
      stop = child
      cut = starts[node.start_line]
      break
    }
    child = node.next
  }
  let blocks = build_blocks(parser.tree, parser.refs, flavor, parser.root, stop)
  (blocks, cut)
}

///|
/// Whether the source of a block holds a `[`, which is the only way a link
/// reference definition written later can change what it means.
fn block_mentions_bracket(
  text : String,
  starts : Array[Int32],
  node : TreeNode,
  parser : BlockParser,
) -> Bool {
  let start = starts[node.start_line]
  let end = if node.next >= 0 {
    starts[parser.tree.nodes[node.next].start_line]
  } else {
    text.length()
  }
  let mut i = start
  let limit = if end > text.length() { text.length() } else { end }
  while i < limit {
    if cu(text[:], i) == 0x5B {
      return true
    }
    i = i + 1
  }
  false
}