#!/usr/bin/perl -Tw
# 
#   $Id: trafgraph,v 1.8 2005/04/29 17:57:39 dgregor Exp $
#
# Copyright (c) 1998 Daniel J. Gregor, Jr., All rights reserved.
# 
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions
# are met:
# 1. Redistributions of source code must retain the above copyright
#    notice, this list of conditions and the following disclaimer.
# 2. Redistributions in binary form must reproduce the above copyright
#    notice, this list of conditions and the following disclaimer in the
#    documentation and/or other materials provided with the distribution.
# 3. All advertising materials mentioning features or use of this software
#    must display the following acknowledgement:
# 	This product includes software developed by Daniel J. Gregor, Jr.
# 4. The name of Daniel J. Gregor, Jr. may not be used to endorse or promote
#    products derived from this software without specific prior written
#    permission.
# 
# THIS SOFTWARE IS PROVIDED BY DANIEL J. GREGOR, JR. ``AS IS'' AND
# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
# ARE DISCLAIMED.  IN NO EVENT SHALL DANIEL J. GREGOR, JR. BE LIABLE
# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
# SUCH DAMAGE.

use Getopt::Std;
require 'timelocal.pl';

$print_range = 1;
$default_term = "png small color";
$default_plotstyle = "lines";

$usage = <<EOF;
Usage: trafgraph.pl [-h] [-m] [-e] [-b <seconds>] [-T <gnuplot terminal type>]
                    [-t <title>] [-x <xaxislabel>] [-y <yaxislabel>] 
                    [-s <plot style>] [-a <seconds>]
                    [<data file 1> <comment 1>] [<data file 2> <comment 2>] ...
EOF

$detailedusage = <<EOF;
    -h        This help message.
    -m        Read hours and minutes in input file and on graph print hours
                     and minutes in tick labels.
    -b        Break graph if we are missing input values for this number of
                     seconds.  Mutually exclusive with -B.
    -B        Drag the graph down to the X axis if we are missing input
                     values for this number of seconds.  Mutually exclusive
                     with -b.
    -e        Input times are in seconds since epoch.
    -t        Graph title.
    -T        GNUplot terminal type.  Defaults to "$default_term".
    -a        Aggregate data over <seconds> interval
    -s        Plot style, defaults to "$default_plotstyle".
    -x,-y     X (or Y) axis comment.
EOF

Getopt::Std::getopts('a:t:x:y:b:B:T:s:hme', \%opts) || die $usage;

if (defined($opts{'h'})) {
	print STDERR $usage;
	print STDERR $detailedusage;
	exit 1;
}

if (defined($opts{'b'}) && defined($opts{'B'})) {
	print STDERR "-b and -B are mutually exclusive\n";
	print STDERR $usage;
	exit 1;
}

if ((@ARGV % 2) != 0) {
	die "$usage";
}

$graphtitle = $opts{'t'};
$xaxislabel = $opts{'x'};
$yaxislabel = $opts{'y'};
$aggregate = $opts{'a'};

$termtype = defined($opts{'T'}) ? $opts{'T'} : $default_term;
$plotstyle = defined($opts{'s'}) ? $opts{'s'} : $default_plotstyle;

