#!/bin/perl -W

use GD;

$/ = "\r\n";

$near0 = 70;
$near1 = 200;
$thumbsize = 400;

$black = 0x000000;
$white = 0xffffff;
$red   = 0xff0000;
$green = 0x00ff00;
$blue  = 0x0000ff;
$magenta = 0xff00ff;
$yellow = 0xffff00;
$orange = 0xff8000;
$grey  = 0x404040;
$teamcol[1] = $blue;
$teamcol[2] = $magenta;

# from damageTypes.cs
$DamageTypeText[0] = 'default';
$DamageTypeText[1] = 'blaster';
$DamageTypeText[2] = 'plasma';
$DamageTypeText[3] = 'chaingun';
$DamageTypeText[4] = 'disc';
$DamageTypeText[5] = 'grenade';
$DamageTypeText[6] = 'laser';
$DamageTypeText[7] = 'ELF';
$DamageTypeText[8] = 'mortar';
$DamageTypeText[9] = 'missile';
$DamageTypeText[10] = 'shocklance';
$DamageTypeText[11] = 'mine';
$DamageTypeText[12] = 'explosion';
$DamageTypeText[13] = 'impact';
$DamageTypeText[14] = 'ground';
$DamageTypeText[15] = 'turret';
$DamageTypeText[16] = 'plasma turret';
$DamageTypeText[17] = 'AA turret';
$DamageTypeText[18] = 'ELF turret';
$DamageTypeText[19] = 'mortar turret';
$DamageTypeText[20] = 'missile turret';
$DamageTypeText[21] = 'clamp turret';
$DamageTypeText[22] = 'spike turret';
$DamageTypeText[23] = 'sentry turret';
$DamageTypeText[24] = 'out of bounds';
$DamageTypeText[25] = 'lava';
$DamageTypeText[26] = 'shrike blaster';
$DamageTypeText[27] = 'belly turret';
$DamageTypeText[28] = 'bomber bomb';
$DamageTypeText[29] = 'tank chaingun';
$DamageTypeText[30] = 'tank mortar';
$DamageTypeText[31] = 'satchel charge';
$DamageTypeText[32] = 'MPB missile';
$DamageTypeText[33] = 'lighting';
$DamageTypeText[35] = 'ForceField';
$DamageTypeText[36] = 'Crash';
$DamageTypeText[98] = 'nexus camping';
$DamageTypeText[99] = 'suicide';

$icon_player = load_image("icons/com_player_grey_24x.png");
$icon{'flag'} = load_image("icons/com_icon_flag_outside.png");
$icon{'gen'} = load_image("icons/com_icon_generator.png");
$icon{'turret'} = load_image("icons/com_icon_turretbase.png");
$icon{'inv'} = load_image("icons/com_icon_inventory.png");
$icon{'sensor'} = load_image("icons/com_icon_sensor.png");
$icon{'vpad'} = load_image("icons/com_icon_vehicle_inventory.png");

# read header
$mission = <>; chomp $mission;
$mission_type = <>; chomp $mission_type;
$mission_name = <>; chomp $mission_name;
$mission_type_name = <>; chomp $mission_type_name;
$area = <>; chomp $area; ($x0, $y0, $width, $height) = split / /, $area;
while (($_ = <>) ne $/) {
	chomp;
	($team, $type, $block, $name, $x, $y, $z, $desc) = split / /, $_, 8;
	$x -= $x0; $y = $height - $y + $y0;
	if ($type eq 'StaticShape' and $block eq 'GeneratorLarge') {
		push @landmarks, "$z gen $team $x $y";
	} elsif ($type eq 'Turret' and $block eq 'TurretBaseLarge') {
		push @landmarks, "$z turret $team $x $y";
	} elsif ($type eq 'StaticShape' and $block eq 'ExteriorFlagStand') {
		push @landmarks, "$z flag $team $x $y";
	} elsif ($type eq 'StaticShape' and $block eq 'StationVehicle') {
		push @landmarks, "$z vpad $team $x $y";
	} elsif ($type eq 'StaticShape' and $block eq 'StationInventory') {
		push @landmarks, "$z inv $team $x $y";
	} elsif ($type eq 'StaticShape' and $block eq 'SensorLargePulse') {
		push @landmarks, "$z sensor $team $x $y";
	} elsif ($type eq 'WayPoint' and $block eq 'WayPointMarker') {
		push @waypoints, "$team $x $y $z $desc";
	}
}
@landmarks = sort { my ($az, $ra) = split / /, $a, 2; my ($bz, $rb) = split / /, $b, 2;
		return $az <=> $bz; } @landmarks;

