#!/s/std/bin/perl -w
#!/usr/bin/perl -w
use Tk;

# -----------------------------------------------------------------------------
# Variable declaration.....

# the current running state of the logs
my %statedb;

# associates subnet with symbolic name
my %subnetnamedb;

# the first key is a symbolic name, the second key is a subnet.
# there is also the 'numcpus' key in addition to subnet.
my %symbol;
my %numcpus;

# the subroutine that builds the currently running state of the log file.
sub BuildLogState;

# associate subnets with symbolic names and build some of the symbol table.
sub BuildColorAndSymbolState;

# this subroutine takes the statedb, the subnetnamedb, and the symboldb
# and produces a symboldb that contains the color and number of machine
# for each symbol
sub JoinEverything;

# This function creates a correct symbol hash table with everything in it
# we need to draw it.
sub Update;

# set up all of the nasty canvas stuff.
sub SetupCanvas;

sub CleanNumCpus;

# turn on color box drawing.
my $noboxes;

# turn this on for verbose crap.
my $debug;

# race condition shit... maybe I have one....
my $global;

# variables that control canvas generation
# color legend box dimesnsions and spacing
my ($wbox, $hbox, $xspcbox); # pixel dimensions
# vertical spacing if needed, in pixels
my $vspc;
# bar height
my $hbar;
# maximum length of a status bar in pixels
my $maxwbar;
# the length of each bar in the display
my @wbar;
# each color of the bar in question
my @cbar;
# total height of a color/bar combo
my $hcombo;
# left justification of the entire thing
my $leftjust;
# height adjustment justification
my $topjust;
# height of the font used.
my $fontheight;
# the totals that each status bar combo needs
my @totals;
# The canvas I know and care about
my $c;
# the update time for re-reading the config file, in seconds
my $updatetime;

# -----------------------------------------------------------------------------
# Code starts here....

# set up defaults for the canvas generator
$wbox = 13;
$hbox = 13;
$xspcbox = 4;
$vspc = 4;
$hbar = 20;
$maxwbar = 300;
$hcombo = $hbox + $vspc + $hbar + $vspc;
$leftjust = 30;
$topjust = 5;

# this is how fast the timer updates, in seconds.
$updatetime = 2;

# XXX this is a guessed number
$fontheight = 13;

# name of the project in question
$projname = shift || die "Please supply a project name";

# turn this on and off
$debug = 0;

# don't mess with this.
$global = 0;

# turn off drawing of color boxes.
$boxes = 0;

# get a snapshot of the log state with a completed symbol hash table.
Update;

# start the TK madness.
$mw = MainWindow->new;
$mw->title("Legend--Total Jobs Running: $statedb{'numrunning'}");
$mw->geometry("+0+640");
SetupCanvas;

# every n seconds
$c->repeat(1000*$updatetime, \&UpdateCanvas);

MainLoop;
exit 0;

# -----------------------------------------------------------------------------
# Subroutines.....

sub BuildLogState
{
	my @lines = ();
	my $line;
	my ($event, $procid, $ip);
	my $waiting;

	%statedb = ();
	$statedb{'numrunning'} = 0;

	BADFORM1: sleep 2 && goto BADFORM1 if (! -f "$projname.log");

	@lines = `egrep '^[0-9][0-9][0-9].*\$' $projname.log | egrep -v '^000.*\$'`;

	# parse an event, and record the state changes for jobs
	foreach $line (@lines)
	{
		chomp $line;
		
		# I wish perl had a cool switch statement
		if ($line =~ m/^001.*$/)
		{
			print "Executing Event...\n" if $debug;
			($event, $procid, $ip) = 
				$line =~ /^([0-9][0-9][0-9]).*(\(.*\..*\..*\)).*<(.*):.*$/;
			chomp $event;
			chomp $procid;
			chomp $ip;

			if (exists($statedb{$procid}))
			{
				print "What! How did I execute something twice in a row?\n";
				print "$event $procid $ip\n";
			}
			else
			{
				$statedb{'numrunning'}++
			}
			# If I somehow executed twice, give it the current ip address...
			print "$procid = $ip\n" if $debug;
			$statedb{$procid} = $ip;
		}
		elsif ($line =~ m/^005.*$/)
		{
			print "Termination event...\n" if $debug;
			($event, $procid, $ip) = 
				$line =~ /^([0-9][0-9][0-9]).*(\(.*\..*\..*\)).*$/;
			chomp $event;
			chomp $procid;

			if (!exists($statedb{$procid}))
			{
				print "What! How did I terminate something that isn't running!?!\n";
				print "$event $procid\n";
			}
			else
			{
				delete $statedb{$procid};
				$statedb{'numrunning'}--;
			}
		}
		elsif ($line =~ m/^004.*$/)
		{
			print "Eviction event...\n" if $debug;
			($event, $procid, $ip) = 
				$line =~ /^([0-9][0-9][0-9]).*(\(.*\..*\..*\)).*$/;
			chomp $event;
			chomp $procid;
			if (!exists($statedb{$procid}))
			{
				print "What! How did I evict something that isn't running!?!\n";
				print "$event $procid\n";
			}
			else
			{
				delete $statedb{$procid};
				$statedb{'numrunning'}--;
			}
		}
		elsif ($line =~ m/^009.*/)
		{
			print "Abort event...\n" if $debug;
			($event, $procid, $ip) = 
				$line =~ /^([0-9][0-9][0-9]).*(\(.*\..*\..*\)).*$/;
			chomp $event;
			chomp $procid;
			if (!exists($statedb{$procid}))
			{
				# I could have checkpointed, and then aborted...
				#print "What! How did I abort something that isn't running!?!\n";
				#print "$event $procid\n";
			}
			else
			{
				delete $statedb{$procid};
				$statedb{'numrunning'}--;
			}
		}
	}
}

