// Resolving the delimiter stack into emphasis, strong emphasis and, when the
// flavor allows it, strikethrough. This is the part of the specification with
// the most rules per line of code, so it follows the reference wording closely.

///|
/// Pair up the delimiters from `stack_bottom` up, turning each pair into an
/// emphasis or strikethrough node. A closer that finds no opener stops future
/// searches from looking below it.
fn Subject::process_emphasis(self : Subject, stack_bottom : Int32) -> Unit {
  let openers_bottom = array_of(3 * 128, 0)
  for i in 0..<3 {
    openers_bottom[i * 128 + 0x2A] = stack_bottom
    openers_bottom[i * 128 + 0x5F] = stack_bottom
    openers_bottom[i * 128 + 0x27] = stack_bottom
    openers_bottom[i * 128 + 0x22] = stack_bottom
  }
  // Start at the first delimiter that is above the bottom of this stack.
  let mut closer = -1
  let mut candidate = self.last_delim
  while candidate >= 0 && self.delims[candidate].pos >= stack_bottom {
    closer = candidate
    candidate = self.delims[candidate].prev
  }
  while closer >= 0 {
    if self.delims[closer].can_close {
      let ch = self.delims[closer].ch
      let len = self.delims[closer].len
      let bottom = openers_bottom[len % 3 * 128 + ch]
      let mut opener = self.delims[closer].prev
      let mut opener_found = false
      while opener >= 0 &&
            self.delims[opener].pos >= stack_bottom &&
            self.delims[opener].pos >= bottom {
        if self.delims[opener].can_open && self.delims[opener].ch == ch {
          // A run of three can only pair with a run that is not a multiple of
          // three in total, unless both ends could also be openers or closers.
          if !(self.delims[closer].can_open || self.delims[opener].can_close) ||
            len % 3 == 0 ||
            (self.delims[opener].len + len) % 3 != 0 {
            opener_found = true
            break
          }
        }
        opener = self.delims[opener].prev
      }
      let old_closer = closer
      if opener_found {
        if ch == 0x7E {
          closer = self.insert_strike(opener, old_closer)
        } else {
          closer = self.insert_emph(opener, old_closer)
        }
      } else {
        closer = self.delims[old_closer].next
        openers_bottom[len % 3 * 128 + ch] = self.delims[old_closer].pos
        if !self.delims[old_closer].can_open {
          self.remove_delimiter(old_closer)
        }
      }
    } else {
      closer = self.delims[closer].next
    }
  }
  while self.last_delim >= 0 && self.delims[self.last_delim].pos >= stack_bottom {
    self.remove_delimiter(self.last_delim)
  }
}

///|
/// Turn a pair of `*` or `_` runs into emphasis or strong emphasis. Returns the
/// closer to carry on from, which moves when the closer run is used up.
fn Subject::insert_emph(
  self : Subject,
  opener : Int32,
  closer : Int32,
) -> Int32 {
  let opener_inl = self.delims[opener].inl
  let closer_inl = self.delims[closer].inl
  let opener_chars = self.tree.nodes[opener_inl].text_len
  let closer_chars = self.tree.nodes[closer_inl].text_len
  let used = if closer_chars >= 2 && opener_chars >= 2 { 2 } else { 1 }
  self.tree.nodes[opener_inl].text_len = opener_chars - used
  self.tree.nodes[closer_inl].text_len = closer_chars - used
  let mut delim = self.delims[closer].prev
  while delim >= 0 && delim != opener {
    let prev = self.delims[delim].prev
    self.remove_delimiter(delim)
    delim = prev
  }
  let emph = self.tree.make(
    if used == 1 {
      EmphNode
    } else {
      StrongNode
    },
    "",
    0,
    "",
  )
  let mut node = self.tree.nodes[opener_inl].next
  while node >= 0 && node != closer_inl {
    let next = self.tree.nodes[node].next
    self.tree.detach(node)
    self.tree.append_child(emph, node)
    node = next
  }
  self.tree.insert_after(opener_inl, emph)
  let mut result = closer
  if opener_chars - used == 0 {
    self.tree.detach(opener_inl)
    self.remove_delimiter(opener)
  }
  if closer_chars - used == 0 {
    self.tree.detach(closer_inl)
    let next = self.delims[closer].next
    self.remove_delimiter(closer)
    result = next
  }
  result
}

///|
/// Turn a pair of tilde runs into a strikethrough. The two runs have to be the
/// same width; otherwise the tildes stay as text.
fn Subject::insert_strike(
  self : Subject,
  opener : Int32,
  closer : Int32,
) -> Int32 {
  let result = self.delims[closer].next
  let opener_inl = self.delims[opener].inl
  let closer_inl = self.delims[closer].inl
  if self.delims[opener].len == self.delims[closer].len {
    self.tree.nodes[opener_inl].kind = StrikeNode
    self.tree.nodes[opener_inl].text = ""
    self.tree.nodes[opener_inl].text_len = 0
    let mut node = self.tree.nodes[opener_inl].next
    while node >= 0 && node != closer_inl {
      let next = self.tree.nodes[node].next
      self.tree.detach(node)
      self.tree.append_child(opener_inl, node)
      node = next
    }
    self.tree.detach(closer_inl)
  }
  let mut delim = closer
  while delim >= 0 && delim != opener {
    let prev = self.delims[delim].prev
    self.remove_delimiter(delim)
    delim = prev
  }
  self.remove_delimiter(opener)
  result
}