#!/usr/bin/perl

##**************************************************************
##
## Copyright (C) 1990-2007, Condor Team, Computer Sciences Department,
## University of Wisconsin-Madison, WI.
## 
## Licensed under the Apache License, Version 2.0 (the "License"); you
## may not use this file except in compliance with the License.  You may
## obtain a copy of the License at
## 
##    http://www.apache.org/licenses/LICENSE-2.0
## 
## Unless required by applicable law or agreed to in writing, software
## distributed under the License is distributed on an "AS IS" BASIS,
## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
## See the License for the specific language governing permissions and
## limitations under the License.
##
##**************************************************************


use warnings;
use strict;

sub ParseHex($);
sub DumpHistories( $$ );
sub DumpHistory( $$$ );

sub Usage ( $$ );
sub Help ( $ );

my %Config =
(
 HistMin	=> 0,
 HistMax	=> 99999,
 Long		=> 0,
 SummaryOnly	=> 0,
 UpdateInterval => 0,
 DumpTime	=> 1,
);


my $Program = "condor_updates_stats";
my $RevisionString = '$Revision: 1.5 $';
my $CondorVersion = '$CondorVersion$';
my $CondorPlatform = '$CondorPlatform$';
my %Help =
    (
     Usage => "$Program [options]",
     Options =>
     {
      "[--long|-l]"
      => "Enable long output <off>",
      "[--summary|-s]"
      => "Summary-only mode <off>",
      "[--history=[min][-max]]"
      => "Set history range from min to max",
      "[--[no]time]"
      => "Enable / disable time output (if available) <on>",
      "[--interval=seconds]"
      => "Force ad update interval to seconds <no default>",
      "[--version]"
      => "Displays version information <off>",
      "[--help|-h]"
      => "Dump help",
     },
    );

# Parse command line...
foreach my $Arg ( @ARGV )
{
    if ( $Arg =~ /^--history=(\d*)(-(\d+))?$/ )
    {
	$Config{HistMin} = $1 if ( $1 ne "" );
	$Config{HistMax} = $3 if ( defined $3 && $3 ne "" );
    }
    elsif ( $Arg =~ /^--interval=(\d+)/ )
    {
	$Config{UpdateInterval} = $1;
	$Config{DumpTime} = 1;
    }
    elsif ( $Arg eq "--summary|-s" or $Arg eq "-s" )
    {
	$Config{SummaryOnly} = 1;
    }
    elsif ( $Arg eq "--time" )
    {
	$Config{DumpTime} = 1;
    }
    elsif ( $Arg eq "--notime" )
    {
	$Config{DumpTime} = 0;
    }
    elsif (  ( $Arg eq "--long" ) || ( $Arg eq "-l" ) )
    {
	$Config{Long} = 1;
    }
    elsif( $Arg eq "--version" )
    {
	if ( length( $CondorVersion ) > 20 )
	{
	    print "$CondorVersion\n";
	    print "$CondorPlatform\n";
	}
	elsif ( open( CV, "condor_version|" ) )
	{
	    while( <CV> )
	    {
		print;
	    }
	    close( CV );
	}
	else
	{
	    if ( $RevisionString =~ /([\d\.]+)/ )
	    {
		$RevisionString = $1;
	    }
	    print "$Program version: $RevisionString\n";
	}
	exit 0;
    }
    elsif(  ( $Arg eq "--help" ) || ( $Arg eq "-h" ) )
    {
	Help( \%Help );
	exit 0;
    }
    else
    {
	Usage( \%Help, $Arg );
	exit 1;
    }
}
$#ARGV = -1;

