#!/usr/bin/perl -w
use strict;
use CGI;

my $width=865; # 1 + 86400/100
my $height=201; # 1 + 100*2;
my $maxms=20; # +/- 20ms
my $timewidth=86400; # last 24 hours

my $cgi=new CGI;
$width=$cgi->param("width") if($cgi->param("width") and $cgi->param("width") =~ /^[1-9]\d*$/s);
$height=$cgi->param("height") if($cgi->param("height") and $cgi->param("height") =~ /^[1-9]\d*$/s);
if($cgi->param("maxms") and $cgi->param("maxms") =~ /^([1-9]\d*(?:\.\d+)?)(s?)$/s) {
	$maxms=$1*($2?1000:1);
}
if($cgi->param("timewidth") and $cgi->param("timewidth") =~ /^([1-9]\d*(?:\.\d+)?)([dh]?)$/s) {
	if("d" eq $2) {
		$timewidth=$1*86400;
	} elsif("h" eq $2) {
		$timewidth=$1*3600;
	} else {
		$timewidth=$1;
	}
}

my $mintime=0;
my $maxtime=0;
my %offsets=();

sub parsefile($$) {
	my ($file,$timeoffset)=@_;
	my %myoffsets=();
	my ($line,@line);

	open(LOG,"<",$file) or die "open($file): $!";
	while(defined($line=<LOG>)) {
		chomp $line;
		@line=split / /,$line;
		die $line unless(8==@line);
# day timeofday ip status offset delay dispersion jitter
		my $time=$line[1]+$timeoffset;
		my $ip=$line[2];
		$maxtime=$time if($maxtime<$time);
		$mintime=$time if($time<$mintime);
		my $ms=$line[4]*1000;
		$myoffsets{$ip}=[] unless($myoffsets{$ip});
		push @{$myoffsets{$ip}},[$time,$ms];
	}
	close(LOG) or die "close: $!";
	foreach my $ip (keys %myoffsets) {
		if($offsets{$ip}) {
			unshift @{$offsets{$ip}},@{$myoffsets{$ip}};
		} else {
			$offsets{$ip}=$myoffsets{$ip};
		}
	}
}

my @files=sort {$b cmp $a} glob "/var/log/ntpstats/peerstats.*";
unless(@files) {
	print STDERR "unable to find peerstats log files\n";
	print "Content-type: text/plain\n\n";
	exit 0;
}
for(my $i=0;$i<@files and $maxtime-$mintime<$timewidth;$i++) {
	parsefile($files[$i],$i*-86400);
}

my $numhosts=0;
foreach my $ip (keys %offsets) {
	my $lastsample=$#{$offsets{$ip}};
	if($lastsample<1) {
		delete $offsets{$ip};
		next;
	}
	my $time=$offsets{$ip}[$lastsample][0];
	$time-=$maxtime-$timewidth;
	if($time<0) {
		delete $offsets{$ip};
		next;
	}
	$numhosts++;
}

my $xscale=($width-1)/$timewidth;
my $yscale=($height-1)/(2*$maxms);
my $width_minus_1=$width-1;
my $height_minus_1=$height-1;
my $y_is_0=$height/2;

my $textheight=12;
my $texty=5+$textheight;
my $textgap=3;
if($height<5+$textheight*$numhosts+$textgap*($numhosts-1)+5) {
	$textheight=($height-2)/$numhosts;
	$texty=$textheight;
	$textgap=0;
}
my @colors=(
	"red",
	"green",
	"blue",
	"magenta",
	"cyan",
	"yellow",
	"grey",
);

print <<"EOD";
Content-type: image/svg+xml

<svg xmlns="http://www.w3.org/2000/svg" width="$width" height="$height">
<g transform="translate(.5,$y_is_0)">
EOD
foreach my $ms (-1000,-100,-10,0,10,100,1000) {
	my $y=$ms*$yscale;
	next if($y<-$height_minus_1/2 or $height_minus_1/2<$y);
	my $color=$ms?"lightgrey":"black";
	print "<line x1=\"0\" y1=\"$y\" x2=\"$width_minus_1\" y2=\"$y\" stroke=\"$color\" stroke-width=\"1\" />\n";
}
print "</g>\n";

my $hostlist="";
foreach my $ip (sort keys %offsets) {
	my $path="";
	for(my $i=0;$i<@{$offsets{$ip}};$i++) {
		my ($time,$offset)=@{$offsets{$ip}[$i]};
		$time-=$maxtime-$timewidth;
		$time*=$xscale;
		$offset*=$yscale;
		$path="" if($time<0); # Keep only the first negative-time value
		$time=sprintf "%.3f",$time;
		$time =~ s/\.?0+$//s;
		$path.=" $time,$offset";
	}
	next unless($path =~ s/^ (\S+) /M $1 L /s);
	$hostlist.="<text x=\"5\" y=\"$texty\" font-size=\"$textheight\" stroke=\"white\" stroke-width=\"$textgap\" fill=\"none\">$ip</text>\n";
	$hostlist.="<text x=\"5\" y=\"$texty\" font-size=\"$textheight\" stroke=\"none\" fill=\"$colors[0]\">$ip</text>\n";
	print "<path stroke=\"$colors[0]\" d=\"$path\" fill=\"none\" stroke-width=\"1\" transform=\"translate(.5,$y_is_0),scale(1,-1)\" />\n";
	$texty+=$textheight+$textgap;
	shift @colors if(1<@colors);
}

print $hostlist,<<"EOD";
<rect x1="0" y1="0" width="$width_minus_1" height="$height_minus_1" stroke="black" stroke-width="1" fill="none" transform="translate(.5,.5)" />
</svg>
EOD
0;