sub BuildColorAndSymbolState
{
	my @lines;
	my $line;
	my ($name, $subnet, $color, $red, $green, $blue);
	my @thingy;

	%symbol = ();
	%subnetnamedb = ();

	BADFORM2: sleep 2 && goto BADFORM2 if (! -f "$projname.colors");
	@lines = `cat $projname.colors`;

	foreach $line (@lines)
	{
		chomp $line;
		($name, $subnet, $color) = split /\t/, $line;
		($red, $green, $blue) = split /\s/, $color;
		# if there is a last period, remove it.
		$subnet =~ s/(^.*)\.$/$1/;
		print "$subnet\n" if $debug;
		$subnetnamedb{$subnet} = "$name";
		$red = sprintf "%02lX", $red;
		$green = sprintf "%02lX", $green;
		$blue = sprintf "%02lX", $blue;
		$symbol{$name}{$subnet} = "#$red$green$blue";
		$numcpus{$name} = 0;
	}
}

sub CleanNumCpus
{
	my $name;
	foreach $name (keys %numcpus)
	{
		$numcpus{$name} = 0;
	}
}

sub JoinEverything
{
	my $job;
	my ($ip4, $ip3, $ip2, $ip1);
	my $tmp;
	
	print "There are $statedb{'numrunning'} jobs running.\n" if $debug;
	foreach $job (keys %statedb)
	{
		next if ($job =~ /numrunning/);
		print "$job is executing on $statedb{$job}\n" if $debug;

		$tmp = $statedb{$job};

		# construct all of the prefixes of the ip addresses I care about.
		$ip4 = $tmp;
		($ip3)=$tmp =~ /(\d{1,3}\.\d{1,3}\.\d{1,3})\./;
		($ip2)=$tmp =~ /(\d{1,3}\.\d{1,3})/;
		($ip1)=$tmp =~ /(\d{1,3})/;

		print "$ip4 | $ip3 | $ip2 | $ip1\n" if $debug;

		# preform a prefix match until I get one that works, if not, assign 
		# the machine to the other group.
		if (exists($subnetnamedb{$ip4}))
		{
			print "Matched ip4\n" if $debug;
			$numcpus{$subnetnamedb{$ip4}}++;
		}
		elsif (exists($subnetnamedb{$ip3}))
		{
			print "Matched ip3\n" if $debug;
			$numcpus{$subnetnamedb{$ip3}}++;
		}
		elsif (exists($subnetnamedb{$ip2}))
		{
			print "Matched ip2\n" if $debug;
			$numcpus{$subnetnamedb{$ip2}}++;
		}
		elsif (exists($subnetnamedb{$ip1}))
		{
			print "Matched ip1\n" if $debug;
			$numcpus{$subnetnamedb{$ip1}}++;
		}
		else
		{
			$tmp = $subnetnamedb{'other'};
			print "Matched other\n" if $debug;
			$numcpus{$tmp}++;
		}
	}
}

# read the log file, and produce the symbol table with colors and numbers as the
# output.
sub Update
{
	BuildColorAndSymbolState;
	BuildLogState;
	JoinEverything;
}

