#!/usr/bin/env ruby

$us = File.basename $0

require 'fileutils'
require 'getoptlong'
require 'json'
require 'open3'

# ----------------------------------------------------------------
# This is used to run live code for Miller Sphinx docs.
# * Edit foo.md.in
# * Run this script to generate foo.md
# * The caller should chmod 400 the foo.md file
# * See README.md for more information.

def main
  input_handle = $stdin
  output_handle = $stdout

  output_handle.puts("<!---  PLEASE DO NOT EDIT DIRECTLY. EDIT THE .md.in FILE PLEASE. --->")

  # The filename on the command line is used for link-generation; file contents
  # are read from standard input.
  input_filename = ARGV[0]
  write_quicklinks_header(output_handle, input_filename)

  while true
    begin
      content_line = input_handle.readline

      if content_line =~ /^GENMD-RUN-COMMAND$/
        cmd_lines = read_until_genmd_eof(input_handle)
        run_command(cmd_lines, output_handle)

      elsif content_line =~ /^GENMD-CARDIFY$/
        lines = read_until_genmd_eof(input_handle)
        write_card([], lines, output_handle)

      elsif content_line =~ /^GENMD-CARDIFY-HIGHLIGHT-ALL$/
        lines = read_until_genmd_eof(input_handle)
        write_card(lines, [], output_handle)

      elsif content_line =~ /^GENMD-CARDIFY-HIGHLIGHT-ONE$/
        lines = read_until_genmd_eof(input_handle)
        line1 = lines.shift
        write_card([line1], lines, output_handle)

      elsif content_line =~ /^GENMD-CARDIFY-HIGHLIGHT-TWO$/
        lines = read_until_genmd_eof(input_handle)
        write_card(lines.slice(0,2), lines.slice(2, lines.length), output_handle)

      elsif content_line =~ /^GENMD-CARDIFY-HIGHLIGHT-THREE$/
        lines = read_until_genmd_eof(input_handle)
        write_card(lines.slice(0,3), lines.slice(3, lines.length), output_handle)

      elsif content_line =~ /^GENMD-CARDIFY-HIGHLIGHT-FOUR$/
        lines = read_until_genmd_eof(input_handle)
        write_card(lines.slice(0,4), lines.slice(4, lines.length), output_handle)

      elsif content_line =~ /^GENMD-RUN-COMMAND-TOLERATING-ERROR$/
        cmd_lines = read_until_genmd_eof(input_handle)
        run_command_tolerating_error(cmd_lines, output_handle)

      elsif content_line =~ /^GENMD-RUN-COMMAND-STDERR-ONLY$/
        cmd_lines = read_until_genmd_eof(input_handle)
        run_command_stderr_only(cmd_lines, output_handle)

      elsif content_line =~ /^GENMD-SHOW-COMMAND$/
        cmd_lines = read_until_genmd_eof(input_handle)
        show_command(cmd_lines, output_handle)

      elsif content_line =~ /^GENMD-INCLUDE-ESCAPED\(([^)]+)\)$/
        included_file_name = $1
        include_escaped(included_file_name, output_handle)

      elsif content_line =~ /^GENMD-INCLUDE-AND-RUN-ESCAPED\(([^)]+)\)$/
        included_file_name = $1
        cmd_lines = File.readlines(included_file_name).map{|line| line.chomp}
        run_command(cmd_lines, output_handle)

      elsif content_line =~ /^GENMD-RUN-CONTENT-GENERATOR\(([^)]+)\)$/
        cmd = $1
        run_content_generator(cmd, output_handle)

      elsif content_line =~ /^GENMD/
        raise "Unhandled genmd line #{content_line}"
        output_handle.write(content_line)

      else
        output_handle.write(content_line)
      end
    rescue EOFError
      break
    end
  end
end

# ----------------------------------------------------------------
# Our job is to read foo.md.in and write foo.md. Then Mkdocs will create HTML
# in the build directory. However, Mkdocs writes not foo.html but
# foo/index.html. This is true for all pages except the base index.html. Also,
# in this function we're writing raw HTML not Markdown so we can't leverage
# Mkdocs foo.md -> foo/index.html renaming.  This also means we need to be
# mindful of directory structure. Given input
#   index.md.in
#   foo.md.in
#   bar.md.in
# we'll generate
#   index.md
#   foo.md
#   bar.md
# However, Mkdocs will create
#   index.html
#   foo/index.html
#   bar/index.html
# So if the input_filename is 'index.md.in' we need need to quicklink
# to ./glossary/index.html et al; if it's anything else, we need to
# quicklink to ../glossary/index.html.