# Just pull from stdin...
my %AdInfo;
my %UpdateTypes;
my $UpdateHistories = 0;
while( <> )
{
    chomp;
    if ( /(\w*)\s*=\s*\"(.*)\"$/ )
    {
	my $Attr = $1;
	my $Value = $2;
	$AdInfo{$Attr} = $Value;

	if ( $Attr =~ /^UpdatesHistory/ )
	{
	    $UpdateHistories++;
	}
	elsif ( $Attr =~ /^Updates.*_(.*)/ )
	{
	    $UpdateTypes{$1} = 1;
	}
    }
    elsif ( /(\w*)\s*=\s*(.*)$/ )
    {
	my $Attr = $1;
	my $Value = $2;
	$AdInfo{$Attr} = $Value;

	if ( $Attr =~ /^UpdatesHistory/ )
	{
	    $UpdateHistories++;
	}
	elsif ( $Attr =~ /^Updates.*_(.*)/ )
	{
	    $UpdateTypes{$1} = 1;
	}
    }
    else
    {
	DumpHistories( \%AdInfo, \%UpdateTypes ) if ( $UpdateHistories );

	%AdInfo = ();
	%UpdateTypes = ();
	$UpdateHistories = 0;
    }

}

# Dump all history info
sub DumpHistories( $$ )
{
    my $Ad = shift;
    my $Types = shift;

    my $Name = "*Unknown*";
    my $MyType = "*Unknown*";

    # Pull out the name, etc.
    if ( exists $Ad->{Name} )
    {
	$Name = $Ad->{Name};
    }
    elsif ( exists $Ad->{Machine} )
    {
	$Name = $Ad->{Machine};
    }
    $MyType = $Ad->{MyType} if ( exists $Ad->{MyType} );
    my $NameStr = "*** Name/Machine = '$Name' MyType = '$MyType' ***";

    if ( DumpHistory( $NameStr, $Ad, undef ) )
    {
	$NameStr = "";
    }
    foreach my $Type ( keys %{$Types} )
    {
	DumpHistory( $NameStr, $Ad, $Type );
    }
}


# Dump history info
sub DumpHistory( $$$ )
{
    my $NameStr = shift;
    my $Ad = shift;
    my $Type = shift;
    my $UpdateInterval = $Config{UpdateInterval};

    my %Updates;
    if ( ! defined $Type )
    {
	if ( exists $Ad->{UpdatesTotal} )
	{
	    $Updates{Total} = $Ad->{UpdatesTotal};
	    $Updates{Seq} = $Ad->{UpdatesSequenced};
	    $Updates{Lost} = $Ad->{UpdatesLost};
	}
	if ( exists $Ad->{UpdatesHistory} )
	{
	    $Updates{Hist} = $Ad->{UpdatesHistory};
	}
    }
    else
    {
	if ( exists $Ad->{"UpdatesTotal_".$Type} )
	{
	    $Updates{Total} = $Ad->{"UpdatesTotal_".$Type};
	    $Updates{Seq} = $Ad->{"UpdatesSequenced_".$Type};
	    $Updates{Lost} = $Ad->{"UpdatesLost_".$Type};
	}
	if ( exists $Ad->{"UpdatesHistory_".$Type} )
	{
	    $Updates{Hist} = $Ad->{"UpdatesHistory_".$Type};
	}
    }

    #Anything to do?
    if ( ( scalar keys %Updates ) < 0 )
    {
	return 0;
    }

    # Extract the update interval if it exists...
    if ( exists $Ad->{UpdateInterval} && ( $Ad->{UpdateInterval} =~ /^\d/ ) )
    {
	$UpdateInterval = $Ad->{UpdateInterval};
    }

    # Dump the title string
    print "$NameStr\n" if ( $NameStr ne "" );
    print " Type: " . ( ( defined $Type ) ? "$Type" : "Main" ) . "\n";

    # Dump the "summary" info
    if ( exists $Updates{Total} )
    {
	my $LostPercent = "0.00";
	if ( $Updates{Seq} )
	{
	    $LostPercent =
		sprintf( "%.2f", 100.0 * $Updates{Lost} / $Updates{Seq} );
	}
	print "   Stats: Total=$Updates{Total}, Seq=$Updates{Seq}, ".
	    "Lost=$Updates{Lost} ($LostPercent\%)\n";
    }

    # Summary mode: we're done
    return 1 if ( $Config{SummaryOnly} );

    # Dump the "History" info
    if ( exists $Updates{Hist} )
    {
	my @History = ParseHex( $Updates{Hist} );
	my $UpdateNum = 0;
	my $LastStatus = -1;
	my $Repeats = 0;

	my $Time = time( );
	$Time = $Ad->{LastHeardFrom} if ( exists $Ad->{LastHeardFrom} );

	foreach my $Sample ( 0 .. $#History )
	{
	    if ( ( $Sample >= $Config{HistMin} ) &&
		 ( $Sample <= $Config{HistMax} ) )
	    {
		my $Status = $History[$Sample];
		if ( ( ! $Config{Long} ) && ( $Status == $LastStatus ) &&
		     ( $Sample < $#History )  )
		{
		    print "  ...\n" if ( ! $Repeats );
		    $Repeats++;
		}
		else
		{
		    $Repeats = 0;
		    my $TimeStr = "";
		    if ( $UpdateInterval && $Config{DumpTime} )
		    {
			$TimeStr = " @ " . localtime( $Time );
		    }
		    printf "  %4d%s: %s\n",
			$UpdateNum, $TimeStr,  $Status ? "Ok" : "Missed";
		}
		$LastStatus = $Status;
	    }
	    $Time -= $UpdateInterval;
	    $UpdateNum++;
	}
	print "\n";
    }

    return 1;
}

# Parse the hex string into array of ints
sub ParseHex( $ )
{
    my $Hex = shift;
    $Hex =~ s/^0x//i;

    my @History;
    my $Len = length( $Hex ) - 1;
    foreach my $Offset ( 0 .. $Len )
    {
	my $HexChar = substr( $Hex, $Offset, 1 );
	my $Value = hex( $HexChar );
	foreach my $Mask ( 0x8, 0x4, 0x2, 0x1 )
	{
	    my $Bit = ( $Mask & $Value ) ? 0 : 1;
	    push( @History, $Bit );
	}
    }
    @History;
}

# ******************************************************
# Dump out usage
# ******************************************************
sub Usage ( $$ )
{
    my $Help = shift;
    my $Unknown = shift;

    print "$Program: unknown argument: '$Unknown'\n"
	if ( $Unknown ne "" );
    printf "usage: " . $Help->{Usage} . "\n";
    print "use '-h' for more help\n";

    exit 1;

} # usage ()
# ******************************************************

# ******************************************************
# Dump out help
# ******************************************************
sub Help ( $ )
{
    my $Help = shift;
    my ($opt, $text);

    printf "usage: " . $Help->{Usage} . "\n";
    foreach $opt (sort {lc($a) cmp lc($b) } keys %{$Help->{Options}})
    {
	printf ("  %32s : %-40s\n", $opt, $Help->{Options}{$opt} );
    }
    exit 0;

} # help ()
# ******************************************************
