#
# = generate.rb, for the ruby-journal project.
#
# Documents are written in Textile and converted to HTML with RedCloth.
#
# However, RedCloth only generates HTML _fragments_.  We need to generate HTML _documents_,
# with <tt><head></tt> information (especially CSS), etc.
#
# The simple function +generate_document+ does this.
#

require 'rubygems'
require 'redcloth'
require 'extensions/io'
require 'celsoft.com/template'
require 'pathname'

  #
  # Takes the Textile file from +path+ (relative to ~/Projects/dev-utils) and generates an HTML
  # file in the appropriate place, which is the <tt>build/www</tt> directory, and with an +html+
  # instead of +textile+ extension.
  #
  # The HTML document will contain an inline stylesheet.
  #
  # The first "h1." tag in the document is used as the title of the HTML page. 
  #
  # Special lines !header and !toc are expanded.
  #
def generate_document(input_path, output_dir)
  output_path = File.join(output_dir, "#{File.basename(input_path)}".sub(/\.textile/, ".html"))
  raw_text = File.read(input_path)
  #trace {'input_path'}
  #trace { 'raw_text.length' }
  input_text = process_text(raw_text)
  red_cloth_text = RedCloth.new(input_text)
  red_cloth_text.fold_lines = true
  html_fragment = red_cloth_text.to_html
  html = html_wrap(html_fragment)
  File.write(output_path, html)
  #File.write(output_path + '.tmp', input_text)
end

  #
  # Expand any meta-markers like !header and !toc.
  #
def process_text(text)
  require 'shellwords'
  text.gsub(/^!(.*)\s*$/) {
    command, *args = Shellwords.shellwords($1.strip)
    case command
    when 'header' then header(*args)
    when 'toc'
      _toc = toc(text)
      _toc
    else
      raise "Unknown command embedded in textile input: #{command}"
    end
  }
end

CSS_FILE = 'etc/doc/textile.css'

  #
  # Wraps the given HTML fragment in head, body, etc., to produce a proper document.
  #
  # It points to a hardcoded stylesheet. 
  #
def html_wrap(fragment)
  require 'extensions/string'
  title = fragment.scan(%r{<h1>(.*?)</h1>}).first || []
  title = title.first || "(no title)"
  return %{
    <html>
    <head>
      <title>#{title}</title>
      <style type="text/css">
        #{File.read(CSS_FILE).tabto(6)}
      </style>
    </head>
    <body>
    #{fragment}
    </body>
    </html>
  }.tabto(0)
end

def header(*args)
  require_arg = args.shift or raise ArgumentError
  require_text = "%(small-title)<code>require '#{require_arg}'</code>%"
  if args.shift == 'main'
    main_page_link = '%(mylink)"main page":index.html%'
  else
    main_page_link = ''
  end
  %!table{width:100%}.\n| #{require_text} |>. #{main_page_link} |\n\n!
end

TOC_TEMPLATE = Template::Document.new
TOC_TEMPLATE.load %!
  <table class="toc_table">
    <tr>
      <td>
        <span class="header">Links</span><br/>
        <ul id="links">
        ${each links}
          <li><a href="${var href}">${var name}</a></li>
        ${end}
        </ul>
      </td>
      <td>
        <span class="header">Contents</span><br/>
        <ul id="contents">
        ${each contents}
          <li><a href="#\${var anchor}">${var heading}</a>
            ${if subheadings}
            <ul>
              ${each subheadings}
                <li><a href="#\${var anchor}">${var heading}</a></li>
              ${end}
            </ul>
          </li>
          ${end}
        ${end}
        </ul>
      </td>
    </tr>
  </table>
!

Heading = Struct.new(:level, :heading, :anchor)

  # Returns the HTML for a table containing links (from links.dat) and the contents
  # of this page, derived from the given text.  h2 and h3 markup elements are taken
  # to be first- and second-level headings in the text.  The text itself is modified
  # so that those headings have anchors on them.
def toc(text)
    # Form links: [ { 'name' => 'Synopsis', 'href' => 'Synopsis.html' }, ... ]
  links = links().map { |name, href|
    Hash[ 'name', name, 'href', href ]
  }

    # Form contents: [
    #   { 'heading' => 'Introduction', 'anchor' => 'Introduction', 'subheadings' => [ { ... } ] },
    #   ...
    # ]
  regex = /^h([234])\.\s+(.*?)$/
  headings = text.grep(regex).map { |line|
    line =~ regex
    level, heading = $1.to_i - 1, $2.strip
    anchor = heading.gsub(/[^\s\w]/, '')
    #Hash[ 'level', level, 'heading', heading, 'anchor', anchor ]
    Heading.new(level, heading, anchor)
  }
  contents = []
  headings.each do |h|
    case h.level
    when 1
      contents << Hash['heading', h.heading, 'anchor', h.anchor]
    when 2
      (contents.last['subheadings'] ||= []) << Hash['heading', h.heading, 'anchor', h.anchor]
    end
  end

    # Place anchors into document.  This changes the input text.
  text.gsub!(regex) {
    match = Regexp.last_match
    heading = $2.strip
    anchor = heading.gsub(/[^\s\w]/, '')
    match[0].chomp + %{ <a name="#{anchor}"/>\n}
  }

    # Run it through the template.
  TOC_TEMPLATE.data = { 'links' => links, 'contents' => contents }
  TOC_TEMPLATE.output
end

  # Return [ [name, href], [name, href], ... ].
def links
  @links ||=
    begin
      links_file = Pathname.new(__FILE__).dirname + 'links.dat'
      File.readlines(links_file).grep(/^\w/).map { |line|
        line =~ %r{^(.*?)/(.*)$} or raise "Bad data in links.dat."
        [$1.strip, $2.strip]
      }
    end
end