#!/Users/newby/.local/bin/perl -w

# gbn: My solution to the log analyzer assignment
# Required output:
#  number of entries
#  earliest & latest data
#  total time logged in per user
#  total logged in time overall plus average time logged in per day

use strict;                          # strict syntax checking

#####
# Data definition:
# username tty site weekday month monthday starttime - endtime (total)
# exceptions:
#  "still logged in" replaces from - onwards
#  (total) is either "hours:minutes" or "days+hours:minutes"

# uname
# tty
# host
# startday startmonth startdate starttime
# endtime
# totaltime

my %llog; my @empty;
my @tmp;

my $linecount=0;                    # number of input lines
my $skipped=0;                      # we'll skip 'still logged in'

# Array of months for earliest & latest date:
my $firstmon=2**32; my $lastmon=-1; my $firstday=2**32; my $lastday=-1;
my %mon = (                         # Map both ways for output
    Jan => 1, 1 => "Jan",
    Feb => 2, 2 => "Feb",
    Mar => 3, 3 => "Mar",
    Apr => 4, 4 => "Apr",
    May => 5, 5 => "May",
    Jun => 6, 6 => "Jun",
    Jul => 7, 7 => "Jul",
    Aug => 8, 8 => "Aug",
    Sep => 9, 9 => "Sep",
    Oct => 10, 10 => "Oct",
    Nov => 11, 11 => "Nov",
    Dec => 12, 12 => "Dec"
);

#####
# Get input into data structures:
open (FH, "<lastlog.txt") || die "Can't open lastlog.txt!\n";
while (<FH>) {
    $linecount++;

    # Skip people not logged out yet: 
    if ( /still logged in/ ) {
	$skipped++;
	next;
    }
    chomp;
    @tmp = split(/\s+/);
    my @th; 

# Track earliest & latest days:
    # Earliest:
    my $thismon = $mon{$tmp[4]};
    if ($thismon < $firstmon) {
	$firstmon = $thismon; # New earlier month!
	$firstday = $tmp[5];
    } else {
	if ($firstmon == $thismon) {
	    if ($tmp[5] < $firstday) {
		$firstday = $tmp[5]
		} 
	}
    }
    # Latest:
    if ($thismon > $lastmon) {
	$lastmon = $thismon; # New earlier month!
	$lastday = $tmp[5];
    } else {
	if ($lastmon == $thismon) {
	    if ($tmp[5] > $lastday) {
		$lastday = $tmp[5]
		} 
	}
    }

# Convert totaltime to minutes, rather than days+hours:minutes
    my @ttime = split(/[\+:]/, $tmp[8]);
    my $totaltime=0;               # in minutes
    # Got days?
    if ($#ttime == 3) {
	$totaltime += 24 * 60 * $ttime[0]; # days
	$totaltime += 60 * $ttime[1];      # hours
	$totaltime += $ttime[2];           # minutes
    } else {
	# No days, just hours + minutes
	$totaltime += 60 * $ttime[0];      # hours
	$totaltime += $ttime[1];           # minutes
    }

# This seemed like a good idea, but the hash just gets flattened
# to a list.  So, I will use an array, rather than an array of hashes.
#    @th = (
#	   tty => $tmp[1],
#	   host => $tmp[2],
#	   startwday => $tmp[3],
#	   startmonth => $tmp[4],
#	   startdate => $tmp[5],
#	   starttime => $tmp[6],
#	   endtime => $tmp[7],
#	   totaltime => $tmp[8]
#	   );
@th = (
       $tmp[1],                   # tty
       $tmp[2],                   # host
       $tmp[3],                   # startwday
       $tmp[4],                   # startmonth
       $tmp[5],                   # startdate
       $tmp[6],                   # starttime
       $tmp[7],                   # endtime
       $totaltime                 # totaltime in minutes
       );
       
#    push @{ $llog{"$tmp[0]"} }, [ @tmp ]; # syntax from "man perldsc"
    push @{ $llog{"$tmp[0]"} }, [ @th ]; # syntax from "man perldsc"
}
close(FH);

print "** Total number of entries: \t$linecount\n";
print "** Lines skipped (\"still logged in\"):\t$skipped\n"; 
printf "** Earliest entry is %s %2d\n", $mon{$firstmon}, $firstday;
printf "** Latest entry is %s %2d\n", $mon{$lastmon}, $lastday;

#####
# Output summary:
my $sanity=0;
my $nunique=0;                    # unique usernames

foreach my $i (sort keys %llog) {
    my $j=0; $nunique++;
    $sanity++;
    foreach ( $llog{"$i"} ) {
	my @q = @{$llog{"$i"}};
	$j = $#q;
	print "array of $i is $j\n";
    }
}
print "sanity=$sanity\n";
#####
# Count weekdays
my %weekday;
$sanity=0;
# Loop: for each username
foreach my $i (keys %llog) {
	my @q = @{$llog{"$i"}};
	# For each entry for this user:
	foreach my $j (0 .. $#q) {
	    $weekday{ $q[$j][2] } ++;
#	    $weekday{ $q[2] }++;
	    $sanity++;
	}
}
print "sanity=$sanity\n";

$sanity=0;
foreach my $i (sort keys %weekday) {
    print "Total " . $i . " logins were:\t" . $weekday{$i} . "\n";
    $sanity+= $weekday{$i};
}
print "sanity=$sanity\n";

#####
# Logins per day; average
my $numdays=0;
my %date;
foreach my $i (keys %llog) {
    my @q = @{$llog{"$i"}};
    # For each entry for this user:
    foreach my $j (0 .. $#q) {
	my $str = $q[$j][3] . " " . $q[$j][4]; # Mon Day
	$date{"$str"}++;
    }
}

my $average=0;
foreach my $i (keys %date) {
    $average += $date{"$i"};
    $numdays++;
}
$average = ($average + 0.0) / ($numdays + 0.0);

print "This log included $numdays unique days with logins,\n";
print "Average logins per day with logins is $average\n";

#####
# Total minutes for users
my $utime=0; my $alltime=0;
foreach my $i (keys %llog) {
    $utime=0;
    my @q = @{$llog{"$i"}};
    # For each entry for this user:
    foreach my $j (0 .. $#q) {
	$utime += $q[$j][7];
    }
    print "** Total time for user " . $i . "\t=\t" . $utime . "\n";
    printf "\tAverage per day (all days with any logins):\t%5.2f\n",
	    ($utime / $numdays);
    $alltime += $utime;
}

print "** All minutes for all users=$alltime\n";
printf "** Average time per day across all users=\t%5.2f\n",
	$alltime / $numdays;

exit;
