# MERRendererHTML.rb
#
# Author: David Joy
#
# These software packages were developed at the National Institute of Standards and Technology
# by employees of the Federal Government in the course of their official duties. Pursuant to 
# title 17 Section 105 of the United States Code this software is not subject to copyright 
# protection and is in the public domain. These software packages are experimental systems.  
# NIST assumes no responsibility whatsoever for its use by other parties, and makes no 
# guarantees, expressed or implied, about its quality, reliability, or any other characteristic. 
# We would appreciate acknowledgement if the software is used.  This software can be 
# redistributed and/or modified freely provided that any derivative works bear some notice that
# they are derived from it, and any modified versions bear some notice that they have been 
# modified.

require 'optparse'
require 'libxml'
include LibXML

options = {}
#Parse options
OptionParser.new do |opts|
  opts.banner = "Usage: MERRenderer.rb [options]"

  opts.on("-m", "--mer MEROUTPUTFILE", "MER Output file to render") do |m|
    options[:mer] = m
  end
  opts.on("-o", "--outfile [OUTFILE]", "Output file for the render") do |o|
    options[:out] = o
  end
  opts.on("-d", "--dtd DTDFILE", "Check MER Output against specified DTD") do |d|
    options[:dtd] = d
  end
end.parse!

#if options bad exit 1
if options[:mer].nil? || options[:dtd].nil?
  abort "Missing required arguments #{options[:mer].nil? ? '-m' : ''} #{options[:dtd].nil? ? '-d' : ''}, run with -h for help.".squeeze(" ")
end
options[:out] += ".html" if !options[:out].nil? && options[:out] !~ /.html$/i

#Load MER Output
@mer = XML::Document.file(options[:mer])
if !options[:dtd].nil?
  #Load DTD
  @dtd = XML::Dtd.new(File.read(options[:dtd]))
  puts "Validating.."
  if @mer.validate(@dtd)
    puts "MER output file '#{ options[:mer] }' validated!"
  else
    puts "MER output file '#{ options[:mer] }' did not validate!"
  end
else
  puts "No DTD included, skipping validation.."
end

def add_css_styles
  #Add css styles
  puts '<style type="text/css">'
  #Tooltips
  puts '.obs-tooltip {'
  puts 'border-bottom: 1px dotted #333;'
  puts 'position: relative;'
  puts 'cursor: pointer;'
  puts '}'
  puts ''
  puts '.obs-tooltip:hover:after {'
  puts 'content: attr(data-obs-desc)"\a"attr(data-obs-conf)" | "attr(data-obs-imp);'
  puts 'display: inherit;'
  puts 'position: absolute;'
  puts 'width: 400px;'
  puts 'white-space: pre-line;'
  puts 'z-index: 1;'
  puts 'background: #EAF2D3;'
  puts 'padding: 3px 7px;'
  puts 'color: #000;'
  puts 'border: 1px solid #A7C942;'
  puts 'border-radius: 3px;'
  puts '-moz-border-radius: 3px;'
  puts '-webkit-border-radius: 3px;'
  puts 'left: 24px;'
  puts 'top: -24px;'
  puts '}'

  #Table styles
  puts '#observations'
  puts '{'
  puts 'width:96%;'
  puts 'border-collapse:collapse;'
  puts 'margin-bottom:6px;'
  puts '}'
  puts '#observations td, #observations th '
  puts '{'
  puts 'font-size:1em;'
  puts 'border:1px solid #A7C942;'
  puts 'padding:3px 7px 2px 7px;'
  puts '}'
  puts '#observations th'
  puts '{'
  puts 'font-size:1.4em;'
  puts 'text-align:center;'
  puts 'padding-top:5px;'
  puts 'padding-bottom:4px;'
  puts 'background-color:#A7C942;'
  puts 'color:#ffffff;'
  puts '}'
  puts '#observations tr.label td '
  puts '{'
  puts 'color:#000000;'
  puts 'background-color:#EAF2D3;'
  puts '}'
  puts '#observations tr'
  puts '{'
  puts 'background-color:#FFFFFF;'
  puts '}'
  puts '#relationships'
  puts '{'
  puts 'width:96%;'
  puts 'border-collapse:collapse;'
  puts 'margin-bottom:6px;'
  puts '}'
  puts '#relationships td, #relationships th '
  puts '{'
  puts 'font-size:1em;'
  puts 'border:1px solid #B547B5;'
  puts 'padding:3px 7px 2px 7px;'
  puts '}'
  puts '#relationships th'
  puts '{'
  puts 'font-size:1.4em;'
  puts 'text-align:center;'
  puts 'padding-top:5px;'
  puts 'padding-bottom:4px;'
  puts 'background-color:#B547B5;'
  puts 'color:#ffffff;'
  puts '}'
  puts '#relationships tr.label td '
  puts '{'
  puts 'color:#000000;'
  puts 'background-color:#D699D6;'
  puts '}'
  puts '#relationships tr'
  puts '{'
  puts 'background-color:#FFFFFF;'
  puts '}'
  puts '#activitysequence'
  puts '{'
  puts 'width:96%;'
  puts 'border-collapse:collapse;'
  puts 'margin-bottom:6px;'
  puts '}'
  puts '#activitysequence td, #activitysequence th '
  puts '{'
  puts 'font-size:1em;'
  puts 'border:1px solid #FF8330;'
  puts 'padding:3px 7px 2px 7px;'
  puts '}'
  puts '#activitysequence th'
  puts '{'
  puts 'font-size:1.4em;'
  puts 'text-align:center;'
  puts 'padding-top:5px;'
  puts 'padding-bottom:4px;'
  puts 'background-color:#FF8330;'
  puts 'color:#ffffff;'
  puts '}'
  puts '#activitysequence tr.label td '
  puts '{'
  puts 'color:#000000;'
  puts 'background-color:#FFBD7D;'
  puts '}'
  puts '#activitysequence tr'
  puts '{'
  puts 'background-color:#FFFFFF;'
  puts '}'
  #
  puts '#evidence-element-container'
  puts '{'
  puts 'width:96%;'
  puts 'margin-left:auto;'
  puts 'margin-right:auto;'
  puts 'padding-bottom:6px;'
  puts '}'
  puts '#evidence-container-label'
  puts '{'
  puts 'width:100%;'
  puts 'border-radius:4px;'
  puts 'margin-left:auto;'
  puts 'margin-right:auto;'
  puts 'font-size:1.4em;'
  puts 'text-align:center;'
  puts 'padding-top:5px;'
  puts 'padding-bottom:4px;'
  puts 'margin-top:6px;'
  puts 'margin-bottom:6px;'
  puts 'color:#ffffff;'
  puts '}'

  puts '</style>'