$html_index = create_html('Match Summary');

$time = 0;
$nicetime = "0:00";
$imall = create_image();
%alive = ();
@allevents = ();

while (<>) {
	chomp;
	if ($_ eq '') {
		plot_frame();
		$time++;
		$secs = $time % 60;
		$nicetime = int($time / 60) . ':' . (($secs < 10) ? "0$secs" : $secs);
		next;
	}

	($cmd, $params) = split / /, $_, 2;

	if ($cmd eq 'S' or $cmd eq 'V') {	# player spawned / new vehicle
		($id, $team, $name) = split / /, $params, 3;
		if ($cmd eq 'V') {
			$vname{$id} = $name;
			$name = $id;
		}
		$name{$id} = $name;
		$life{$name}++;
		$type{$name} = $cmd eq 'S' ? 'player' : 'vehicle';
		$team{$name} = $team;
		$im{$name} = create_image();
		delete $last_pos{$name};
		delete $pos{$name};
		$alive{$name} = 1;
		$near{$name} = {};
		$events{$name} = [];
		if (!exists $html{$name}) {
			$html{$name} = create_html($name);
			unless ($type{$name} eq 'vehicle') {
				$html{$name} .= <<END;
<p>Total score FINALSCORE, deaths FINALDEATHS, kills FINALKILLS.</p>
END
			}
			$score{$name} = 0;
			$deaths{$name} = 0;
			$kills{$name} = 0;
		} else {
			$html{$name} .= "</table></div>\n";
		}
		$sname = safe_name($name);
		$html{$name} .= <<END;
<div><h2 id="life$life{$name}">Life $life{$name} ($nicetime - ENDTIME)</h2>
<div><a href="${sname}_$life{$name}.png"><img src="s${sname}_$life{$name}.png"
alt="" title="Map of life $life{$name}"></a></div>
<table>
END
		event($name, "spawned on team $team", $yellow);

	} elsif ($cmd eq 'F') {		# flag position

	} elsif ($cmd eq 'D') {		# player damaged
		($victim, $attacker, $weapon) = split / /, $params;

	} elsif ($cmd eq 'K' or $cmd eq 'L' or $cmd eq 'W') {	# player killed / left / vehicle destroyed
		if ($cmd eq 'K') {
			($victim, $killer, $weapon) = split / /, $params;
			$victim = $name{$victim};
			$killer = $name{$killer};
			event($victim, "killed by $DamageTypeText[$weapon]", $red, $killer);
			if (defined $killer) {
				event($killer, "killed ($DamageTypeText[$weapon])", $green, $victim);
				$kills{$killer}++;
			}
			$deaths{$victim}++;

		} elsif ($cmd eq 'L') {
			($victim, $score) = split / /, $params;
			$victim = $name{$victim};
			undef $killer;
			event($victim, "left the game, score $score", $red);
			$score{$victim} += $score;

		} elsif ($cmd eq 'W') {
			$victim = $name{$params};
		}

		foreach $name (keys %near) {
			delete ${$near{$name}}{$victim} if exists ${$near{$name}}{$victim};
		}

		if ($im{$victim}) {
			plot_events($victim);
			$sname = safe_name($victim);
			save_image($im{$victim}, "${sname}_$life{$victim}.png");
		}
		delete $alive{$victim};
		$html{$victim} =~ s/ENDTIME/$nicetime/;

	} elsif ($cmd eq 'A') {		# armour changed
		($id, $armour) = split / /, $params;
		$name = $name{$id};
		$armour{$name} = $armour;
		event($name, "$armour armour", $orange);

	} elsif ($cmd eq 'Z') {		# final score

	} else {			# player / vehicle position
		$id = $cmd;
		$name = $name{$id};
		next unless (exists $name{$id} and exists $im{$name});
		($x, $y, $z, $vehicle, $seat) = split / /, $params;
		$x -= $x0; $y = $height - $y + $y0;
		$pos{$name} = "$x $y $z";
		if (exists $pending{$name}) {
			push @{$events{$name}}, "$x $y $z $pending{$name}";
			delete $pending{$name};
		}

	}
}

