// The inline phase. One subject (the text of a leaf block) is scanned from left
// to right; the nodes produced are kept in a flat array linked both ways, so
// that emphasis and links can splice the list the way the specification's
// delimiter stack requires.

///|
#derive(Eq)
priv enum InlineKind {
  TextNode
  CodeNode
  HtmlNode
  EmphNode
  StrongNode
  StrikeNode
  LinkNode
  ImageNode
  SoftBreakNode
  HardBreakNode
  FootnoteRefNode
}

///|
priv struct InlineNode {
  mut kind : InlineKind
  mut parent : Int32
  mut first_child : Int32
  mut last_child : Int32
  mut next : Int32
  mut prev : Int32
  mut text : String
  /// How much of `text` is still in use: a delimiter run shrinks as it pairs up.
  mut text_len : Int32
  title : String
}

///|
priv struct InlineTree {
  nodes : Array[InlineNode]
}

///|
fn InlineTree::new() -> InlineTree {
  let tree : InlineTree = { nodes: [], }
  tree.nodes.push(InlineNode::{
    kind: TextNode,
    parent: -1,
    first_child: -1,
    last_child: -1,
    next: -1,
    prev: -1,
    text: "",
    text_len: 0,
    title: "",
  })
  tree
}

///|
fn InlineTree::make(
  self : InlineTree,
  kind : InlineKind,
  text : String,
  text_len : Int32,
  title : String,
) -> Int32 {
  self.nodes.push(InlineNode::{
    kind,
    parent: -1,
    first_child: -1,
    last_child: -1,
    next: -1,
    prev: -1,
    text,
    text_len,
    title,
  })
  self.nodes.length() - 1
}

///|
fn InlineTree::append_child(
  self : InlineTree,
  parent : Int32,
  child : Int32,
) -> Unit {
  let last = self.nodes[parent].last_child
  self.nodes[child].parent = parent
  self.nodes[child].prev = last
  self.nodes[child].next = -1
  if last >= 0 {
    self.nodes[last].next = child
  } else {
    self.nodes[parent].first_child = child
  }
  self.nodes[parent].last_child = child
}

///|
fn InlineTree::insert_after(
  self : InlineTree,
  index : Int32,
  node : Int32,
) -> Unit {
  let parent = self.nodes[index].parent
  let next = self.nodes[index].next
  self.nodes[node].parent = parent
  self.nodes[node].prev = index
  self.nodes[node].next = next
  self.nodes[index].next = node
  if next >= 0 {
    self.nodes[next].prev = node
  } else {
    self.nodes[parent].last_child = node
  }
}

///|
/// The text a node still shows, without the part a delimiter run has used up.
fn InlineTree::literal(self : InlineTree, index : Int32) -> StringView {
  let node = self.nodes[index]
  if node.text_len >= node.text.length() {
    node.text[:]
  } else {
    node.text[:].clamped_view(start=0, end=node.text_len)
  }
}

///|
/// Remove a node, giving its children to the parent of `keep_children_of` when
/// asked, or simply dropping it.
fn InlineTree::detach(self : InlineTree, index : Int32) -> Unit {
  let parent = self.nodes[index].parent
  if parent < 0 {
    return
  }
  let prev = self.nodes[index].prev
  let next = self.nodes[index].next
  if prev >= 0 {
    self.nodes[prev].next = next
  } else {
    self.nodes[parent].first_child = next
  }
  if next >= 0 {
    self.nodes[next].prev = prev
  } else {
    self.nodes[parent].last_child = prev
  }
  self.nodes[index].parent = -1
  self.nodes[index].prev = -1
  self.nodes[index].next = -1
}

///|
priv struct Delimiter {
  ch : Int32
  can_open : Bool
  can_close : Bool
  inl : Int32
  pos : Int32
  len : Int32
  mut prev : Int32
  mut next : Int32
}

///|
priv struct Bracket {
  image : Bool
  inl : Int32
  prev : Int32
  pos : Int32
  /// The position right after the `[`, where the bracket's content begins:
  /// the start of a footnote reference's label.
  body_start : Int32
  mut bracket_after : Bool
}

///|
/// One leaf block being parsed into inlines.
priv struct Subject {
  input : String
  tree : InlineTree
  refs : Map[String, Reference]
  flavor : Flavor
  mut pos : Int32
  root : Int32
  mut last_delim : Int32
  mut last_bracket : Int32
  mut no_link_openers : Bool
  delims : Array[Delimiter]
  brackets : Array[Bracket]
  backticks : Array[Int32]
  mut scanned_for_backticks : Bool
}

///|
fn Subject::new(
  input : String,
  refs : Map[String, Reference],
  flavor : Flavor,
) -> Subject {
  let tree = InlineTree::new()
  {
    input,
    tree,
    refs,
    flavor,
    pos: 0,
    root: 0,
    last_delim: -1,
    last_bracket: -1,
    no_link_openers: false,
    delims: [],
    brackets: [],
    backticks: array_of(81, 0),
    scanned_for_backticks: false,
  }
}

///|
fn Subject::peek(self : Subject, i : Int32) -> Int32 {
  cu(self.input[:], i)
}

///|
fn Subject::eof(self : Subject) -> Bool {
  self.pos >= self.input.length()
}

///|
fn Subject::skip_spaces(self : Subject) -> Unit {
  while true {
    let c = self.peek(self.pos)
    if c == 0x20 || c == 0x09 {
      self.pos = self.pos + 1
    } else {
      break
    }
  }
}

