#!/usr/bin/env perl
use strict;
use Time::HiRes qw( gettimeofday );
use CGI;
use DBI;
use GD;
use Geo::IP;
use Geo::Proj4;


my ($start_sec, $start_micro) = gettimeofday;

my ($page);

my ($dsn) = "DBI:mysql:database=eprintstats;host=localhost";
my ($user_name) = "eprintstatspriv";
my ($password) = "AuldGrizzel";
my ($connect, $query, %types, $stat, $row, $num_rows, $vtype);

my ($mapimage, $mapimagefile);
my ($red, $white, $black, $blue);
my ($width, $height);

my ($gi, $proj);
my ($gidb) = '/usr/local/share/GeoIP/GeoLiteCity.dat';

my ($x_offset) = 16986796.16;
my ($y_offset) = 8615499.05;
my ($max_x) = $x_offset * 2;
my ($max_y) = $y_offset * 2;
my (%cities);
my ($num_IPs) = -1;
my ($num_hits) = 0;
my ($ip, $count, $location);
my ($city, $lat, $long, $x, $y);


$page = new CGI;
print $page->header( -type=>"image/jpeg", -Pragma=>'no-cache' );

$gi = Geo::IP->open( $gidb, GEOIP_STANDARD )
	or die "Unable to open GeoIP database $gidb\n";

$proj = Geo::Proj4->new( proj => "robin", ellps => "sphere", lon_0 => 10 )
	or die "parameter error: " . Geo::Proj4->error . "\n";

$width = $page->param('width');
$height = $page->param('height');
$mapimagefile = "map_${width}x${height}.png";
$mapimage = GD::Image->newFromPng( $mapimagefile, 1 );
$white = $mapimage->colorAllocate( 255, 255, 255 );
$black = $mapimage->colorAllocate( 0, 0, 0 );
$red = $mapimage->colorAllocate( 255, 0, 0 );
$blue = $mapimage->colorAllocate( 0, 0, 255 );

$connect = DBI->connect( $dsn, $user_name, $password, { RaiseError => 1 } );

$types{'download'} = $types{'abstract'} = 0;
$query =
	"SELECT ip, view_type, COUNT(*) AS count
	 FROM view
	 GROUP BY ip, view_type
	 ORDER BY count DESC" .
	 ( $num_IPs > 0 ? " LIMIT $num_IPs" : '' );

$stat = $connect->prepare( $query );
$stat->execute();
$num_rows = $stat->rows;

if ( $num_rows > 0 )
{
	$num_IPs = $num_rows if ( $num_IPs < 1 );
	
	while ( $row = $stat->fetchrow_hashref() )
	{
		$ip = $row->{'ip'};
		$count = $row->{'count'};
		$vtype = $row->{'view_type'};
		
		$location = $gi->record_by_addr( $ip );
		
		if ( defined( $location ) )
		{
			$lat = $location->latitude;
			$long = $location->longitude;
			$city = $location->city;
			($x, $y) = $proj->forward( $lat, $long );
			$x = round(($x + $x_offset) / $max_x * $width);
			$y = round(($y_offset - $y) / $max_y * $height);

			$city = "Unknown ($lat, $long)" if ( $city eq '' );

			if ( !defined( $cities{$city} ) )
			{
				$cities{$city}{'count'} = 0;
				$cities{$city}{'lat'} = $lat;
				$cities{$city}{'long'} = $long;
				
				$mapimage->filledRectangle( $x - 1, $y - 1, $x + 1, $y + 1, ( $vtype eq 'download' ? $red : $blue ) );
			}
			$cities{$city}{'count'} += $count;
			$types{$vtype} += $count;
			
			$cities{$city}{'x'} = $x;
			$cities{$city}{'y'} = $y;
		}
	}
}
	
$stat->finish();
$connect->disconnect();
	
$mapimage->string( gdSmallFont, 3, 3, "$types{'download'} downloads", $red );
$mapimage->string( gdSmallFont, 3, 15, "$types{'abstract'} abstracts", $blue );
$mapimage->string( gdSmallFont, 3, 27, 'from ' . scalar( keys %cities ) . ' cities', $black );
$mapimage->string( gdSmallFont, 3, 39, "($num_rows IP addresses)", $black );

my ($finish_sec, $finish_micro) = gettimeofday();
$mapimage->string( gdSmallFont, 3, $height - 15, 'Generated in ' . round( ( $finish_micro - $start_micro ) / 1000 ) . ' ms', $black );

binmode(STDOUT);

print $mapimage->jpeg();


sub round
{
	my ($n) = shift;
	return int( $n + .5 * ($n <=> 0) );
}
