#!/usr/bin/perl -w

# gs_analyze2.pl
# NDC Code Release 1
#
# Reads a GS-compliant log file from STDIN, writes processed logs
# to various files.
#
#   Copyright (C) 2001 Andreas Ulbrich, Ralf Ertzinger (ndccode@ndc.sh)
#
#   This program is free software; you can redistribute it and/or modify
#   it under the terms of the GNU General Public License as published by
#   the Free Software Foundation; either version 2 of the License, or
#   (at your option) any later version.
#
#   This program is distributed in the hope that it will be useful,
#   but WITHOUT ANY WARRANTY; without even the implied warranty of
#   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#   GNU General Public License for more details.
#
#   You should have received a copy of the GNU General Public License
#   along with this program; if not, write to the Free Software
#   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA

@gslog=<STDIN>;

# Enable this line for debugging

# $debug = 1;

# Read mapname-mapping from file

open (MAPNAMES,"mapnames.txt") or die "Could not open mapnames.txt: $!\n";

# Log file for skill calculation
open (SKILLOG, ">skill.log") or die "Could not open skill.log: $!\n";

while ($line=<MAPNAMES>) {

  ($handle,$mapname) = split (":",$line);
  chop($mapname);
  $handle{$mapname} = $handle;
}

close (MAPNAMES); 

while ($gslog_line = shift(@gslog)) {

  ($killer,$victim,$action,$weapon,$fragdiff,$timestamp,$ping) = 
  split("\t",$gslog_line);
 
  unless (defined($action)) {
    next;
  }

  if ($action eq "Kill") {
    $pl_kills{"##global"}{$killer}++;
    $pl_kills{$actual_map}{$killer}++;
    $pl_victims{"##global"}{$victim}++;
    $pl_victims{$actual_map}{$victim}++;
    if (defined($pl_count_map{$killer})) {
      $pl_maps_played{"##global"}{$killer}++;
      $pl_maps_played{$actual_map}{$killer}++;
      print "Map count for $killer increased to $pl_maps_played{\"##global\"}{$killer}\n" if $debug;
      undef $pl_count_map{$killer};
    }
    if (defined($pl_count_map{$victim})) {
      $pl_maps_played{"##global"}{$victim}++;
      $pl_maps_played{$actual_map}{$victim}++;
      print "Map count for $victim increased to $pl_maps_played{\"##global\"}{$victim}\n" if $debug;
      undef $pl_count_map{$victim};
    }
    $sys_kills{"##global"}++;
    $sys_kills{$actual_map}++;
    $pl_kills_local{$killer}++;

    # Print out skill log
    print SKILLOG "K:$killer:$victim\n";
  }

  if ($action eq "Suicide") {
    $pl_suicides{"##global"}{$killer}++;
    $pl_suicides{$actual_map}{$killer}++;
    if (defined($pl_count_map{$killer})) {
      $pl_maps_played{"##global"}{$killer}++;
      $pl_maps_played{$actual_map}{$killer}++;
      print "Map count for $killer increased to $pl_maps_played{\"##global\"}{$killer}\n" if $debug;
      undef $pl_count_map{$killer};
    }
    $sys_suicides{"##global"}++;
    $sys_suicides{$actual_map}++;
    $pl_kills_local{$killer}--;

    # Print out skill log
    print SKILLOG "S:$killer\n";
  }

  # Check if a new map has been started.
  # $weapon holds the map name

  if ($action eq "Map") {
    chop ($weapon);
    chop ($weapon);
    $actual_map = $handle{$weapon};
    unless (defined($actual_map)) {
      $actual_map = "##undefined";
    }
    $sys_maps_played{"##global"}++;
    $sys_maps_played{$actual_map}++;
    print "Map started: $actual_map\n" if $debug;
  }

  # Check for a newly started game.

  if ($action eq "GameStart") {
    pl_disconnect_all();
  }


  # Check for the end of a map. Calculate map winner.
  # Log out all players

  if ($action eq "GameEnd") {
    print "Map ends: $actual_map\n" if $debug;
    $sys_connect_total{"##global"} += $timestamp;
    $sys_connect_total{$actual_map} += $timestamp;
    $pl_best_frags = 0;
    undef $pl_best_nick;
    if (defined(%pl_kills_local)) {
      foreach $nick (keys %pl_kills_local) {
        if ($pl_kills_local{$nick} > $pl_best_frags) {
          $pl_best_frags = $pl_kills_local{$nick};
          $pl_best_nick = $nick;
        }
      }
      if (defined($pl_best_nick)) {
        $pl_maps_won{"##global"}{$pl_best_nick}++;
        $pl_maps_won{$actual_map}{$pl_best_nick}++;
        print "$pl_best_nick wins $actual_map with $pl_best_frags\n" if $debug;
      }
    }
    pl_disconnect_all();

    # Print out skill log
    if (defined($pl_best_nick)) { 		# If no one won the map, why bother?
      print SKILLOG "W:$pl_best_nick\n";
    } 
  }


  # Check for a newly connecting player. Initialize all fields, if necessary.
  # $weapon holds player name.

  if ($action eq "PlayerConnect") {

    pl_connect($weapon);

  }


  # If a player disconnects, log his online time.
  # $weapon holds the player name.

  if ($action eq "PlayerLeft") {

    pl_disconnect($weapon);

  }


  # If a player renames himself, consider this a disconnect and immediate
  # reconnect.
  # $weapon holds the old name, $fragdiff the new one.
  # Preserve frags in the local map! Important for winning caclulations
  # WARNING! We might get into trouble here. If a player wins a map,
  # and renames during the ending music, the "new" player will win the
  # map (since he inherits the kills), but the map will not count as
  # played (since he was neither fragged, nor fragged another player).
  # PS: this is not theoretical ;)

  if ($action eq "PlayerRename") {

    $tmp = $pl_kills_local{$weapon};
    print "$weapon renamed to $fragdiff, inheriting $tmp\n" if $debug;
    # If the map counts for the "leaving" player, decrease
    # his mapcount, and increase the count of the "connecting" player.
    unless(defined($pl_count_map{$weapon})) {
      $pl_maps_played{"##global"}{$weapon}--;
      $pl_maps_played{$actual_map}{$weapon}--;
      print "Map count for $weapon decreased to $pl_maps_played{\"##global\"}{$weapon}\n" if $debug;
    }
    pl_disconnect($weapon);
    pl_connect($fragdiff);
    unless(defined($pl_count_map{$weapon})) {
      $pl_maps_played{"##global"}{$fragdiff}++;
      $pl_maps_played{$actual_map}{$fragdiff}++;
      print "Map count for $fragdiff increased to $pl_maps_played{\"##global\"}{$fragdiff}\n" if $debug;
    }
    $pl_kills_local{$fragdiff} = $tmp;
  }

}