///|
/// Skip a `\r\n`, `\r` or `\n`; true when one was there or the input ended.
fn Subject::skip_line_end(self : Subject) -> Bool {
  let mut seen = false
  if self.peek(self.pos) == 0x0D {
    self.pos = self.pos + 1
    seen = true
  }
  if self.peek(self.pos) == 0x0A {
    self.pos = self.pos + 1
    seen = true
  }
  seen || self.eof()
}

///|
fn Subject::add_text(self : Subject, text : StringView) -> Int32 {
  let node = self.tree.make(TextNode, text.to_owned(), text.length(), "")
  self.tree.append_child(self.root, node)
  node
}

///|
fn Subject::add_break(self : Subject, kind : InlineKind) -> Int32 {
  let node = self.tree.make(kind, "", 0, "")
  self.tree.append_child(self.root, node)
  node
}

///|
/// Whether a character would start an inline construct, so that a run of plain
/// text can stop before it.
fn Subject::is_special(self : Subject, c : Int32) -> Bool {
  if c == 0x0A ||
    c == 0x0D ||
    c == 0x5C ||
    c == 0x60 ||
    c == 0x26 ||
    c == 0x5F ||
    c == 0x2A ||
    c == 0x5B ||
    c == 0x5D ||
    c == 0x3C ||
    c == 0x21 {
    return true
  }
  if self.flavor.autolinks && (c == 0x3A || c == 0x77) {
    return true
  }
  if self.flavor.strikethrough && c == 0x7E {
    return true
  }
  if self.flavor.footnotes && c == 0x5E {
    return true
  }
  false
}

///|
/// Push a delimiter run onto the stack.
fn Subject::push_delimiter(
  self : Subject,
  ch : Int32,
  can_open : Bool,
  can_close : Bool,
  inl : Int32,
) -> Unit {
  let index = self.delims.length()
  self.delims.push(Delimiter::{
    ch,
    can_open,
    can_close,
    inl,
    pos: self.pos,
    len: self.tree.nodes[inl].text_len,
    prev: self.last_delim,
    next: -1,
  })
  if self.last_delim >= 0 {
    self.delims[self.last_delim].next = index
  }
  self.last_delim = index
}

///|
fn Subject::remove_delimiter(self : Subject, index : Int32) -> Unit {
  if index < 0 {
    return
  }
  let prev = self.delims[index].prev
  let next = self.delims[index].next
  if next >= 0 {
    self.delims[next].prev = prev
  } else {
    self.last_delim = prev
  }
  if prev >= 0 {
    self.delims[prev].next = next
  }
  self.delims[index].prev = -1
  self.delims[index].next = -1
}

///|
fn Subject::push_bracket(self : Subject, image : Bool, inl : Int32) -> Unit {
  if self.last_bracket >= 0 {
    self.brackets[self.last_bracket].bracket_after = true
  }
  self.brackets.push(Bracket::{
    image,
    inl,
    prev: self.last_bracket,
    pos: self.pos,
    body_start: self.pos,
    bracket_after: false,
  })
  self.last_bracket = self.brackets.length() - 1
  if !image {
    self.no_link_openers = false
  }
}

///|
fn Subject::pop_bracket(self : Subject) -> Unit {
  if self.last_bracket < 0 {
    return
  }
  self.last_bracket = self.brackets[self.last_bracket].prev
}

///|
/// Whether the delimiter character is one this position may open or close,
/// computed from the characters on either side of the run.
fn Subject::scan_delims(self : Subject, c : Int32) -> (Int32, Bool, Bool) {
  let before = if self.pos == 0 {
    '\n'
  } else {
    flanking_before(self.input[:], self.pos, self.flavor.strikethrough)
  }
  let mut count = 0
  while self.peek(self.pos) == c {
    count = count + 1
    self.pos = self.pos + 1
  }
  let after = if self.eof() {
    '\n'
  } else {
    flanking_after(self.input[:], self.pos, self.flavor.strikethrough)
  }
  let space_before = is_unicode_space(before)
  let space_after = is_unicode_space(after)
  let punct_before = is_unicode_punct(before)
  let punct_after = is_unicode_punct(after)
  let left = count > 0 &&
    !space_after &&
    (!punct_after || space_before || punct_before)
  let right = count > 0 &&
    !space_before &&
    (!punct_before || space_after || punct_after)
  let can_open = if c == 0x5F { left && (!right || punct_before) } else { left }
  let can_close = if c == 0x5F {
    right && (!left || punct_after)
  } else {
    right
  }
  (count, can_open, can_close)
}

///|
/// The character before `pos`, skipping the runs of special characters that do
/// not count when flanking is decided.
fn flanking_before(text : StringView, pos : Int32, skip_tilde : Bool) -> Char {
  let mut i = pos
  if skip_tilde {
    while i > 0 && cu(text, i - 1) == 0x7E {
      i = i - 1
    }
  }
  char_before(text, i)
}

///|
/// The character at `pos`, skipping the same runs.
fn flanking_after(text : StringView, pos : Int32, skip_tilde : Bool) -> Char {
  let mut i = pos
  if skip_tilde {
    while cu(text, i) == 0x7E {
      i = i + 1
    }
  }
  char_at(text, i)
}

///|
/// `array_of` without pulling in a package for it.
fn array_of(size : Int32, value : Int32) -> Array[Int32] {
  let out : Array[Int32] = []
  for _ in 0..