# this will draw everything once onto the canvas.
sub SetupCanvas
{
	my ($maxheight, $maxwidth);
	my ($x, $y, $box, $count);
	my ($totalcombosdrawn);
	my $tmp;

	$totalcombosdrawn = 0;

	$maxwidth = $maxwbar;
	$maxheight = ((scalar(keys %symbol) * $hcombo) + 2*$topjust);
	print "Canvas Size: $maxheight $maxwidth\n" if $debug;

	$c = $mw->Canvas(-width => $maxwidth, -height => $maxheight)->pack;

	# for each symbolic name, draw a combo
	foreach $name (sort keys %symbol)
	{
		$count = 0;
		$tmp = "nocolor";
		print "Computing combo for $name\n" if $debug;
		# create all of the little boxes that represent subnets
		foreach $subnet (keys %{$symbol{$name}})
		{
			next if ($subnet =~ /numcpus/);

			# if I see the same color, skip it
			next if ($symbol{$name}{$subnet} =~ /$tmp/);
			if ($boxes == 0)
			{
				$tmp = $symbol{$name}{$subnet};
				next;
			}
			print "\t$subnet: color = $symbol{$name}{$subnet}\n" if $debug;

			$x = $leftjust + ($count * ($wbox + $xspcbox));
			$y = $topjust + ($totalcombosdrawn * $hcombo);
			$c->createRectangle($x, $y, $x+$wbox, $y+$hbox, 
				-fill=> $symbol{$name}{$subnet});
			
			$count++;
			$tmp = $symbol{$name}{$subnet};
		}
		# create the text at the end of the boxes.
		$x = $leftjust + ($count * ($wbox + $xspcbox));
		$y = $topjust + ($totalcombosdrawn * $hcombo) + ($hbox-$fontheight);
		$c->createText($x, $y, -text => $name, -anchor => 'nw');

		# create the total number of jobs to the left of the bars.
		$x = $leftjust/2;
		$y = $topjust+($hbox + $vspc) + (($hbox + $vspc) * $totalcombosdrawn) +
			($totalcombosdrawn * ($hbar + $vspc)) + ($hbar/2);
		$totals[$totalcombosdrawn] = 
			$c->createText($x, $y, -text => $numcpus{$name}, 
			-anchor => 'c');

		# create the bar
		$x = $leftjust;
		$y = $topjust+($hbox + $vspc) + (($hbox + $vspc) * $totalcombosdrawn) +
			($totalcombosdrawn * ($hbar + $vspc));
		# assign the last color I used above to the bar.
		$cbar[$totalcombosdrawn] = $tmp;
		if ($numcpus{$name} != 0)
		{
			$wbar[$totalcombosdrawn] = 
				$c->createRectangle($x, $y, 
					$x + (($numcpus{$name}/$statedb{'numrunning'}) 
					* ($maxwbar-$leftjust)), 
					$y+$hbar, -fill => $cbar[$totalcombosdrawn]);
		}
		else
		{
			$wbar[$totalcombosdrawn] = 
				$c->createRectangle($x, $y, $x, $y+$hbar, 
					-fill => $cbar[$totalcombosdrawn]);
		}

		$totalcombosdrawn++;
	}
}

# this will update the totals and bar size according to what is in the log.
sub UpdateCanvas
{
	my $name;
	my ($x, $y);
	my ($totalcombosdrawn);
	
	$totalcombosdrawn = 0;

	if ($global == 1) { return; }
	$global = 1;

	print "Updating...\n" if $debug;

	#Update;
	CleanNumCpus;
	BuildLogState;
	JoinEverything;


	foreach $name (sort keys %symbol)
	{
		# create the total number of jobs to the left of the bars.
		$x = $leftjust/2;
		$y = $topjust+($hbox + $vspc) + (($hbox + $vspc) * $totalcombosdrawn) +
			($totalcombosdrawn * ($hbar + $vspc)) + ($hbar/2);
		$c->delete($totals[$totalcombosdrawn]);
		$totals[$totalcombosdrawn] = 
			$c->createText($x, $y, -text => $numcpus{$name}, 
			-anchor => 'c');

		# create the bar
		$x = $leftjust;
		$y = $topjust+($hbox + $vspc) + (($hbox + $vspc) * $totalcombosdrawn) +
			($totalcombosdrawn * ($hbar + $vspc));
		$c->delete($wbar[$totalcombosdrawn]);
		if ($numcpus{$name} != 0)
		{
			$wbar[$totalcombosdrawn] = 
				$c->createRectangle($x, $y, 
					$x + (($numcpus{$name}/$statedb{'numrunning'}) 
					* ($maxwbar-$leftjust)), 
					$y+$hbar, -fill => $cbar[$totalcombosdrawn]);
		}
		else
		{
			$wbar[$totalcombosdrawn] = 
				$c->createRectangle($x, $y, $x, $y+$hbar, 	
					-fill => $cbar[$totalcombosdrawn]);
		}
		$totalcombosdrawn++;
	}

	$mw->title("Legend--Total Jobs Running: $statedb{'numrunning'}");
	$global = 0;
}