sub readdata {
	local($fh) = shift(@_);
	local($linetitle);
	local($lasttime);
	local($aggregateval);

	if (defined(@_)) {
		$linetitle = shift(@_);
	}
	local(%plot);

	while (<$fh>) {
		chomp();

		if (m/^\s*$/) {
			defined($time = ${$plot{'datetimes'}}[$#{$plot{'datetimes'}}])
				|| next;
			$val = undef;
		} elsif (defined($opts{'e'})) {
			m/^\s*(\d+)\s+([0-9.]+)\s*$/ || die "syntax error: \"$_\"";
			$time = $1;
			$val = $2;
		} elsif (defined($opts{'m'})) {
			m/^\s*(\d{4})(\d{2})(\d{2})(\d{2})(\d{2})\s+([0-9.]+)\s*$/
				|| die "syntax error: \"$_\"";
	    	$time = &timelocal(0, $5, $4, $3, $2 - 1, $1 - 1900);
			$val = $6;
		} else {
			m/^\s*(\d{4})(\d{2})(\d{2})\s+([0-9.]+)\s*$/ ||
				die "syntax error: \"$_\"";
	    	$time = &timelocal(0, 0, 0, $3, $2 - 1, $1 - 1900);
			$val = $4;
		}

		if (defined($aggregate)) {
		  if (defined($lasttime) &&
			   int($time / $aggregate) == $lasttime) {
		    $aggregateval += $val;
		  } else {
		    if (defined($lasttime)) {
		      $plottime = $lasttime * $aggregate;
		      $plot{'vals'}{$plottime} = $aggregateval;
		      push(@{$plot{'datetimes'}}, $plottime);
		    }

		    $lasttime = int($time / $aggregate);
		    $aggregateval = $val;
		  }
		} else {
		  $plot{'vals'}{$time} = $val;
		  push(@{$plot{'datetimes'}}, $time);
		}
	}

	if (defined($aggregate) && defined($lasttime)) {
	  $plottime = $lasttime * $aggregate;
	  $plot{'vals'}{$plottime} = $aggregateval;
	  push(@{$plot{'datetimes'}}, $plottime);
	}

	$plot{'plotcommand'} =  "'-' using 1:2 ";
	$plot{'plotcommand'} .= " title \"$linetitle\"" if defined($linetitle);
	$plot{'plotcommand'} .=  " with $plotstyle";

	return \%plot;
}

sub initgraph {

	print "set term $termtype\n\n";

	print "set xlabel \"$xaxislabel\"\n" if defined($xaxislabel);
	print "set ylabel \"$yaxislabel\"\n" if defined($yaxislabel);
	print "\n";

	print "set title \"$graphtitle\"\n" if defined($graphtitle);

	print "set xdata time\n\n";

	print "set timefmt \"%Y%m%d%H%M%S\"\n\n";
	
	if (defined($opts{'m'})) {
		print 'set format x "%m/%d/%y\n%H:%M"', "\n";
	} else {
		print 'set format x "%m/%d/%y"', "\n";
	}

}

if (@ARGV) {
	while (@ARGV) {
		local($datafile) = shift(@ARGV);
		local($comment) = shift(@ARGV);

		if ($datafile eq "-") {	# read from STDIN
			push(@plots, readdata(\*STDIN, $comment));
		} else {
			open(PLOTDATA, "<" . $datafile) ||
				die "Could not open $datafile";
			push(@plots, readdata(\*PLOTDATA, $comment));
		}
	}
} else {
	push(@plots, readdata(\*STDIN));
}

initgraph();
foreach $plot (@plots) {
	push(@plotcommands, $$plot{'plotcommand'});
	push(@datetimes, @{$$plot{'datetimes'}});
}

@datetimes = sort( {$a <=> $b} @datetimes);

@t = localtime($datetimes[0]);
$begindatetime = sprintf("%04d%02d%02d%02d%02d%02d",
		$t[5] + 1900, $t[4] + 1, $t[3], $t[2], $t[1], $t[0]);

@t = localtime($datetimes[$#datetimes]);
$enddatetime = sprintf("%04d%02d%02d%02d%02d%02d",
		$t[5] + 1900, $t[4] + 1, $t[3], $t[2], $t[1], $t[0]);

print "set xrange [\"$begindatetime\":\"$enddatetime\"]\n" if $print_range;

print "plot " . join(", \\\n", @plotcommands). "\n";

foreach $plot (@plots) {
	foreach $i (@{$$plot{'datetimes'}}) {
		if (!defined($$plot{'vals'}{$i})) {
			print "\n";
		} else {
			if (defined($opts{'b'}) && defined($lasttime) &&
					$i > ($lasttime + $opts{'b'})) {
				print "\n";
			}
			if (defined($opts{'B'}) && defined($lasttime) &&
					$i > ($lasttime + $opts{'B'})) {
				@l = localtime($lasttime + $opts{'B'});
				@c = localtime($i - $opts{'B'});
				printf "%04d%02d%02d%02d%02d%02d %f\n",
					$l[5] + 1900, $l[4] + 1, $l[3], $l[2],
					$l[1], $l[0],
					0;
				printf "%04d%02d%02d%02d%02d%02d %f\n",
					$c[5] + 1900, $c[4] + 1, $c[3], $c[2],
					$c[1], $c[0],
					0;
			}
				
			@t = localtime($i);
			printf "%04d%02d%02d%02d%02d%02d %f\n",
				$t[5] + 1900, $t[4] + 1, $t[3], $t[2],
				$t[1], $t[0],
				$$plot{'vals'}{$i};
		}
		$lasttime = $i;
	}
	print "e\n";
	undef($lasttime);
}

