#!/usr/bin/perl
#
# This will be the main TableGen executable and preprocessor.
#
use strict;

use vars qw [ $tgexec $debug $tmpExt $plain $outFile $onlyPre $noPost $scopeStarts
	      $latex $latexVerLines $latexHorLines $latexHeader 
	      $html $htmlBorder $htmlHeader ];
$debug = 0;
$outFile = 0;
$onlyPre = 0;
$noPost = 0;
$plain = 0;
$html  = 0;
$htmlBorder = 0;
$htmlHeader = 0;
$latex = 0;
$latexVerLines = 0;
$latexHorLines = 0;
$latexHeader = 0;

$scopeStarts = "^((if)|(else)|(while)|(for)|(foreach)|(func))";

# Location of interpretor
$tgexec = 'main/TableGen';

# Temporary files extension
$tmpExt = ".tmpout";

my $prog = pop @ARGV;

my $index = 0;
my $skip = 0;
for (@ARGV) {
	$index += 1;
	if ($skip) {
		$skip = 0;
		next;
	}
	
	/^--help$/ && do { &help; next };
	/^--debug$/ && do { $debug = 1; next };
	/^--html-border$/ && do { $htmlBorder = &getNext ($index); 
				&help ("html-border parameter is wrong. Must be integer.") 
					if (!($htmlBorder =~ /^\d+$/)); 
				next };
	/^--html-header$/ && do { $htmlHeader = 1; next };
	/^--only-intermediate$/ && do { $noPost = 1; next };
	/^--only-preprocess$/ && do { $onlyPre = 1; next };
	/^--output$/ && do { $outFile = &getNext ($index); next };
	/^--output-type$/ && do { my $type = &getNext ($index);
			   if ($type eq 'plain') {
			   	$plain = 1;
			   } elsif ($type eq 'html') {
			   	$html = 1;
			   } elsif ($type eq 'latex') {
				$latex = 1;
			   } else {
		   		&help ("Error. Unknown type of output: $type.");
			   }
			   next };
	/^--latex-all-lines$/ && do { $latexVerLines = 1; $latexHorLines = 1; next };
	/^--latex-only-hor-lines$/ && do { $latexHorLines = 1; $latexVerLines = 0; next };
	/^--latex-only-vert-lines$/ && do { $latexVerLines = 1; $latexHorLines = 0; next };
	/^--latex-header$/ && do { $latexHeader = 1; next };
	# More commandline options are to come. Follow 
        &help ("Can't process request because I do not know what $_ means. \n");

	# Nested subroutine so that we keep the same scope.
	sub getNext {
		my $index = shift;
		$skip = 1;
		return $ARGV[$index];
	}
}

sub help {
	my $error = shift;
	print STDERR "$error\n";
	print "TableGen expects the filename at the end of any commandline sequence: \n".
		"TableGen <options> [filename].\n\n";
	print "Available options: \n";
	
	print "\t--debug\t\t\t\t\tEnables debugging output and activities.\n";
	
	print "\t--help\t\t\t\t\tPrints this help menu.\n";

	print "\t--html-border [size]\t\t\t\t\tSets border of html chart to \"size\". Default 0.\n";
	print "\t--html-header\t\t\t\t\tPrint html title, body, and other common lines for a new html file.\n";

	print "\t--only-intermediate\t\t\t\t\tKeeps intermediate output of program without postprocessing.\n";
	print "\t--only-preprocess\t\t\t\t\tOutputs preprocessed code, but without running code.\n";

	print "\t--output [filename]\t\t\t\t\tPuts program output into filename.\n";

	print "\t--output-type [type of output]\t\tControls the type of output interpretor generates.\n";
	print "\t\tAvailable output types:\n";
	print "\t\t\thtml\n";
	print "\t\t\tplain\n";
	print "\t\t\tlatex\n";

	print "\t--latex-all-lines\t\t\t\t\tCreates horizontal and vertical lines for latex charts.\n";
	print "\t--latex-only-hor-lines\t\t\t\t\tCreates only horizontal lines for latex.\n";
	print "\t--latex-only-vert-lines\t\t\t\t\tCreates only vertical lines for latex.\n";
	print "\t--latex-header\t\t\t\t\tPrint latex being and end statement for a new latex file.\n";
	# Describe any options that you add here. New Print statement for each option.
	exit 0;
}

# Make sure that we output something.
if ($html == 0 && $latex == 0) {
	$plain = 1;
}

# Check input:
if (! -e $prog) {
	&help ("Error: The filename ($prog) of the program you wish to run does not exist.\n");
}

# Take program and shove it into memory: 
open PROG, $prog or die "Could not open $prog: $!\n";

my @lines = <PROG>;

push @lines, "\n"; # Last element needs to end with a newline for the preprocessor.

# Run proprocess. Pass it as reference because we do not want 
# to make copies of a potentially big list of lines.
my $code = "";
&preprocess (\@lines);

if ($outFile) {
        open OUT, $outFile or die "Can't open $outFile for output: $!\n";
        select OUT;
}


# Exit if user only wants to see preprocessed output.
if ($onlyPre) {
	print $code;
	exit 0;
}

# Now take the pre-processed code and run it through the interpreter:
my $out = &getTmpOut ($prog);
open TMP, ">$out" or die "Can't write to temporary file: $!\n";
print TMP "$code";
my $basicOutput = `java $tgexec $out`;
unlink "$out" if (!$debug);

if ($noPost) {
	print $basicOutput;
	exit 0;
}



# Debugging framwork below.

#open TMP, $prog;

#my $basicOutput = "";
#$basicOutput = $basicOutput . $_ for (<TMP>);

# Debugging framework end.