def write_quicklinks_header(output_handle, input_filename)
  relative_path = '..'
  if input_filename == 'index.md.in'
  relative_path = '.'
  end
  output_handle.puts <<EOF
<div>
<span class="quicklinks">
Quick links:
&nbsp;
<a class="quicklink" href="#{relative_path}/reference-main-flag-list/index.html">Flags</a>
&nbsp;
<a class="quicklink" href="#{relative_path}/reference-verbs/index.html">Verbs</a>
&nbsp;
<a class="quicklink" href="#{relative_path}/reference-dsl-builtin-functions/index.html">Functions</a>
&nbsp;
<a class="quicklink" href="#{relative_path}/glossary/index.html">Glossary</a>
&nbsp;
<a class="quicklink" href="#{relative_path}/release-docs/index.html">Release docs</a>
</span>
</div>
EOF
# &nbsp;
# <a class="quicklink" href="https://github.com/johnkerl/miller" target="_blank">Repository ↗</a>
end

# ----------------------------------------------------------------
def read_until_genmd_eof(input_handle)
  begin

    lines = []
    while true
      line = input_handle.readline.chomp
      if line == 'GENMD-EOF'
        break
      end
      lines << line
    end
    return lines

  rescue EOFError
    $stderr.puts "$0: did not find GENMD-EOF"
    exit 1
  end
end

# ----------------------------------------------------------------
# The command can be multi-line
def run_command(cmd_lines, output_handle)
  cmd = cmd_lines.join("\n")
  # Avoid non-deterministic mingling of stdout and stderr which happens with backticks and 2>&1
  stdout, stderr, status = Open3.capture3(cmd)
  status = status.to_i
  if status != 0
    raise "\"#{cmd}\" exited with non-zero code #{status}.\nStderr:: #{stderr}"
  end
  combined = stdout + stderr
  # chomp and split so "a\nb\nc\n" splits to ["a", "b", "c"]
  write_card(cmd_lines, combined.chomp.split(/\n/, -1), output_handle)
end

# ----------------------------------------------------------------
def run_command_tolerating_error(cmd_lines, output_handle)
  cmd = cmd_lines.join("\n")
  # Avoid non-deterministic mingling of stdout and stderr which happens with backticks and 2>&1
  stdout, stderr, status = Open3.capture3(cmd)
  combined = stdout + stderr
  # chomp and split so "a\nb\nc\n" splits to ["a", "b", "c"]
  write_card(cmd_lines, combined.chomp.split(/\n/, -1), output_handle)
end

# ----------------------------------------------------------------
def run_command_stderr_only(cmd_lines, output_handle)
  cmd = cmd_lines.join("\n")
  # Avoid non-deterministic mingling of stdout and stderr which happens with backticks and 2>&1
  stdout, stderr, status = Open3.capture3(cmd)
  # chomp and split so "a\nb\nc\n" splits to ["a", "b", "c"]
  write_card(cmd_lines, stderr.chomp.split(/\n/, -1), output_handle)
end

# ----------------------------------------------------------------
def include_escaped(included_file_name, output_handle)
  unescaped_lines = File.readlines(included_file_name)
  escaped_lines = unescaped_lines.map do |line|
    # Necessary for <pre> in Mkdocs
    line.chomp.gsub("<", "&lt;").gsub(">", "&gt;")
  end
  write_card([], escaped_lines, output_handle)
end

# ----------------------------------------------------------------
def show_command(cmd_lines, output_handle)
  # The command can be multi-line
  write_card(cmd_lines, [], output_handle)
end

# ----------------------------------------------------------------
def run_content_generator(cmd, output_handle)
  # Avoid non-deterministic mingling of stdout and stderr which happens with backticks and 2>&1
  stdout, stderr, status = Open3.capture3(cmd)
  status = status.to_i
  if status != 0
    raise "\"#{cmd}\" exited with non-zero code #{status}."
  end
  combined = stdout + stderr
  output_handle.puts(combined)
end

# ----------------------------------------------------------------
def write_card(command_lines, output_lines, output_handle)

  if command_lines.length > 0
    if output_lines.length > 0
      output_handle.puts('<pre class="pre-highlight-in-pair">')
    else
      output_handle.puts('<pre class="pre-highlight-non-pair">')
    end
    command_lines.each do |command_line|
      output_handle.puts("<b>"+command_line+"</b>")
    end
    output_handle.puts("</pre>")
  end

  if output_lines.length > 0
    if command_lines.length > 0
      output_handle.puts('<pre class="pre-non-highlight-in-pair">')
    else
      output_handle.puts('<pre class="pre-non-highlight-non-pair">')
    end
    output_lines.each do |output_line|
      output_handle.puts(output_line)
    end
    output_handle.puts("</pre>")
  end

end

# ================================================================
main()