# Just to be sure....

pl_disconnect_all();

# Write out all results. Phew.

foreach $mapkey (keys %pl_kills) {

  if ($mapkey eq "##global") {
    $filename = "big.res";
  } else {
    $filename = $mapkey . ".res";
  }
  

  open(LOGFILE, ">$filename") or die "Could not open $filename: $!\n";

  select LOGFILE;

  # Print final results. First line holds overall system statistics.

  print ":";
  print "$sys_kills{$mapkey}:";
  print ":";
  print "$sys_suicides{$mapkey}:";
  print "$sys_maps_played{$mapkey}:";
  print ":";
  print "$sys_connect_total{$mapkey}:";
  print "\n";

  foreach $nick (keys %{ $pl_kills{$mapkey} }) {

    print "$nick:";
    print "$pl_kills{$mapkey}{$nick}:";
    print "$pl_victims{$mapkey}{$nick}:";
    print "$pl_suicides{$mapkey}{$nick}:";
    print "$pl_maps_played{$mapkey}{$nick}:";
    print "$pl_maps_won{$mapkey}{$nick}:";
    print "$pl_connect_total{$mapkey}{$nick}:";
    print "\n";

  }

  select STDOUT;

  close (LOGFILE);

}


###### - Subroutines

sub pl_connect() {

  $pl_name = shift;
  return unless defined($pl_name);

  # Reduce the player name to the first 15 characters. You want that.
  # Believe me.

  $pl_name = substr($pl_name, 0, 15);

  print "Player connected: $pl_name\n" if $debug;

  $pl_kills{"##global"}{$pl_name} = 0 unless defined($pl_kills{"##global"}{$pl_name});
  $pl_kills{$actual_map}{$pl_name} = 0 unless defined($pl_kills{$actual_map}{$pl_name});

  $pl_victims{"##global"}{$pl_name} = 0 unless defined($pl_victims{"##global"}{$pl_name});
  $pl_victims{$actual_map}{$pl_name} = 0 unless defined($pl_victims{$actual_map}{$pl_name});

  $pl_suicides{"##global"}{$pl_name} = 0 unless defined($pl_suicides{"##global"}{$pl_name});
  $pl_suicides{$actual_map}{$pl_name} = 0 unless defined($pl_suicides{$actual_map}{$pl_name});

  $pl_maps_played{"##global"}{$pl_name} = 0 unless defined($pl_maps_played{"##global"}{$pl_name});
  $pl_maps_played{$actual_map}{$pl_name} = 0 unless defined($pl_maps_played{$actual_map}{$pl_name});

  $pl_maps_won{"##global"}{$pl_name} = 0 unless defined($pl_maps_won{"##global"}{$pl_name});
  $pl_maps_won{$actual_map}{$pl_name} = 0 unless defined($pl_maps_won{$actual_map}{$pl_name});



  # If the player reconnects before a map change, don't count as
  # a played map
  # This flag is reset by pl_disconnect_all().

  unless (defined($pl_connect{$pl_name})) {
    $pl_count_map{$pl_name} = 1;
  }

  $pl_connect{$pl_name} = 1;
  $pl_kills_local{$pl_name} = 0;
  $pl_connect_start{"##global"}{$pl_name} = $timestamp;
  $pl_connect_start{$actual_map}{$pl_name} = $timestamp;
}

# ----------------------

sub pl_disconnect {

  $pl_name = shift;
  return unless defined($pl_name);

  print "Player left: $pl_name with $pl_maps_played{\"##global\"}{$pl_name} maps played, $pl_maps_won{\"##global\"}{$pl_name} maps won\n" if $debug;

  $pl_connect_total{"##global"}{$pl_name} += ($timestamp - $pl_connect_start{"##global"}{$pl_name});
  $pl_connect_total{$actual_map}{$pl_name} += ($timestamp - $pl_connect_start{$actual_map}{$pl_name});
  $pl_connect_start{"##global"}{$pl_name} = -1;
  delete $pl_kills_local{$pl_name};
}

# ----------------------

sub pl_disconnect_all {

  foreach $name (keys %pl_connect) {

    undef $pl_connect{$name};
    undef $pl_count_map{$name};
    if ($pl_connect_start{"##global"}{$name} >= 0) {
      pl_disconnect($name);
    }
    undef %pl_kills_local;
  }
}