&printPlain ($basicOutput) if ($plain);
&printHtml ($basicOutput) if ($html);
&printTex ($basicOutput) if ($latex);

# Subroutines below...
sub preprocess {
	my $ref = shift;

	my $wscnt = 0;
	my $brak = 0;
	my $comment = 0;
	for my $next (@{$ref}) {
		# Don't mess with comments
		$comment = 1 if ($next =~ /\/\*/); # /*
		$comment = 0 if ($next =~ /\*\//); # */
		if ($comment) {
			$code = $code . $next;
			next;
		}

		# Remove new lines at end...we add them later.
		next if (/^\n$/); # Skip all new lines.
		chomp ($next);

		#Place square brackets around coordinates and make separator 
		#a pipe
		$next =~ s/->\s*(.+\s*),(\s*.+)\s*$/-> \[$1\|$2\]/;

		#Brackets at begining of tab increase.
		if ($next =~ /^(\t+)/) {
			my @ws;
			if (!$1) {
				die "This should never happen.";
			} else {
				@ws = split //, $1;
			}

			if (@ws > $wscnt) {
				$next =~ s/^/$1 \{\n/;
				$brak += 1;
			} elsif ($brak > @ws) {
				while ($brak > @ws) {
					my $tabs = "\t" x ($brak - 1);
					$next =~ s/^(\t+)/\n$tabs\}\n$1/;
					$brak -= 1;
				}
			} elsif (@ws < $wscnt) {
				$next =~ s/^(\t+)/\n$1\}\n$1/;
				$brak -= 1;
			} else {
				# Add the new line back.
				$code = $code . "\n";
			}
			$wscnt = scalar @ws;
			$code = $code . "$next";	

			next;
		} elsif ($brak > 0) {
			# Here we "abruptly" exit scope, and need to fill out all 
			# the brakets we need. The number of brakets is 
			# coincidentally the number of tabs.
			while ($brak > 0) {
				$code = $code . "\n" . ("\t" x ($brak - 1));
				$code = $code . "}\n";
				$brak -= 1;
			}
			$wscnt = 0;
		} else {
			$code = $code . "$next";
			# This is keep open brakets on same line as start of 
			# scope.
			$code = $code . "\n" if (!($next =~ /$scopeStarts/));
		}

	}
}

sub getTmpOut {
	# Take only the last part of path. And append 
	# a tmp file extension.
	my $path = shift;

	if ($path =~ /\/(.+)$/) {
		return $1 . $tmpExt;
	} else {
		return $path . $tmpExt;
	}
}

sub printPlain {
        my $out = shift;
        my @rows = split /\n/, $out;

        my $maxW = &getMaxColWidth ($out) + 1;
        
        # Easy case first.
        for (@rows) {
		chomp;
                my @cols = split /\t/, $_;
                for (@cols) {
        	        print $_;
			# Leave the needed amount of space between column.
			my @chars = split //, $_;
                        print " " x ($maxW - int (@chars));
                }
                print "\n";
        }
}

sub printHtml {
	my $out = shift;

	if ($htmlHeader) {
		print qq(<html>\n);
		print qq(<head>\n);
		print qq(<title>Title</title>\n);
		print qq(</head>\n);
		print qq(<body>\n);
	}

	print qq(<table border=$htmlBorder>\n);
	my @rows = split /\n/, $out;
	for (@rows) {
		print qq(<tr>\n);
                my @cols = split /\t/, $_;
                for (@cols) {
			print qq(<td>$_</td>\n);
		}
		print qq(</tr>\n);
        }

	if ($htmlHeader) {
		print qq(</body>\n);
		print qq(</html>\n);
	}

}

sub printTex {
	my $out = shift;

	if ($latexHeader) {
		# Give users good starting point:
		print '\documentclass[12pt]{article}' . "\n";
		print '\usepackage{pslatex}' . "\n";
		print '\usepackage{geometry}' . "\n";
		print '\geometry{verbose,letterpaper,tmargin=1in,bmargin=1in,lmargin=1.5in,rmargin=1in}' . "\n";

		print '\begin{document}' . "\n";

		print '\title{}' . "\n";
		print '\date{}' . "\n";
		print '\author{}' . "\n";

		print '\maketitle' . "\n";
	}

	print '\begin{tabular}{';

	my $colcnt = &numOfCols ($out);
	
	if ($latexVerLines) {
		print "|";
		print "c|" x $colcnt;
	} else {
		print "c" x $colcnt;
	}
	print "}\n";

	my @rows = split /\n/, $out;
        for (@rows) {
                my @cols = split /\t/, $_;
		print "\\hline\n" if ($latexHorLines);

		my $last = pop @cols; # Last element is special.

                for (@cols) {
			# Latex does not like these chars: 
			s/&/\\&/g;
			s/%/\\%/g;
			s/\$/\\\$/g;

                        print "$_ & ";
                }

		print $last . " \\\\\n";
        }

	print "\\hline\n" if ($latexHorLines);

	print '\end{tabular}' . "\n";

	if ($latexHeader) {
		print '\end{document}' . "\n";
	}
		
}

sub numOfCols {
	my $out = shift;
	
	my @rows = split /\n/, $out;

	my $most = 0;
	for (@rows) {
		my @cols = split /\t/, $_;
		$most = int (@cols) if ($most < int (@cols));
	}

	return $most;
}

sub getMaxColWidth {
	my $out = shift;

	my @rows = split /\n/, $out;
	
	my $widest = 0;
	for (@rows) {
		my @cols = split /\t/, $_;

		for (@cols) {
			my @count = split //, $_;
			$widest = int (@count) if ($widest < int (@count));
		}
	}

	return $widest;
}

sub numOfRows {
	my $out = shift;

	my @rows = split /\n/, $out;

	return int (@rows);
}