foreach $victim (keys %alive) {
	if ($im{$victim}) {
		plot_events($victim);
		$sname = safe_name($victim);
		save_image($im{$victim}, "${sname}_$life{$victim}.png");
	}
}

$html_index .= <<END;
<div><a href="Overview.png"><img src="sOverview.png"
alt="" title="Overview map"></a></div>
<p>Playing time: $nicetime</p>
<table>
<tr><th>Player</th><th>Team</th><th>Score</th><th>Deaths</th><th>Kills</th></tr>
END
while (($name, $html) = each %html) {
	$html .= "</table></div>\n";
	$html =~ s/FINALSCORE/$score{$name}/;
	$html =~ s/FINALDEATHS/$deaths{$name}/;
	$html =~ s/FINALKILLS/$kills{$name}/;
	$sname = safe_name($name);
	save_html("$sname.html", $html);
	if ($type{$name} eq 'player') {
		$html_index .= <<END;
<tr><td><a href="$sname.html">$name</a></td><td>$team{$name}</td><td>$score{$name}</td><td>$deaths{$name}</td><td>$kills{$name}</td></tr>
END
	}
}

$html_index .= "</table>\n";


#$html_index .= "<h2>Timeline<h2>\n<table class='timeline'>\n";
$header = "<tr><td></td>";
foreach $n (keys %type) {
	$header .= "<th>$n</th>";
}
$header .= "</tr>\n";
#$html_index .= $header;
$time = 0;
%event = ();
foreach $e (@allevents) {
	($t, $colour, $name) = split / /, $e, 3;
	while ($time < $t) {
		$secs = $time % 60;
		$nicetime = int($time / 60) . ':' . (($secs < 10) ? "0$secs" : $secs);
#		$html_index .= "<tr><td>$nicetime</td>";
		foreach $n (keys %type) {
			if (exists $event{$n}) {
				$hexcol = sprintf "%.6lx", $event{$n};
#				$html_index .= "<td style='background-color: #$hexcol'>&nbsp;</td>";
			} else {
#				$html_index .= "<td>&nbsp;</td>";
			}
		}
		%event = ();
#		$html_index .= "</tr>\n";
		$time++;
#		$html_index .= $header if (($time % 30) == 0);
	}
	$event{$name} = $colour;
}

save_image($imall, "Overview.png");
#$html_index .= "</table>\n";
save_html("index.html", $html_index);


sub plot_frame {
	$s = 1;
	$s = 2 if ($time % 10) == 0;
	foreach $name (keys %alive) {
		next unless exists $pos{$name};
		($x, $y, $z) = split / /, $pos{$name};
		%near0 = %{$near{$name}};
		foreach $name1 (keys %alive) {
			next if $name1 eq $name;
			($x1, $y1, $z1) = split / /, $pos{$name1};
			my $dist = distance($x, $y, $z, $x1, $y1, $z1);
			if (exists ${$near{$name}}{$name1}) {
				delete ${$near{$name}}{$name1} if $near1 < $dist;
			} elsif ($dist < $near0) {
				($x1, $y1, $z1) = split / /, $pos{$name1};
				text($im{$name}, $x1 + 4, $y1, $name1);
				${$near{$name}}{$name1} = 1;
			}
		}
		foreach $name1 (keys %{$near{$name}}) {
			($x1, $y1, $z1) = split / /, $pos{$name1};
			my $colour = $team{$name1} == $team{$name} ? $green : $red;
			if ($last_pos{$name1}) {
				($xp, $yp, $zp) = split / /, $last_pos{$name1};
				line($im{$name}, $xp, $yp, $x1, $y1, $colour);
			}
			square($im{$name}, $x1, $y1, $s, $colour);
			line($im{$name}, $x, $y, $x1, $y1, $grey) if $s == 2;
		}
		text($im{$name}, $x, $y, $nicetime) if $s == 2;
		if (exists $last_pos{$name}) {
			($xp, $yp, $zp) = split / /, $last_pos{$name};
			line($im{$name}, $xp, $yp, $x, $y, $white);
			line($imall, $xp, $yp, $x, $y, $teamcol[$team{$name}]);
		}
		square($im{$name}, $x, $y, $s, $white);

	}
	%last_pos = %pos;
	%pos = ();
}

