#!/usr/bin/perl


# mdc3-parse.pl
# June 2007
# Yoran Heling

# License: MIT


use strict;
use warnings;
use Storable 'store', 'retrieve';
use POSIX;
use POSIX::strptime;

my $log = 'log';                 # the microdc2 log file
my $result = 'transferred.html'; # the resulting html file
my $cache = 'logparse.pl_cache'; # file for the Storable cache


my %ts = -s $cache ? ( %{retrieve($cache)} ) : ();
my $totalsent = 0;

# using STDIN/OUT -> lazy and ugly :-)
open(STDIN, '<', $log) || die $!;
open(STDOUT, '>', $result) || die $!;


while(<>) {
  s/\r?\n//;
 # 28.04.2007 20:15:25 
  next if !s/^([0-9]{2}\.[0-9]{2}\.[0-9]{4}\s+[0-9]{2}:[0-9]{2}:[0-9]{2})\s//;
  my $date = $1;

 # plesnivec: Upload of `Ghost in the Shell - Stand Alone Complex - 25.avi' succeeded (transfer complete). 1.5MiB transferred in 53s (29KiB/s).
 # [Anime]Spank: Upload of `Rozen Maiden - Traumend - 07.avi' succeeded (transfer complete). 177MiB transferred in 2h46m35s (19KiB/s).
 # [izms]Inkognito: Upload of `Ultra Maniac TV - 16.avi' failed (communication error). 1.7MiB transferred in 3m4s (9KiB/s).
  if(/^(.+): Upload of `(.+)' (succeeded|failed) \(([^)]+)\)\. ([0-9\.]+[GMK]?i?B) transferred in ([0-9dhms]+) \(([0-9]+)KiB\/s\)/) {
    my($nick, $file, $sf, $msg, $sent, $time, $speed) = ($1, $2, $3, $4, $5, $6);
    $file =~ s/\\'/'/g;

    my $epoch = POSIX::mktime(POSIX::strptime($date, '%d.%m.%Y %T'));

    $sent = calcsize($sent);
    $time = calctime($time);

    $totalsent += $sent;

    my $k = $nick.$file;
    next if $ts{$k} && grep { +$_ == $epoch } @{$ts{$k}{epoch}};
    if($ts{$k}) {
      $ts{$k}{sent} += $sent;
      $ts{$k}{time} += $time;
      $ts{$k}{chunks}++;
      $ts{$k}{failed}++ if $sf eq 'failed';
      $ts{$k}{succeeded}++ if $sf eq 'succeeded';
      push(@{$ts{$k}{epoch}}, $epoch);
    } else {
      $ts{$k} = {
        file => $file,
        nick => $nick,
        sent => $sent,
        time => $time,
        date => $date,
        epoch => [ $epoch ], # only used for identification and sorting
#        msg => $msg, # unused
        chunks => 1,
        failed => $sf eq 'failed' ? 1 : 0,
        succeeded => $sf eq 'succeeded' ? 1 : 0,
      };
    }
  }
}
store \%ts, $cache;

printf "\nSent %d files and %s\n", scalar keys %ts, calcsize($totalsent);
print qq|
<html>
<head>
 <style>
  table { font-size: 13px; }
  td { padding: 0 5px 0 5px; border: 1px solid #ccc; white-space: nowrap; }
  thead td { font-weight: bold; }
  .tc4, .tc5, .tc7 { text-align: right }
 </style>
</head>
<body>
<table cellspacing=0 cellpadding=0>
 <thead><tr>
  <td class="tc1">Date/time</td>
  <td class="tc4">Transfered</td>
  <td class="tc5">Time</td>
  <td class="tc7">Speed</td>
  <td class="tc6">Chunks</td>
  <td class="tc2">User</td>
  <td class="tc3">Filename</td>
 </tr></thead>\n|;

printf(qq|<tr>
 <td class="tc1">%s</td>
 <td class="tc4">%s</td>
 <td class="tc5">%s</td>
 <td class="tc7">%.2f KiB/s</td>
 <td class="tc6">%d/%d/%d</td>
 <td class="tc2">%s</td>
 <td class="tc3">%s</td>
</tr>\n|,
  $ts{$_}{date},
  calcsize($ts{$_}{sent}),
  calctime($ts{$_}{time}),
  $ts{$_}{sent}/($ts{$_}{time}||1),
  $ts{$_}{chunks}, $ts{$_}{succeeded}, $ts{$_}{failed},
  $ts{$_}{nick},
  $ts{$_}{file},
)
  for (sort { $ts{$b}{epoch}[0] <=> $ts{$a}{epoch}[0] } keys %ts);

print qq|</table>
</body>
</html>|;


sub calcsize { # in KBytes by default, to avoid a 32bit int overflow
  if($_[0] =~ /^([0-9\.]+)(GiB|MiB|KiB|B)$/) {
    return $2 eq 'B'   ? $1/1024 :
           $2 eq 'KiB' ? $1 :
           $2 eq 'MiB' ? $1*1024 :
                         $1*1024*1024;
  } else {
    my $r = $_[0]; my $x = 'KiB';
    if($r > 1024) { $r/=1024; $x='MiB' }
    if($r > 1024) { $r/=1024; $x='GiB' }
    return sprintf "%.1f %s", $r, $x;
  }
}
sub calctime {
  if($_[0] =~ /[dhms]/) {
    my @t = reverse split(/[dhms]/, $_[0]);
    return ($t[3]||0)*86400 + ($t[2]||0)*3600 + ($t[1]||0)*60 + $t[0];
  } else {
    my $r = $_[0]; my $x = '';
    if($r > 24*3600) { $x .= int($r/(24*3600)).' d '; $r%=24*3600; }
    $x .= sprintf '%d:%02d:%02d',
      int($r/3600),
      int(($r%3600)/60),
      ($r%3600) % 60;
    return $x;
  }
}
