#! /usr/bin/env ruby
#

USAGE = """
javadoc-markdown.rb - Print out the java source with markdown replaced by html

Usage:

  Single file version:
  $ javadoc-markdown.rb < input.java > output.java

  gradlew friendly version:
  $ javadoc-markdown.rb SRCDIR DESTDIR
"""

# should work with recent ubuntu packaged version of ruby-redcarpet, no need
# to install gems etc
require 'redcarpet'
require 'fileutils'

RENDERER = Redcarpet::Render::HTML.new
MARKDOWN = Redcarpet::Markdown.new(RENDERER, extensions = {})


# Store a block of javadoc
class Block

  def initialize()
    @lines = []
    @indent_size = 0
  end

  def add_line(line)
    if @lines.size == 0
      @indent_size = [0, line.index("*") - 1].max
    end

    @lines.push(line.strip.delete_prefix("*"))
  end

  def to_s
    @lines.join("\n")
  end

  def to_javadoc(out)
    collected = []

    # start the block
    out.puts("#{' ' * @indent_size}/**")

    # convert content to html
    @lines.each do |line|
      # We attempt to convert each tag in its own chunk, so start and stop on '@'
      if line.strip.start_with?('@')
        render(collected, out)
        collected = []
      end

      collected.push(line)
    end

    # render the remainder
    render(collected, out)

    # close the block
    out.puts("#{' ' * (@indent_size + 1)}*/")
  end

  # print out some lines of markdown as html with leading ' * '
  def render(lines, out)
    return if lines.empty?

    string = lines.join("\n")

    rendered = MARKDOWN.render(string)

    # remove <p> </p> from tagged lines - NB this is a bit nasty and could result
    # in us producing invalid html, but it removes the situation where we produce
    # html like <p>@return</p> which confuses the javadoc tool
    # NB we might decide it's safe to remove this...
    if lines.first.strip.start_with?('@')
      rendered = rendered.strip.delete_prefix('<p>').delete_suffix('</p>')
    end

    rendered.split("\n").each {|rendered_line| out.puts("#{' ' * (@indent_size + 1)}* #{rendered_line}") }

  end

end

# does line by line replacement of java source, scanning for /** */ blocks
def replace_markdown(input, output)

  state = :code
  block = Block.new

  input.each {|line|

    # look for /**
    if state == :javadoc
      # NB this will ignore any doc string on the line if it's been improperly
      # terminated (i.e. `* some comment */`) but we never do this, I've only
      # seen it in picocli's src, which I care less about as long as it doesn't
      # kill the build
      if line.strip.end_with?("*/")
        block.to_javadoc(output)
        block = Block.new
        state = :code
      else
        block.add_line(line)
        line = "" # print nothing out
      end
    else
      if line.strip == "/**"
        state = :javadoc
      else
        output.puts(line)
      end
    end

  }
end

# Main

if ![0, 2].any?(ARGV.length)
  STDERR.puts("Wrong number of args: #{ARGV.length}")
  STDERR.puts(USAGE)
  exit 1
end


if ARGV.length == 0
  # first form - convert a single file using stdin and stdout
  replace_markdown(STDIN, STDOUT)
else
  # second form - srcdir destdir form
  srcdir, destdir = ARGV
  sources = Dir[srcdir + '/**/*.java']

  FileUtils.mkdir_p(destdir)

  sources.each do |source|

    # trim src dir prefix from destdir filename to preserve only the package dir structure
    outfile = File.join(destdir, source[srcdir.length..-1])

    FileUtils.mkdir_p(File.dirname(outfile))

    File.open(outfile, 'w') do |output|
      File.open(source, 'r') do |input|
        replace_markdown(input, output)
      end
    end
  end
end
