#!/usr/bin/perl -w
# -*-cperl-*-
###################################################################
###                                                             ###
###    Author: Stefan Evert                                     ###
###   Purpose: Render compiled SFST (or source) with GraphViz   ###
###   Created: Tue Apr 12 15:25:43 2005                         ###
###  Modified: Wed Nov  4 16:09:47 2015 (schmid)                ###
###                                                             ###
###################################################################
# 
# 
# 

use FileHandle;
use Getopt::Long;

$View = 0;					# --view
$Dot = 0;					# --dot
$EPS = 0;					# --eps
$PDF = 0;					# --pdf
$Bitmap = 0;					# --bitmap
$All = 0;					# --all
$EpsString = undef;				# --null
$Labels = 0;					# --labels
$UTF8 = 0;					# --utf8
$Quiet = 0;					# --quiet
$Help = 0;					# --help

$ok = GetOptions(
		 "view|v" => \$View,
		 "dot|d" => \$Dot,
		 "eps|e" => \$EPS,
		 "pdf|p" => \$PDF,
		 "bitmap|b" => \$Bitmap,
		 "utf8|u" => \$UTF8,
		 "all|a" => \$All,
		 "null|0=s" => \$EpsString,
		 "labels|l" => \$Labels,
		 "quiet|q" => \$Quiet,
		 "help|h" => \$Help,
		 );

die
  "\n",
  "Usage:   fst-draw [options] <file>\n\n",
  "Options:\n",
  "  --view, -v    view FST diagram (okular)\n",
  "  --dot, -d     generate GraphViz input file\n",
  "  --eps, -e     generate EPS file [default]\n",
  "  --pdf, -p     translate to PDF as well (epstopdf)\n",
  "  --bitmap, -b  render as PNG bitmap (pstoimg)\n",
  "  --all, -a     generate all formats\n",
  "  --utf8, -u    expect unicode input\n",
  "  --null <s>,   show eps-transitions as <s> [default: <>]\n",
  "    -0 <s>\n",
  "  --labels, -l  show node labels (numbers)\n",
  "  --quiet, -q   run silent (no terminal output)\n",
  "  --help, -h    this help page\n\n",
  "Output file names are automatically derived from the data file,\n",
  "which must have the extension .a (compiled FST) or .fst (FST\n",
  "grammar file). In the latter case, fst-compiler will be run first.\n\n",
  "[requires: fst-compiler (SFST tools), dot (GraphViz tools),\n",
  "           epstopdf, pstoimg (latex2html package), okular (PS viewer)]\n",
  unless $ok and @ARGV == 1;

$EPS = 1					# default: --eps (if no other formats are specified)
  unless $View or $Dot or $PDF or $Bitmap;
$EPS = $Dot = $PDF = $Bitmap = 1
  if $All;

$datafile = shift @ARGV;
$datafile =~ /\.(a|fst)$/
  or die "Error: input file must have extension .fst or .a\n";
$basename = $`;
$ext = $1;

$tmpbase = "/tmp/fst2dot.$$";	   # basename for intermediate files (in /tmp)

## run fst-compiler on grammar file if necessary (otherwise copy to /tmp dir)
##   (file.fst -> file.a)
if ($ext eq "fst") {			    
  if ($UTF8) {			    
    run_command("fst-compiler-utf8 $datafile $tmpbase.a");
  }
  else {
    run_command("fst-compiler $datafile $tmpbase.a");
  }
}
else {
  run_command("cp -p $datafile $tmpbase.a");
}

print "$datafile\n"
  unless $Quiet;

## dump compiled FST and convert to .dot specification
##   (file.a -> file.dot)
$fh = new FileHandle "fst-print $tmpbase.a |"  
  or die "Can't dump FST from $tmpbase.a: $!";
$ofh = new FileHandle "> $tmpbase.dot"
  or die "Can't open output file $tmpbase.dot: $!";
print $ofh "digraph fst {\n";
print $ofh "\trankdir=LR\n";
$nodesize = ($Labels) ? .5 : .2;
printf $ofh "\tnode [shape=circle,height=$nodesize,fixedsize=true]\n";
print $ofh "\tnode [label=\"\"]\n"
  unless $Labels;
print $ofh "\t0 [style=bold]\n";
while (<$fh>) {
  chomp;
  if (/^([0-9]+)$/) {
    print $ofh "\t$1 [shape=doublecircle]\n";
  }
  else {
    my ($from, $to, $lower, $upper) = split /\t/;
    my $label = $lower;
    $label .= ":$upper" if $lower ne $upper;
    print $ofh "\t $from -> $to [label = \"$label\"]\n";
    print $ofh "\n";
  }
}
print $ofh "}\n";
$ofh->close;
$fh->close;

if ($Dot) {
  run_command("cp -p $tmpbase.dot $basename.dot");
  print "|- $basename.dot\n"
    unless $Quiet;
}

## render .dot file as PostScript image
##   (file.dot -> file.eps)
run_command("dot -Tps -o $tmpbase.eps $tmpbase.dot");
if ($EPS) {
  run_command("cp -p $tmpbase.eps $basename.eps");
  print "|- $basename.eps\n"
    unless $Quiet;
}

## convert PostScript file to PDF (--pdf option)
##   (file.eps -> file.pdf)
run_command("epstopdf $tmpbase.eps")
  if $PDF or $Bitmap;
if ($PDF) {
  run_command("cp -p $tmpbase.pdf $basename.pdf");
  print "|- $basename.pdf\n"
    unless $Quiet;
}

## convert PostScript file to bitmap image (--bitmap option)
##   (file.eps -> file.png)
if ($Bitmap) {
  run_command("pstoimg -antialias -type png $tmpbase.pdf");
  run_command("cp -p $tmpbase.png $basename.png");
  print "|- $basename.png\n"
    unless $Quiet;
}

## view .eps image with okular (--view option)
if ($View) {
  print " \\_ <viewer> \n"
    unless $Quiet;
  run_command("okular $tmpbase.eps");
  print "    (done)\n"
    unless $Quiet;
}

## clean up intermediate files in /tmp
run_command("rm -f $tmpbase.*");


######################################################################

## SUB: run_command($cmd);
##   (execute shell command $cmd silently with some error checks)
sub run_command {
  my $cmd = shift;
  my $tmp = "/tmp/fst2dot.$$.output";
  my $status = system "$cmd > $tmp 2>\&1";
  my $exit_code = $status >> 8;
  if ($status != 0) {
    print STDERR "COMMAND: $cmd\n";
    print STDERR "OUTPUT:\n";
    print STDERR `cat $tmp`;
  }
  unlink $tmp;
  if ($status != 0) {
    if ($tmpbase) {
      system "rm -f $tmpbase.* >/dev/null 2>\&1"; # clean up intermediate files
    } 
    die "Error: command execution failed (exit code $exit_code).\n";
  }
}
