//
// Copyright (c) 2024, Brian Frank and Andy Frank
// Licensed under the Academic Free License version 3.0
//
// History:
//   05 Nov 2024  Matthew Giannini  Creation
//

**
** PostProcessors are run as the last step of parsing and provide an opportunity
** to inspect/modify the parsed AST before rendering.
**
@Js
mixin PostProcessor
{
  ** Post-process this node and return the result (which may be a modified node).
  abstract Node process(Node node)
}

**
** A post-processor for handling link resolution
**
@Js
@NoDoc mixin LinkResolver : PostProcessor, Visitor
{
  override Node process(Node node)
  {
    node.walk(this)
    return node
  }

  override Void visitLink(Link link) { resolve(link) }
  override Void visitImage(Image img) { resolve(img) }

  ** Resolve the given `LinkNode`. This will be called prior to any rendering
  ** for the given node and provides an opportunity to modify the link destination,
  ** mark the link as code, and change the link display text.
  protected abstract Void resolve(LinkNode linkNode)
}

**
** A post-processor for generating anchor ids for headings
**
@Js
@NoDoc class HeadingProcessor : PostProcessor, Visitor
{
  new make()
  {
    this.ids = [Str:Int?][:]
  }

  private [Str:Int?] ids

  override Node process(Node node)
  {
    node.walk(this)
    return node
  }

  ** Generate an id per [GitHub markdown rules]`https://docs.github.com/en/get-started/writing-on-github/getting-started-with-writing-and-formatting-on-github/basic-writing-and-formatting-syntax#section-links`
  **
  ** 1. Letters are converted to lower case
  ** 2. Spaces are replaced by hypens ('-'). Any other whitespace character
  **    or punctuation characters are removed
  ** 3. Leading and trailing whitespace are removed
  ** 4. Markup formatting is removed leaving only the contents
  **    (for example _italics_ becomes italics)
  ** 5. If the auto-generated anchor for a heading is identical to an earlier anchor
  **    in the same document, a unique identifier is generated by appending a hyphen and
  **    an auto-incrementing integer.
  override Void visitHeading(Heading heading)
  {
    // get all the text content of the heading
    words := Str[,]
    heading.eachDescendant |node|
    {
      if (node is Text || node is Code) words.add(node->literal)
    }

    // join into a single str that is normalized to lower-case
    // and  has leading/trailing whitespace removed
    text := words.join.lower.trimToNull
    if (text == null) return

    // build the normalized anchor
    buf := StrBuf()
    text.each |ch, i|
    {
      if (ch.isAlphaNum) buf.addChar(ch)
      else if (ch == ' ') buf.addChar('-')
    }
    anchor := buf.toStr

    // check for collision
    id := ids.get(anchor)
    if (id == null)
    {
      // never seen this anchor before
      ids[anchor] = 0
    }
    else
    {
      // seen this anchor before, so append auto-incrementing integer
      id = id + 1
      ids[anchor] = id
      anchor = "${anchor}-${id}"
    }

    // done!
    heading.anchor = anchor
  }
}