end

def render_relationships(rels, table_id)
  rels.each do |rel|
    puts %|<table id="#{table_id}" align="center">|
    puts '<tr class="label">'
    puts %|<td>#{rel.attributes["description"]}</td>|
    puts %|<td width="80">C = #{rel.attributes["confidence"]}</td>|
    puts %|<td width="80">I = #{rel.attributes["importance"]}</td>|
    puts '</tr>'
    semantics = rel.children.select { |child| child.name =~ /semantic_inference/i }
    semantics.each do |si|
      puts '<tr>'
      puts '<td colspan="3">'
      puts '<span '
      puts %|data-obs-desc="#{@obs_hash[si.attributes["id"]].attributes["description"]}"|
      puts %|data-obs-conf="C = #{@obs_hash[si.attributes["id"]].attributes["confidence"]}"|
      puts %|data-obs-imp="I = #{@obs_hash[si.attributes["id"]].attributes["importance"]}"|
      puts 'class="obs-tooltip">'
      puts %|<i>#{si.attributes["description"]}</i>|
      puts '</span>'
      puts '</td>'
      puts '</tr>'
    end
    puts '</table>'
  end
end

#Render
if !options[:out].nil?
  puts "Writing output to file '#{options[:out]}' .."
  $stdout = File.new(options[:out], 'w')

  add_css_styles
  puts '<body style="font-family:Arial,Helvetica,sans-serif;">'
  puts '<div id="evidence" style="width:90%;height:100%;margin-left:5%;margin-right:5%;border: 2px black solid;background:#5C5C5C;align:center;overflow-y:scroll">'
  root = @mer.root
  root.children.each do |evidence|
    @obs_hash = {}
    @observations = evidence.children.select { |child| child.name =~ /observation/i }
    @observations.each do |obs|
      @obs_hash[obs.attributes["id"]] = obs
    end

    @activity_sequences = evidence.children.select { |child| child.name =~ /sequence_of_activities/i }
    @activity_sequences.each_with_index do |as, i|
      puts '<div id="evidence-element-container">'
      puts '<div id="evidence-container-label" style="background-color:#FF8330">'
      puts %|<span style="float:left;margin-left:8px;margin-right:8px">#{(i+1).to_s}</span>|
      puts '<span>Sequence of Activities</span>'
      puts '</div>'
      
      @as_relationships = as.children.select { |child| child.name =~ /relationship/i }
      render_relationships(@as_relationships.sort {|a,b| a.attributes["startTime"] <=> b.attributes["startTime"]}, "activitysequence")

      puts '</div>'
    end

    @relationships = evidence.children.select { |child| child.name =~ /relationship/i }
    if !@relationships.empty?
      puts '<div id="evidence-element-container">'
      puts '<div id="evidence-container-label" style="background-color:#B547B5">Relationships</div>'

      render_relationships(@relationships, "relationships")
      
      puts '</div>'
    end

    if !@observations.empty?
      puts '<div id="evidence-element-container">'
      puts '<div id="evidence-container-label" style="background-color:#A7C942">Observations</div>'

      @observations.each do |obs|
        puts '<table id="observations" align="center">'
        puts '<tr class="label">'
        puts %|<td>#{obs.attributes["type"]}</td>|
        puts %|<td width="80">C = #{obs.attributes["confidence"]}</td>|
        puts %|<td width="80">I = #{obs.attributes["importance"]}</td>|
        puts '</tr>'
        puts '<tr>'
        puts %|<td colspan="3">#{obs.attributes["description"]}</td>|
        puts '</tr>'
        puts '</table>'
      end
      puts '</div>'
    end
  end
  puts '</div>'
  puts '</body>'
  $stdout = STDOUT
  puts "Done."
end
