#
# Copyright (c) 2005-2009 Nokia Corporation and/or its subsidiary(-ies).
# All rights reserved.
# This component and the accompanying materials are made available
# under the terms of the License "Eclipse Public License v1.0"
# which accompanies this distribution, and is available
# at the URL "http://www.eclipse.org/legal/epl-v10.html".
#
# Initial Contributors:
# Nokia Corporation - initial contribution.
#
# Contributors:
#
# Description: 
#

package Util;
use strict;

$Util::info = 1; # constant
$Util::detailed = 2; # constant
$Util::diagnostic = 3; # constant

$Util::warnings = 1; # constant
$Util::nowarnings = 0; # constant

##############################################
## the object constructor                   ##
##############################################
sub new($;$$)
{
    my $self = {};
    
    shift @_;

    bless($self);

    $self->argcount(3, @_);

    $self->{LEVEL} = shift @_; # Used to set the logging level
    $self->{WARNINGS} = shift @_; # log flag
    $self->{PREFIX} = shift @_; # log prefix
    
    return $self;
}

##############################################
## Class methods to access per-object data  ##
##                                          ##
##############################################

#
# Check expected number of args
#
sub argcount(@)
{
    my $self = shift;
    die "self not defined\n" if (not defined($self));

    my ($expected_num, @args) = @_;
    my $actual_num = scalar(@args);
    
    if ($actual_num != $expected_num)
    {
	$self->error("Expected $expected_num args, got $actual_num");
    }
}

#
# Set the logging level
#
sub level
{
    my $self = shift;
    die "self not defined\n" if (not defined($self));
 
    if (@_) { $self->{LEVEL} = shift }
    return $self->{LEVEL};
}

#
# 
#
sub diagnostic
{
    my $self = shift;
    die "self not defined\n" if (not defined($self));

    if (@_) { $self->{DIAGNOSTIC} = shift }
    return $self->{LEVEL} == $Util::diagnostic;
}

#
# Set/Get the prefix
#
sub prefix
{
    my $self = shift;
    die "self not defined\n" if (not defined($self));

    if (@_) { $self->{PREFIX} = shift }
    return $self->{PREFIX};
}

#
# Log errors to stderr
#
sub error(@)
{
    my $self = shift;
    die "self not defined\n" if (not defined($self));

    my $prefix = $self->prefix();
    $prefix = "${prefix}Error: ";
    
    if (($? >> 8) != 0) { die "${prefix}@_: $!\n" }

    die "${prefix}@_\n";
}

#
# Log if the warnings flag is set
#
sub warning(@)
{
    my $self = shift;
    die "self not defined\n" if (not defined($self));

    return $self->{WARNINGS} if (not @_); 

    my $prefix = $self->prefix();

    print STDERR "${prefix}Warning: @_\n" if ($self->{WARNINGS});
}

#
# Log info to stdout dependent on log level
#
sub info(@)
{
    my $self = shift;
    die "self not defined\n" if (not defined($self));

    return $Util::info <= $self->{LEVEL} if not @_;
    
    my $prefix = $self->prefix();
    
    print STDOUT "${prefix}@_" if ($Util::info <= $self->{LEVEL});
}

#
# Log info to stdout dependent on log level
#
sub detailed(@)
{
    my $self = shift;
    die "self not defined\n" if (not defined($self));

    return $Util::detailed <= $self->{LEVEL} if not @_;
    
    my $prefix = $self->prefix();
    
    print STDOUT "${prefix}@_" if ($Util::detailed <= $self->{LEVEL});
}

# 
# Generate a unique temporary name 
#
sub tmp_name(@)
{
    my $self = shift;
    die "self not defined\n" if (not defined($self));
 
    $self->detailed("Generate temporary name based on @_\n");
    
    use Digest::MD5;
    my $md5 = Digest::MD5->new;
    
    use Cwd;
    my $path = Cwd::cwd();
    my $template = "${path}/XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX";

    use File::Temp;
    (undef, my $tmpfilename) = File::Temp::tempfile($template, OPEN => 0);
    
    $md5->add($tmpfilename);
    $md5->add(time); # time returns number of non-leap seconds since the epoc
    
    foreach (@_)
    {
	$md5->add($_);
    }
    
    return $md5->hexdigest;
}

#
# Generate a digest of the certstore.  Because the digest is used to
# construct the temporary filename name, the Symbian OS limits the
# size of path and file names, Md5 is used.
#
sub certstore_digest($)
{
    my $self = shift;
    die "self not defined\n" if (not defined($self));

    $self->argcount(1, @_);

    my $input_file = shift @_;
  
    $self->detailed("Generate digest from '$input_file'\n");
    
    open(FILEREAD, "< $input_file") 
	or $self->error("Could not open $input_file!");
    
    binmode FILEREAD;
    
    use Digest::MD5;
    
    my $md5 = Digest::MD5->new;
    
    $md5->addfile(*FILEREAD);
    
    close FILEREAD;
    
    return $md5->hexdigest;
}

#
# Generate a timestamp
#
sub timestamp()
{
    my $self = shift;
    die "self not defined\n" if (not defined($self));

    $self->argcount(0, @_);

    (my $sec, my $min, my $hour, my $mday, my $mon,
     my $year, my $wday, my $yday, my $isdst) = localtime(time);
    
    return sprintf("%4d-%02d-%02d @ %02d:%02d:%02d", $year+1900,
		   $mon+1, $mday, $hour, $min, $sec);
}

# Execute a command.  For tools such as abld, which don't return an
# error status if the build fails, we must grep stdout and stderr for
# errors.
sub invoke(@)
{
    my $self = shift;
    die "self not defined\n" if (not defined($self));

    $self->info("Invoking: @_\n");

    # Redirect stderr into stdout.
    open(SAVEOUT, "@_ 2>&1 |") or $self->error("couldn't invoke @_");

    my @out = <SAVEOUT>;
    
    close SAVEOUT;
    
  
    # Check for errors in the output
    $self->detailed("Interpreting results of previous invocation\n");

    my @warnstrings = ();

    my $BuildSystemVersion = $ENV{'SBS_VERSION'};
 
 	if ($BuildSystemVersion == 2) 
 	{
 		foreach (@out)
 		{
 		$self->error(@out) if (/errors:\s[^0]/i);
 		
 		# build list of warnings
 		push (@warnstrings, $_) if ($self->warning and /warnings:\s[^0]/i);
 		}
 	}
 	else 
 	{
 		foreach (@out)
 		{
 		$self->error(@out) if (/Error/i);
 		
 		# build list of warnings
 		push (@warnstrings, $_) if ($self->warning and /Warning/i);
 		}
 	}

    # Issue warnings only if detailed logging has not been enabled
    # since if it has then all warnings will be printed anyway
    if (not $self->detailed()) { $self->warning(@warnstrings) }
    else { $self->detailed(@out) }

#    log->warning(@warnstrings) if (@warnstrings and $verbose < $detailed);
}

1;  # so the require or use succeeds
