#!/usr/bin/perl

# © 2006 Joachim Breitner
#
# gaimgraph.pl
#
# Creates interesting graphs from your gaim history

use strict;
use GD::Graph::lines;
use GD::Graph::area;
use GD::Graph::points;
use GD::Text;
use Data::Dumper;
use Date::Manip;
use XML::XPath;

######## Edit these items ########

#This is the path to your ClamAV log file
my $gaimlogs = "$ENV{HOME}/.gaim/logs";
my $buddyfile = "$ENV{HOME}/.gaim/blist.xml";


#This is that path you want for the output PNG pie chart
#Probably this will be somewhere in your web server's directory
my $imagefile="gaimgraph-%s.png";



#### No user serviceable parts below this line ####
my @months = (
	'Jan',
	'Feb',
	'Mar',
	'Apr',
	'May',
	'Jun',
	'Jul',
	'Aug',
	'Sep',
	'Oct',
	'Nov',
	'Dec',
);
my %months = (
	'Jan' => '01',
	'Feb' => '02',
	'Mar' => '03',
	'Apr' => '04',
	'May' => '05',
	'Jun' => '06',
	'Jul' => '07',
	'Aug' => '08',
	'Sep' => '09',
	'Oct' => '10',
	'Nov' => '11',
	'Dec' => '12'
);

# Reading input



print "Parsing buddy list...\n";
my %whomap;
my $xp = XML::XPath->new(filename => $buddyfile);
my @contacts = $xp->findnodes('//contact');

for my $contact (@contacts) {
	my $alias;
	$alias   = $xp->find('./@alias', $contact)->string_value;
	$alias ||= $xp->find('./buddy/alias', $contact)->string_value;
	$alias ||= $xp->find('./buddy/name', $contact)->string_value;
	$whomap{$_->string_value} = $alias for $xp->findnodes('./buddy/name', $contact);
}

print "Parsing history file sizes...\n";
my %sums;
my %days;

open INPUT, "-|", ("find", $gaimlogs, qw'-mindepth 2 -type f -printf %p_%s\\n');
while (<INPUT>) {
	m#([^/]+)/(\d\d\d\d-\d\d-\d\d)\.\d\d\d\d\d\d(?:\+\d\d\d\d\w+)?.txt_(\d+)$# or do {print "Could not parse line $_"; next};
	my ($who,$date,$size) = ($1,$2,$3,$4);
	next unless exists $whomap{$who};
	$sums{$whomap{$who}} += $size;
	$days{$date}{$whomap{$who}} += $size;
}
close INPUT;

print "Getting top 10...\n";
my @top10 = (sort {$sums{$b} <=> $sums{$a}} keys %sums)[0..9];
print "Top 10: ". join(', ',@top10) ."\n";

my @legend = @top10;

my @graphdataAbs =();
my @graphdataDay =();
my @graphdataWeek =();
my @graphdataMonth =();
my %sofar;

#localtime =~ /^\w\w\w (\w\w\w) +(\d\d?) \d\d:\d\d\:\d\d (\d\d\d\d)/;
#my $today = sprintf("%04i-%02i-%02i",$3,$months{$1},$2);;
my $today = ParseDate("today");

print "Finding first day...\n";
my @seenkeys = sort keys %days;
print "First day: $seenkeys[0]\n";

print "Finding all days...\n";
my @keys = ();
for (my $day = ParseDate($seenkeys[0]); Date_Cmp($day,$today) <= 0; $day = DateCalc($day,"+ 1 day")) {
	push @keys, UnixDate($day,"%Y-%m-%d")
}

