###
# ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
# Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
# SPDX-License-Identifier: Apache-2.0
# ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
###

###

  This file collects documentation sources from around the
  module and creates two outputs:
    * documentation.html at the module root
      this contains all the documentation in a single file

    * additional .md files in the documentation directory
      this contains materials scraped from other sources

  The goal is to have documentation visible in source control
  places that handle .md files elegantly, and to have an easy
  to search local reference once you've pulled the repo.

###


path = require 'path'
fs = require 'fs'
chalk = require 'chalk'


render = ->

  moduleRoot = path.join __dirname, '..', '..'

  makeLinkName = (s) ->
    # convert human readable names into valid url links
    s.replace /\s+/g, '_'

  isCodeLink = (name) ->
    return false unless name[0].toLowerCase() == name[0]
    return true

  # customize marked to collect heading info
  markedLib = require 'marked'
  headings = []
  marked = null
  do ->
    renderer = new markedLib.Renderer()

    renderer.heading = (text, level) ->
      link = makeLinkName text
      headings.push
        level: level
        text: text
        link: link
      return "<h#{level} id='#{link}'>#{text}</h#{level}>"
  
    markedLib.marked.setOptions { renderer: renderer }
    marked = (text) -> markedLib.marked.parse(text)


  entries = {}
  entryNames = []

  do ->
    pegFilename = path.join moduleRoot, 'parser', 'litexa.pegjs'
    pegSource = fs.readFileSync pegFilename, 'utf8'

    test = /\/\*\s*litexa\s+\[([^\]]+)\]\s+([^*]*\*+(?:[^/*][^*]*\*+)*)\//g
    match = test.exec pegSource
    while match
      name = match[1]
      comment = match[2][0...-1]
      if name of entries
        throw "You have duplicate entry names: #{name}"
      entries[name] =
        text: comment
        name: name
      match = test.exec pegSource

  html = fs.readFileSync path.join(__dirname, 'template.html'), 'utf8'


  do ->
    pages = []

    source = fs.readFileSync path.join(moduleRoot, '..', 'README.md'), 'utf8'
    pages.push marked source

    docsRoot = path.join moduleRoot, 'documentation'
    for file in fs.readdirSync docsRoot
      continue if file == 'reference.md'
      filename = path.join docsRoot, file
      continue if fs.statSync(filename).isDirectory()
      source = fs.readFileSync filename, 'utf8'
      pages.push marked source

    html = html.replace '{pages}', pages.join '\n'


  do ->
    entryHTML = ""
    entryNames = ( n for n of entries )
    entries = ( e for n, e of entries )
    entries.sort (a, b) -> a.name.localeCompare(b.name)

    substituteLinks = (text) ->
      test = /\[([a-zA-Z$@ ]+)\]/
      match = test.exec text
      while match
        name = match[1]
        unless name in entryNames
          console.error chalk.red "missing link [#{name}] in `#{text[0...25]}`"
        link = "<a href='##{makeLinkName name}'>#{name}</a>"
        if isCodeLink name
          link = "<code>#{link}</code>"
        text = text.replace match[0], link
        match = test.exec text
      return text

    headings.push
      level: 1
      text: "Language Reference"
      link: "Language_Reference"
      class: "toc-language-reference"

    for e in entries
      linkName = makeLinkName e.name

      headings.push
        level: 2
        text: e.name
        link: linkName

      contents = e.text
      contents = marked contents
      contents = substituteLinks contents

      namePart = e.name
      if isCodeLink e.name
        namePart = "<code>#{namePart}</code>"

      entryHTML += "
        <tr class='entry'>
          <td id='#{linkName}'
              class='entry-cell entry-name'>#{namePart}</td>
          <td class='entry-cell entry-text'>#{contents}</td>
        </tr>
      "

    html = html.replace '{entries}', entryHTML

  do ->
    tocHTML = []
    links = {}
    level = 1
    for h in headings

      opening = false
      if h.level > level
        tocHTML.push "<ul>"
        opening = true
      else if h.level < level
        tocHTML.push "</ul></li>"
      level = h.level

      if h.link of links
        console.error chalk.red "DUPLICATE LINK detected: #{h.link}"
      links[h.link] = true

      link = "<a href='##{h.link}'>#{h.text}</a>"
      #if isCodeLink h.text
      #  link = "<code>#{link}</code>"

      if opening
        terminator = '</li>'
      else
        terminator = ''

      if h.class?
        tocHTML.push "<li class='#{h.class}'>#{link}#{terminator}"
      else
        tocHTML.push "<li>#{link}#{terminator}"

    while level > 1
      tocHTML.push "</ul></li>"
      level -= 1

    html = html.replace '{toc}', tocHTML.join '\n'

  do ->
    # write HTML version, intentionally disabled right now
    ###
    outputFilename = path.join moduleRoot, '..', 'documentation.html'
    fs.writeFileSync outputFilename, html, 'utf8'
    console.log ''
    console.log chalk.green "Wrote #{outputFilename}"
    ###

  do ->
    # instructions to vuepress, where the docs are hosted
    frontMatter = """
      ---
      sidebarDepth: 1
      ---
    """

    markdownEntries = [
      frontMatter
      "# Language Reference\n"
    ]

    substituteLinks = (text) ->
      test = /\[([^\]]+)\]/
      match = test.exec text
      while match
        name = match[1]
        text = text.replace match[0], match[1]
        match = test.exec text
      return text


    for e in entries
      contents = e.text
     #(don't need to do this now) contents = substituteLinks contents

      markdownEntries.push """
      ## #{e.name}

      #{contents}
      """

    outputFilename = path.join moduleRoot, 'documentation', 'reference.md'
    fs.writeFileSync outputFilename, markdownEntries.join('\n'), 'utf8'

  console.log ''
  console.log chalk.green (new Date).toLocaleString()

render()

if process.argv[2] == 'watch'
  docsDir = path.join(__dirname, '../')
  fileCache = {}

  # Takes a string and generates a 32-bit integer
  hash = (contents) ->
    hashedInt = 0
    return hashedInt if contents.length == 0
    for i in [0..contents.length - 1]
      char = contents.charCodeAt(i)
      hashedInt = ((hashedInt << 5) - hashedInt) + char
      hashedInt = hashedInt || 0
    hashedInt

  # Watches for changes
  fs.watch docsDir, (event, file) ->
    filePath = path.join(docsDir, file)
    fileExists = fs.existsSync filePath

    # Ignores extraneous temp data
    if fileExists
      format = 'utf8'

      # hash the file
      data = hash(fs.readFileSync filePath, format)

      # Caches the fingerprint of the data
      fileCache[file] = data unless fileCache[file]?

      # If the contents change, cache contents then re-render the documentation
      if fileCache[file] != data
        fileCache[file] = data
        render()
