#!/usr/bin/perl -w
#
# $Id: density,v 1.2 2002/10/23 10:36:08 kjc Exp $
#
# usage:
#	density [-t '[type]'] file [file2...]
#
# a script to create a traffic density graph in the png format.
# this version works only with IPv4 addresses
# it assumes that the input data size is 720 points:
#	5 sec  x 720 =  1 hour
#	2 min  x 720 = 24 hours
#	1 hour x 720 = 30 days
#
use Socket;
use GD;

$pngfile = "density.png";
$type = "[dst address]";

$xsize = 720;
$ysize = 512;
$xoff = 50;
$yoff = 20;
$x = $xoff - 1;

# specified number of colors are assigned between $minval and $maxval
$numcolors = 64;
$maxval = 1.0;
$minval = 0.0001;
$minlog = log($minval) / log(10);
$maxlog = log($maxval) / log(10);

sub initcolors {
    my ($i, $v, $r, $g, $b);
    
    for ($i = 0; $i < $numcolors; $i++) {
	$v = $i / $numcolors;
	if ($v < 0.2) {
	    $r = 0.0;
	    $g = 0.0;
	    $b = 255.0 * $v/0.2;
	} elsif ($v < 0.4) {
	    $r = 0.0;
	    $g = 255.0 * ($v - 0.2)/0.2;
	    $b = 255.0;
	} elsif ($v < 0.6) {
	    $r = 0.0;
	    $g = 255.0;
	    $b = 255.0 - 255.0 * ($v - 0.4)/0.2;
	} elsif ($v < 0.8) {
	    $r = 255.0 * ($v - 0.6)/0.2;
	    $g = 255.0;
	    $b = 0.0;
	} else {
	    $r = 255.0;
	    $g = 255.0 - 255.0 * ($v - 0.8)/0.2;
	    $b = 0.0;
	}
	$colors[$i] = $im->colorResolve($r, $g, $b);
	if ($colors[$i] < 0) {
	    warn "can't alloc color\n";
	}
    }
}

# create a new image
$im = new GD::Image($xsize + 3 * $xoff, $ysize + 4 * $yoff);

# allocate some colors
$white = $im->colorAllocate(255,255,255);
$black = $im->colorAllocate(0,0,0);

initcolors();

# put background image
$im->filledRectangle(0, 0,
		     $xsize + 3 * $xoff - 1, $ysize + 4 * $yoff ,$white);
$im->filledRectangle($xoff, $yoff,
		     $xoff + $xsize, $yoff + $ysize, $black);
#
# this version works only with IPv4 addresses
#
sub addr2key {
    my $addr = shift;
    my $ip = inet_aton($addr);
    my ($a, $b, $c, $d) = unpack('C4', $ip);
    
    $key =  ($a << 24) + ($b << 16) + ($c << 8) + $d;
    return $key;
}

$addrdst = 0;
$start = ""; $start2 = ""; $end = ""; $end2 = "";

while ($file = shift @ARGV) {
    if ($file eq "-t") {
	$type = shift @ARGV;
	next;
    }

    open(FOO, "< $file");
    while(<FOO>) {
	if (/^\%!AGURI/) {
	    $x++;
	    if ($x > $xoff + $xsize) {
		warn "too many data input";
		last;
	    }
	    next;
	}
	if ($start eq "") {
	    if (/^\%\%StartTime:.*\((\d\d\d\d\/\d\d\/\d\d) (\d\d:\d\d:\d\d)\)/) {
		$start = $1; $start2 = $2;
	    }
	}
	if (/^\%\%EndTime:.*\((\d\d\d\d\/\d\d\/\d\d) (\d\d:\d\d:\d\d)\)/) {
	    $end = $1; $end2 = $2;
	}
	if (/^[\%\n]/) {
	    next;
	}
	if (/^(\[.*\])/) {
	    if ($1 eq $type) {
		$addrdst = 1;
	    } else {
		$addrdst = 0;
	    }
	    next;
	}
	next if ($addrdst == 0);

	if (/\s*(\S+)\s+\d+\s+\(.*?(\d+\.\d+)\%\)/) {
	    $addr = $1;
	    $cnt = $2 / 100.0;
	    if ($addr =~ /(.*)\/(\d+)/) {
		$addr = $1;
		$pfxlen = $2;
	    } else {
		$pfxlen = 32;
	    }

	    $key = addr2key($addr);
	    $y2 = $yoff + $ysize - $ysize * $key / 2 ** 32;
	    $y1 = $y2 - $ysize / 2 ** $pfxlen;

	    #
	    # compute density index:
	    #      density = cnt / size
	    #      dindex  = cnt / sqrt(size)
	    #
	    $n = (32.0 - $pfxlen) / 2.0;
	    $didx = $cnt / 2**$n;

	    $didx = log($didx) / log(10);
	    if ($didx >= $maxlog) {
		$i = $numcolors - 1;
	    } elsif ($didx <= $minlog) {
		$i = 0;
	    } else {
		$i = ($didx - $minlog) / ($maxlog - $minlog) * $numcolors;
	    }
	    $color = $colors[$i];
	    
	    if ($color != -1 && $color != 0) {
		$im->line($x, $y1, $x, $y2, $color);
	    }
	}
    }
    close(FOO);
}

#
# put labels (XXX: positions are hard coded)
#
$im->string(gdMediumBoldFont, $xoff + $xsize/2, $yoff*3/2 + $ysize,
	    "time", $black);
$im->stringUp(gdMediumBoldFont, $xoff/2, $yoff + $ysize/2,
	    "address space", $black);
$im->string(gdSmallFont, $xoff, $yoff + $ysize + 8, $start2, $black);
$im->string(gdSmallFont, $xoff, $yoff + $ysize + 24, $start, $black);
$im->string(gdSmallFont, $x - 36, $yoff + $ysize + 8, $end2, $black);
$im->string(gdSmallFont, $x - 36, $yoff + $ysize + 24, $end, $black);
$im->string(gdSmallFont, 16, $yoff, "2**32", $black);
$im->string(gdSmallFont, 16, $yoff + $ysize - 10, "2**0", $black);

$x = $xoff + $xsize + 10;
$y1 = $yoff + 20;
$didx = 1.0;
$label = 1.0;
while ($didx >= $minval) {
    $idx = log($didx) / log(10);
    if ($idx >= $maxlog) {
	$i = $numcolors - 1;
    } elsif ($idx <= $minlog) {
	$i = 0;
    } else {
	$i = ($idx - $minlog) / ($maxlog - $minlog) * $numcolors;
    }

    $y2 = $y1 + 4;
    $im->filledRectangle($x, $y1, $x + 10, $y2, $colors[$i]);
    if ($didx <= $label) {
	$im->line($x + 10 - 2, $y1, $x + 10 + 2, $y1, $black);
	$im->string(gdSmallFont, $x + 16, $y1 - 6, $label, $black);
	$label = $label * 0.1;
    }
    $y1 = $y2;
    $didx = $didx * 0.9;
}
$im->rectangle($x, $yoff + 20, $x + 10, $y2, $black);

open(FOO, "> $pngfile");

# Convert the image to PNG and print it on standard output
print FOO $im->png;

close(FOO);

exit 0;