print "Calculating graph data..\n";
for my $n (0..@keys) {
	my $key = @keys[$n];
#	next if $key eq $today;
	my $i=0;
	my ($year,$mon,$day) = ($key =~ /^(\d\d\d\d)-(\d\d)-(\d\d)/);
	my $date;
	if ($mon == 1 && $day == 1) {
		$date = "$year:";
	} elsif ($day == 1) {
		$date = $months[$mon-1];
	} else {
		$date = "";
	}
	push @{$graphdataAbs[$i]}, $date;
	push @{$graphdataDay[$i]}, $date;
	push @{$graphdataWeek[$i]}, $date;
	push @{$graphdataMonth[$i]}, $date;
	$i++;
	for my $who (@top10) {
		$sofar{$who} += $days{$key}{$who}||0;

		my $week = 0;
		my $weight = 0;
		for (-3..+3) {
			next if $n+$_ <  0;
			next if $n+$_ >= @keys;
			$week += ((4-abs($_)) * $days{@keys[$n+$_]}{$who}) || 0;
			$weight += 4-abs($_);
		}
		$week /= $weight;
		
		my $month = 0;
		$weight = 0;
		for (-15..+15) {
			next if $n+$_ <  0;
			next if $n+$_ >= @keys;
			$month += ((16-abs($_)) * $days{@keys[$n+$_]}{$who}) || 0;
			$weight += 16-abs($_);
		}
		$month /= $weight;
		
		push @{$graphdataAbs[$i]}, $sofar{$who};
		push @{$graphdataDay[$i]}, $days{$key}{$who} || 0;
		push @{$graphdataWeek[$i]}, $week;
		push @{$graphdataMonth[$i]}, $month;
		$i++;
	}
}

print "Greating Graphs..\n";
my @opts = (
	 x_ticks        => 0,
	 y_label	=> 'Gechattete Bytes',
	 x_labels_vertical => 0,
#	 x_label_skip 	=> 30,
#	 x_tick_offset => 30-((@keys) % 30),
	 correct_width	=> 1,
	 box_axis	=> 0,
	 y_tick_number	=> 'auto',
	 y_long_ticks	=> 1,
#	 x_tick_length  => 4,
#	 line_width     => '2',
	 transparent	=> 0,
	 r_margin        => 40,
);

sub settext($) {
	my $graph = shift;
	$graph->set_legend_font(GD::Font->Large);
	$graph->set_y_label_font(GD::Font->Large);
	$graph->set_y_axis_font(GD::Font->Large);
	$graph->set_x_axis_font(GD::Font->Large);
	$graph->set_legend(@legend) ;
}

my $graphwidth = 2*@keys + 150;

my $graph = GD::Graph::lines->new($graphwidth, 1000);

$graph->set(
	 title          => 'Chat-Graph (absolut)',
	 @opts
 ) or die $graph->error;

settext($graph);

my $gd = $graph->plot(\@graphdataAbs) or die $graph->error;

my $outfile = sprintf($imagefile,'absolute');
open(IMG, ">$outfile") or die $!;
binmode IMG;
print IMG $gd->png;
close IMG;

my $graph = GD::Graph::points->new($graphwidth, 1000);

$graph->set(
	 title          => 'Chat-Graph (daily)',
	 @opts
 ) or die $graph->error;

settext($graph);

my $gd = $graph->plot(\@graphdataDay) or die $graph->error;
my $outfile = sprintf($imagefile,'day');
open(IMG, ">$outfile") or die $!;
binmode IMG;
print IMG $gd->png;
close IMG;


my $graph = GD::Graph::lines->new($graphwidth, 1000);

$graph->set(
	 title          => 'Chat-Graph (weekly)',
	 @opts
 ) or die $graph->error;

settext($graph);

my $gd = $graph->plot(\@graphdataWeek) or die $graph->error;

my $outfile = sprintf($imagefile,'week');
open(IMG, ">$outfile") or die $!;
binmode IMG;
print IMG $gd->png;
close IMG;


my $graph = GD::Graph::lines->new($graphwidth, 1000);

$graph->set(
	 title          => 'Chat-Graph (monthly)',
	 @opts
 ) or die $graph->error;

settext($graph);

my $gd = $graph->plot(\@graphdataMonth) or die $graph->error;

my $outfile = sprintf($imagefile,'month');
open(IMG, ">$outfile") or die $!;
binmode IMG;
print IMG $gd->png;
close IMG;