sub create_image {
	my $image = GD::Image->newTrueColor($width, $height);
	my $lm;
	foreach $lm (@landmarks) {
		my ($z, $type, $team, $x, $y) = split / /, $lm;
		icon($image, $icon{$type}, $x, $y);
	}
	foreach $g (@waypoints) {
		my ($team, $x, $y, $z, $desc) = split / /, $g, 5;
		text($image, $x, $y, $desc);
	}
	return $image;
}

sub save_image {
	my $image = shift;
	my $file = shift;
	my $png = $image->png;
	open PNG, ">$file" or die "failed to open $file: $!";
	binmode PNG;
	print PNG $png;
	close PNG;

	my $max = $width < $height ? $height : $width;
	my $thumb = GD::Image->newTrueColor($thumbsize * $width / $max,
			$thumbsize * $height / $max);
	$thumb->copyResampled($image, 0, 0, 0, 0, $thumbsize * $width / $max,
			$thumbsize * $height / $max, $width, $height);
	$png = $thumb->png;
	open PNG, ">s$file" or die "failed to open s$file: $!";
	binmode PNG;
	print PNG $png;
	close PNG;
}

sub square {
	my $image = shift;
	return unless defined $image;
	my $x = shift;
	my $y = shift;
	my $s = shift;
	my $colour = shift;
	$image->filledRectangle($x - $s, $y - $s, $x + $s, $y + $s, $colour);
}

sub line {
	my $image = shift;
	return unless defined $image;
	my $x0 = shift;
	my $y0 = shift;
	my $x1 = shift;
	my $y1 = shift;
	my $colour = shift;
	$image->line($x0, $y0, $x1, $y1, $colour);
}

sub distance {
	return sqrt (($_[0] - $_[3]) ** 2 + ($_[1] - $_[4]) ** 2 + ($_[2] - $_[5]) ** 2);
}

sub load_image {
	my $file = shift;
	open PNG, $file or die "failed to open $file: $!";
	my $im = GD::Image->newFromPng(\*PNG) or die "PNG load of $file failed";
	close PNG;
	$im->transparent(0);
	return $im;
}

sub icon {
	my $image = shift;
	my $icon = shift;
	my $x = shift;
	my $y = shift;
	$image->setBrush($icon);
	$image->setPixel($x, $y, gdBrushed);
}

sub text {
	my $image = shift;
	my $x = shift;
	my $y = shift;
	my $text = shift;
	$image->string(gdSmallFont, $x, $y, $text, $white);
}

sub create_html {
	my $title = shift;
	my $html = <<END;
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN"
        "http://www.w3.org/TR/html4/strict.dtd">
<html>
<head>
<link rel="stylesheet" type="text/css" href="t2matchlog.css">
<title>$title ($mission_name / $mission_type_name)</title>
</head>

<body>
<h1>$title <em class="mission">($mission_name / $mission_type_name)</em></h1>

END
	return $html;
}

sub save_html {
	my $file = shift;
	my $html = shift;
	$html .= "</body></html>\n";
	open HTML, ">$file" or die "failed to open $file: $!";
	print HTML $html;
	close HTML;
}

sub safe_name {
	my $s = shift;
	$s =~ tr/[a-zA-Z0-9]/_/cs;
	return $s;
}

sub event {
	my $name = shift;
	return unless exists $html{$name};
	my $desc = shift;
	my $colour = shift;
	my $name2 = shift;
	print "$nicetime\t$name $desc";
	my $hexcol = sprintf "%.6lx", $colour;
	my $line = "<tr id='t$time'><td class='time'>$nicetime</td><td style='color: #$hexcol'>$desc";
	if (defined $name2 and exists $html{$name2}) {
		print " $name2";
		$sname = safe_name($name2);
		$line .= " <a href='$sname.html#t$time'>$name2</a>";
	}
	print "\n";
	$line .= "</td></tr>\n";
	$html{$name} .= $line;
	if (exists $last_pos{$name}) {
		push @{$events{$name}}, "$last_pos{$name} $colour $desc";
	} else {
		$pending{$name} = "$colour $desc";
	}
	push @allevents, "$time $colour $name";
}

sub plot_events {
	my $name = shift;
	foreach $e (@{$events{$name}}) {
		my ($x, $y, $z, $colour, $desc) = split / /, $e, 5;
		square($im{$name}, $x, $y, 8, $colour);
	}
}

