#!/usr/bin/perl
#------------------------------------------------------------
# Copyright (C) 2006 RoyaltyShare, Inc.   All Rights Reserved
# $Id$
#------------------------------------------------------------
use strict;

#use warnings;

use Data::Dumper;
use Getopt::Std;
use Carp;
use File::Path;
use Encode;

use lib '/app/tools/common/lib';
use Common::Assert;
use Common::RSMath;

use lib '/app/tools/rps/lib';
use RPS::Statement::UKMechanical::McpsStatementFull;
use RPS::Statement::UKMechanical::McpsText;
use RPS::Payor::Payor;
use RPS::Product::Product;
use RPS::Mechanical::UK::UKMechanicalRun;
use RPS::DB::Item::McpsLicense;
use RPS::DB::Item::PriceLevel;
use RPS::DB::Item::ProductType;
use RPS::DB::Item::McpsStatementItem;
use RPS::CountryList;

use lib '/app/tools/rps/bin/statements';
use Formats;

$SIG{__DIE__} = \&Carp::confess;



# This determines how much output we spew forth to STDERR.
# The user can set this with the -V command-line argument.
#
my $gVerbosityLevel = 1;

# Parse the command-line, then create the CSV!
#
my %options;
parseCommandLine( \%options );
createStatementText( $options{clientID}, $options{runID},
    $options{outputPath} );

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

sub createStatementText
{
    my ( $clientID, $runID, $outFilePath ) = @_;

    # Instantiate the application singleton object.
    #
    my $appSingleton = Common::RSApp->new( clientID => $clientID );


    my $run = RPS::Mechanical::UK::UKMechanicalRun->new(
        ukMechanicalRunID => $runID,
    );
        

    # Create the output path, if it isn't there already.
    #
    if ( $outFilePath )
    {
        if ('/' ne substr($outFilePath, -1, 1))
        {
            $outFilePath .= '/';
        }
        if (! -d $outFilePath)
        {
            mkpath($outFilePath) or die "ERROR: Unable to create path $outFilePath: $!\n";
        }
    }

    my $fileName = RPS::Statement::UKMechanical::McpsText::StatementFileNameFromID($run->UKMechanicalRunID());

	# Open file for writing
	open(my $outFile,">".$outFilePath.$fileName);

		
	statement( $run, $outFile );	


    # All done, close it up.
    #
    close $outFile;
}


sub statement
{
    my ( $run, $outFile ) = @_;

    my $payorID = $run->PayorID();
    my $payor = RPS::Payor::Payor->new(payorID => $payorID);

    # StartTime format is 2009-05-08 11:42:51
    # We need to split it into two values.
    # One like this for date: DDMMYYYY
    # and one like this for time: HHMMSS
    my $startTime = $run->StartTime();
    
	my $dateCreated = formatDate($startTime);

	my $timeCreated = substr($startTime,11,2);
	$timeCreated .= substr($startTime,14,2);
	$timeCreated .= substr($startTime,17,2);	
	
	# Write out header stuff.
	my @headerFields = (
	    '00', 
	    formatAN($payor->Name(), 50), 
	    formatAN($payor->SupplierCode(), 12), 
	    'MCPS-AP1-3', 
	    $dateCreated, 
	    $timeCreated, 
	    formatRZ('', 50), # File supplier reference
	    formatAN('', 282) # Spare positions
	);
	
	my $header = join('', @headerFields);
#	print $outFile "$header\r\n";
    printLine($outFile, $header);
	
	# Add the country info
	#
	my $countries = RPS::DB::Item::McpsStatementItem->GetUniqueCountriesByRun($run->UKMechanicalRunID());
	my $countryCount = 0;
	
	while ($countries->hasNext())
	{
		my $data = $countries->next();
		my $countryCode = $data->country_code;
		my $countryName = RPS::CountryList::CountryNameFor($countryCode);
		my @countryFields = (
		    '02', 
		    formatAN($countryCode, 5), 
		    formatAN($countryName, 50), 
		    'ISO     ', 
		    formatAN('', 355) # Spare positions
		);
	    my $countryRow = join('', @countryFields);
#	    print $outFile "$countryRow\r\n";
        printLine($outFile, $countryRow);
	    $countryCount++;
	}	
	
	# Add the country count
	my @countryCountFields = (
	    '04', 
	    formatRZ($countryCount, 6), 
	    formatAN('', 412) # Spare positions
	);
    my $countryCountRow = join('', @countryCountFields);
#    print $outFile "$countryCountRow\r\n";
    printLine($outFile, $countryCountRow);
	
	
	# Let's figure out the start and end dates based on the quarter.
	#
	my ($startDate, $endDate) = getDatesForQuarter($run->Quarter(), $run->Year());


    # Here's where we need to do a loop for each mcps scheme.
    # Let's create an array of the statement ids for this run
    # in this order: AP1, DVD1, AVP.
    
    my @statementIDs;
    my $runID = $run->UKMechanicalRunID();
    my $ap1StatementID = RPS::DB::Item::McpsStatement->GetIDByRunAndMcpsScheme($runID,'AP1');
    push(@statementIDs, $ap1StatementID);
    my $dvd1StatementID = RPS::DB::Item::McpsStatement->GetIDByRunAndMcpsScheme($runID,'DVD1');
    push(@statementIDs, $dvd1StatementID);
    my $avpStatementID = RPS::DB::Item::McpsStatement->GetIDByRunAndMcpsScheme($runID,'AVP');
    push(@statementIDs, $avpStatementID);

    my $batchNum = 1;

    foreach my $statementID (@statementIDs)
    {
        # Instantiate the McpsStatement object.
        # This object will contain most of the info we need to output the statement document.
        #
        my $statement = RPS::Statement::UKMechanical::McpsStatementFull->new(
            mcpsStatementID => $statementID,
        );    
        
        # Add the record company info... also known as the payor info	
        
        my $supplierCode;
        if ($statement->McpsScheme eq 'AP1')
        {
            $supplierCode = $payor->AP1SupplierCode();
        }	
        elsif ($statement->McpsScheme eq 'DVD1')
        {
            $supplierCode = $payor->DVD1SupplierCode();
        }	
        elsif ($statement->McpsScheme eq 'AVP')
        {
            $supplierCode = $payor->AVPSupplierCode();
        }	                
        
        my @recordCompanyFields = (
            '10', 
            formatRZ($batchNum, 4), 
            formatAN($payor->Name(), 30), 
            formatAN($supplierCode, 12), 
            formatDate($startDate), 
            formatDate($endDate), 
            formatAN('', 30), # Label would go here if we were batching sales by label (which we are not)
            formatAN('', 10), # Record company batch ref
            formatAN('', 316) # Spare positions
        );
        my $recordCompanyRow = join('', @recordCompanyFields);
#        print $outFile "$recordCompanyRow\r\n";    
        printLine($outFile, $recordCompanyRow);
        
        
        # Now the meat of the statement - sales, adjustments, deletions, etc.
        # We need to display counts for all of the later, so we'll capture them here.
        my ($saleCount, $saleTotal, $adjustmentCount, $adjustmentTotal, $deletionCount, $deletionTotal, $deletionAdjustmentCount, $deletionAdjustmentTotal) = records( $statement, $run, $payorID, $outFile );  
     
     
        # Now the batch summary, which is similar to the record company info,
        # but with some extra data thrown in at the end.	
        
        my @batchSummaryFields = (
            '19', 
            formatRZ($batchNum, 4), 
            formatAN($payor->Name(), 30), 
            formatAN($payor->SupplierCode(), 12), 
            formatDate($startDate), 
            formatDate($endDate), 
            formatAN('', 30), # Label would go here if we were batching sales by label (which we are not)
            formatRZ($saleCount, 6), # 11
            formatRZ($saleTotal, 14),
            sign($saleTotal),
            formatRZ($adjustmentCount, 6), # 12
            formatRZ($adjustmentTotal, 14),
            sign($adjustmentTotal),
            formatRZ($deletionCount, 6), # 13
            formatRZ($deletionTotal, 14),
            sign($deletionTotal),   
            formatRZ($deletionAdjustmentCount, 6), # 14
            formatRZ($deletionAdjustmentTotal, 14),
            sign($deletionAdjustmentTotal), 
            formatRZ(0, 20), # 15
            '+',    
            formatRZ(0, 20), # 16
            '+',   
            formatRZ(0, 20), # 17
            '+',               
            formatRZ(0, 20), # 18
            '+',                         
            formatAN('', 158) # Spare positions
        );
        my $batchSummaryRow = join('', @batchSummaryFields);
#        print $outFile "$batchSummaryRow\r\n";  
        printLine($outFile, $batchSummaryRow);
        
        $batchNum++; 
    }
    
    
    # This is the end... 
	my @footerFields = (
	    '99', 
	    formatAN($payor->Name(), 50), 
	    formatAN($payor->SupplierCode(), 12), 
	    'MCPS-AP1-3', 
	    $dateCreated, 
	    $timeCreated, 
	    formatRZ('', 50), # File supplier reference
	    '000003', # Number of batches - will always be 3 for now (one for each mcps scheme)
	    formatAN('', 276) # Spare positions
	);
	
	my $footer = join('', @footerFields);
#	print $outFile "$footer\r\n";    
    printLine($outFile, $footer);

}


sub records
{
    my ( $statement, $run, $payorID, $outFile ) = @_;

    my $statementID = $statement->McpsStatementID(); 

    # We'll change this as we go down through the different record types
    my $type = 'sales';
    my $saleCount;
    my $saleTotal;
    
    # We need to list all valid mcps products for this payor, not just the ones with sales...
    # So, here we go.
    my $mcpsLicenses = RPS::DB::Item::McpsLicense->GetByPayorID($payorID);
       
	while ($mcpsLicenses->hasNext())
	{
		my $mcpsLicense = $mcpsLicenses->next();  

		my $mcpsLicenseID = $mcpsLicense->mcps_license_id;
		
		# Let's see if we have any sales for this combination of statement id and mcps license id.
		# If we do, we add a row for each.
		# If we don't, we'll add one row with a zero for sales.
        my $statementItems = RPS::DB::Item::McpsStatementItem->GetSalesByStatementAndLicense($statementID, $mcpsLicenseID);
        
        if ($statementItems)
        
        {
            while ($statementItems->hasNext())
	        {
	            # Add a row for each statement item with sales
	            my $statementItem = $statementItems->next();
        
                addRecordRow($type, $mcpsLicense, $run, $outFile, $statementItem); 
                $saleCount++;
                $saleTotal += $statementItem->final_net_units;
            } 
        
        } 
        else
        {
            # Add a row with 0 sales
            addRecordRow($type, $mcpsLicense, $run, $outFile);    
            $saleCount++;       
        }		
    }  
    
    # And now for the adjustments.
    # But not the adjustments to deleted products, those come later.
    $type = 'adjustment';
    my $adjustmentCount;
    my $adjustmentTotal;
    
    my $adjustments = RPS::DB::Item::McpsStatementItem->GetAdjustmentsByStatement($statementID);
    
    while ($adjustments->hasNext())
    {
        my $mcpsLicense;
        
        # Add a row for each adjustment
        my $adjustment = $adjustments->next();    
        addRecordRow($type, $mcpsLicense, $run, $outFile, $adjustment); 
        $adjustmentCount++;
        $adjustmentTotal += $adjustment->final_net_units;
    }
    
    # And now for the deletions.
    # But still not the adjustments to deleted products.
    $type = 'deletion';
    my $deletionCount;
    my $deletionTotal;
    
    my $deletions = RPS::DB::Item::McpsStatementItem->GetDeletionsByStatement($statementID);
    
    while ($deletions->hasNext())
    {
        my $mcpsLicense;
        
        # Add a row for each deletion
        my $deletion = $deletions->next();    
        addRecordRow($type, $mcpsLicense, $run, $outFile, $deletion); 
        $deletionCount++;
        $deletionTotal += $deletion->final_net_units;
    }    

    # And now, at long last, I give you the deletion adjustments.
    #
    $type = 'deletion_adjustment';
    my $deletionAdjustmentCount;
    my $deletionAdjustmentTotal;
    
    my $deletionAdjustments = RPS::DB::Item::McpsStatementItem->GetDeletionAdjustmentsByStatement($statementID);
    
    while ($deletionAdjustments->hasNext())
    {
        my $mcpsLicense;
        
        # Add a row for each deletion adjustment
        my $deletionAdjustment = $deletionAdjustments->next();    
        addRecordRow($type, $mcpsLicense, $run, $outFile, $deletionAdjustment); 
        $deletionAdjustmentCount++;
        $deletionAdjustmentTotal += $deletionAdjustment->final_net_units;
    } 
  
  return ($saleCount, $saleTotal, $adjustmentCount, $adjustmentTotal, $deletionCount, $deletionTotal, $deletionAdjustmentCount, $deletionAdjustmentTotal);
}


sub addRecordRow
{
    my ($type, $mcpsLicense, $run, $outFile, $statementItem) = @_;

    # Let's declare all the variables first, because they may come from different places.
    #
    my $countryCode;
    my $grossUnits;
    my $netUnits;
    my $priorUnits;
    my $retentions;
    my $tvRetentions;
    my $promoUnits;
    my $returnUnits;
    my $finalNetUnits;
    my $price;
    my $priceType;
    my $comments;
    my $deletionDate;
    my $catalogNumber;
    my $albumTitle;
    my $artistName;
    my $labelName;
    my $mcpsID;
    my $productName;
    my $releaseDate;
    my $productCode;
      
    if ($statementItem)
    {
        # If we were passed a statement item, then it contains all the data we need. 
         
        $countryCode = $statementItem->country_code;    
        $grossUnits = $statementItem->gross_units;
        $netUnits = $statementItem->net_units;
        $priorUnits = $statementItem->prior_units;
        $retentions = $statementItem->retentions;
        $tvRetentions = $statementItem->tv_retentions;
        $promoUnits = $statementItem->promo_units;
        $returnUnits = $statementItem->return_units;
        $finalNetUnits = $statementItem->final_net_units;
        $price = formatPrice($statementItem->price);
        $priceType = $statementItem->mcps_price_type;
        $comments = $statementItem->comments;
        $deletionDate = $statementItem->deleted_date;
        $catalogNumber = $statementItem->catalog_number;
        $albumTitle = $statementItem->album_title;
        $artistName = $statementItem->artist_name;
        $labelName = $statementItem->label_name;
        $mcpsID = $statementItem->mcps_id;
        $productName = productCodeToName($statementItem->product_type_id);
        $releaseDate = $statementItem->release_date;        
        $productCode = $statementItem->product_code;
    }
    else
    {
        # If a statement item was not passed in, we'll need to scrounge up whatever data we can.
    
	    my $product = RPS::DB::Item::Product->Lookup(product_id => $mcpsLicense->product_id);
	    my $album = RPS::DB::Item::Album->Lookup(album_id => $product->asset_id);    
    
        # album
        $catalogNumber = $album->catalog_number;
        $albumTitle = $album->title;
        $artistName = artistIDToName($album->artist_id);
        $labelName = labelIDToName($album->label_id);
        
        # mcps license
        $mcpsID = $mcpsLicense->mcps_id;
 
        # product
        $productName = productCodeToName($product->product_type_id);
        $releaseDate = $product->release_date;
        $productCode = $product->product_code;
    } 

    # for VP Records UK (and RSTestUK), we'll use product code when available to disambiguate
    # different products when available.
    if (Common::RSApp::GetClientID() =~ /^(288|315)$/)  # 228 = RSTestUK, 315 = VP Records UK
    {
        $catalogNumber = $productCode if ($productCode);
    }

    $catalogNumber =~ s/[^A-Za-z0-9]//g;    # MCPS no likey non-alphanumeric for catalog number

    # run
	my ($startDate, $endDate) = getDatesForQuarter($run->Quarter(), $run->Year());

    my @recordFields = (
        # This is the list of fields that are the same for all record types
        #
        formatAN($catalogNumber, 35), 
        formatAN($mcpsID, 12),
        formatAN($albumTitle, 40),
        formatAN($artistName, 30),
        formatAN($labelName, 30),
        formatAN($productName, 10),
        formatReleaseDate($releaseDate),
        formatAN($countryCode, 5),
        formatDate($startDate),
        formatDate($endDate),
        formatRZ($grossUnits, 8), 
        sign($grossUnits),
        formatRZ($netUnits, 8),
        sign($netUnits),
        'NES',
        formatRZ($priorUnits, 8),
        '+', # Prior units are always negative, which (of course) means we will always display the + here...
        'PRI',        
        formatRZ($retentions, 8),
        sign($retentions),
        'RET',   
        formatRZ($tvRetentions, 8),
        sign($tvRetentions),
        'TVR', 
        formatRZ($promoUnits, 8),
        '+', # Always positive
        'FRE',
        formatRZ($returnUnits, 8),
        '+', # Always positive
        'RTN',    
        formatRZ(0, 8), # We don't have any data on faulty units at this time.
        '+', # Default is positive
        'FAU',   
        formatRZ(0, 8), # We don't have any data on equivalent unit quantity returns at this time.
        '+', # Default is positive
        'XCT',
        formatRZ(0, 8), # We don't have any data on other units at this time.
        '+', # Default is positive
        'OTH',     
        formatRZ(0, 8), # This is the myster one that is really just a placeholder.
        '+', # Default is positive
        '   ',                       
        formatRZ($finalNetUnits, 8),
        sign($finalNetUnits),    
        'R', # Retail is the only allowed sale type    
        formatRZ($price, 12),         
        formatAN($priceType, 1), 
        'GBP', # Hard-coding to GBP for now...   
        '02', # Specifying 2 decimal places for price 
        formatAN('', 10), # Record Company Transaction Reference - not currently used                          
    );
    
    # This is the part that is unique to each record type.
    #
    
    if ($type eq 'sales') 
    {
        unshift(@recordFields, '11');
        push(@recordFields, formatAN('', 67)), # Spare positions                
    }
    elsif ($type eq 'adjustment')
    {
        unshift(@recordFields, '12');
        push(@recordFields, formatAN($comments, 40)), # Reason for adjustment
        push(@recordFields, formatAN('', 27)), # Spare positions      
    }
    elsif ($type eq 'deletion')
    {
        unshift(@recordFields, '13');
        push(@recordFields, formatDate($deletionDate)), 
        push(@recordFields, formatAN('', 59)), # Spare positions      
    }   
    elsif ($type eq 'deletion_adjustment')
    {
        unshift(@recordFields, '14');
        push(@recordFields, formatDate($deletionDate)), 
        push(@recordFields, formatAN($comments, 40)), # Reason for adjustment
        push(@recordFields, formatAN('', 19)), # Spare positions      
    }         
    
    my $recordRow = join('', @recordFields);

    printLine($outFile, $recordRow);
    
}    



# Formatting functions
#

sub getDatesForQuarter
{
    my ($quarter, $year) = @_;
    
	my $startDate = $year . "-";
	my $endDate = $year . "-";
	
	my $startQuarter;
	my $endQuarter;
	
	if ($quarter == 1)
	{
	    $startQuarter = "01-01";
	    $endQuarter = "03-31";
	}
	elsif ($quarter == 2)
	{
	    $startQuarter = "04-01";
	    $endQuarter = "06-30";
	}	
	elsif ($quarter == 3)
	{
	    $startQuarter = "07-01";
	    $endQuarter = "09-30";
	}	
	elsif ($quarter == 4)
	{
	    $startQuarter = "10-01";
	    $endQuarter = "12-31";
	}		
	
	$startDate .= $startQuarter;
	$endDate .= $endQuarter;  
	
	return($startDate, $endDate);     
}

sub formatAN
{
    #  Left-adjusted alphanumeric field, right space-filled
    #
    my ($string, $length) = @_;

    my $padded = pack("A$length",$string);

    return $padded;
}

sub formatRZ
{
    #  Right-adjusted numeric field, left zero filled, unpacked, unsigned.
    #  (When a numeric field can be positive or negative the "+" or "-" sign will be in a separate field.)
    my ($string, $length) = @_;
    
    # Remove the negative sign, if there is one.
    # 
    $string =~ s/-//;

    my $padded= substr("0"x$length.$string,-$length);

    return $padded;
}

sub formatDate
{
    my ($date) = @_;
    
    # Dates are always formatted the British way (DDMMYYYY), since these statements are just for MCPS.
    #
    
	my $formattedDate = substr($date,8,2);
	$formattedDate .= substr($date,5,2);
	$formattedDate .= substr($date,0,4);   
	return $formattedDate;    
}


sub formatReleaseDate
{
    my ($date) = @_;

    # Release date needs to return a two digit year, as opposed to 4 for every other date...
    #
    
	my $formattedDate = substr($date,8,2);
	$formattedDate .= substr($date,5,2);
	$formattedDate .= substr($date,2,2);   
	return $formattedDate;    
}

sub sign
{
    my ($value) = @_;
    
    # Default value is +, which is used for blank and zero.
    #
    my $sign = "+";
    if ($value && $value < 0)
    {
        $sign = "-";       
    }
    return $sign;
}    

sub formatPrice
{
    my ($value) = @_;

    # So we're going to round the price to two decimal places, 
    # and then remove the decimal (because that is what MCPS wants us to do.
    #
    
    my $precision = 2;
    # This won't work for negative numbers.  
    # But in this script, I will only be feeding in positive numbers anyway.
    #
    my $rVal = substr( $value + ( '0.' . '0' x $precision . '5' ), 0, $precision + 
    + length(int($value)) + 1 );

    # We don't want any decimals
    $rVal =~ s/\.//;

    return $rVal;
}

# These functions map ids to their text representations.
# Basically, the first time we ask for a particular id->string mapping, we
# will query the correct database, and build a hash.  Subsequent calls just
# hit the hash.
#
# Because these tables are so similar, I abstracted the guts into the _genericMapAccessor
# method (to save myself some typing).
#
my %idMaps;

sub _genericMapAccessor
{
    my ( $collectionAccessor, $idName, $id ) = @_;

    if ( !defined $idMaps{$collectionAccessor} )
    {
        $idMaps{$collectionAccessor} = {};

       # This is a very naughty thing to do, but it works great in this context.
       #
       #        no strict 'refs';
       #        my $c = &$collectionAccessor();
       #        use strict 'refs';
        my $c = $collectionAccessor->GetAll();

        while ( my $item = $c->next() )
        {
            if ( $idName eq 'label_id' )
            {
                $idMaps{$collectionAccessor}{ $item->$idName() } = $item->label_name;
            }            
            elsif ( $idName ne 'product_type_id' )
            {
                $idMaps{$collectionAccessor}{ $item->$idName() } = $item->name;
            }
            else
            {
                $idMaps{$collectionAccessor}{ $item->$idName() } =
                  $item->description;
            }
        }
    }

    return $idMaps{$collectionAccessor}{$id};
}



sub artistIDToName
{
    my ($id) = @_;
    return _genericMapAccessor( 'RPS::DB::Item::Artist', 'artist_id',
        $id );
}

sub labelIDToName
{
    my ($id) = @_;
    return _genericMapAccessor( 'RPS::DB::Item::Label', 'label_id',
        $id );
}

sub priceLevelIDToName
{
    my ($id) = @_;
    return _genericMapAccessor( 'RPS::DB::Item::PriceLevel', 'price_level_id',
        $id );
}

sub productCodeToName
{
    my ($id) = @_;
    return _genericMapAccessor( 'RPS::DB::Item::ProductType', 'product_type_id',
        $id );
}


sub printLine
{
    my ($outFile, $line) = @_;

    # Downcast whatever we've got to ASCII. Any byte > 127 gets
    # substituted with a space character (which is mostly what
    # we were doing already except for high-order 0xe? 0xf? bytes).
    #
    # There's a CPAN module named Text::Unidecode that does
    # some nice things like diacritical dropping, but it can
    # also change the length of things (like ½ to 1/2), so
    # not great for fixed record requirements as we have here.
    #
    # Could roll our own at some point...
    #
    # Or something we do at a field level before we fix length
    #
    print $outFile encode('ascii', $line, sub {' '}) . "\r\n";
}


#
# Boring script stuff below...
#

sub parseCommandLine
{
    my ($settings) = @_;

    my %opt;
    getopts( 'r:c:p:V:', \%opt );

    if ( !$opt{r} || !$opt{c} )
    {
        usage();
        exit(1);
    }
    $settings->{runID}           = $opt{r};
    $settings->{clientID}        = $opt{c};
    $settings->{outputPath}      = $opt{p};

    if ( defined $opt{V} )
    {
        $gVerbosityLevel = $opt{V};
    }
}

sub usage
{
    print STDERR
"\nusage: $0 -c <client_id> -r <run_id> [-p <output path>]\n";
    print STDERR "\n";
    print STDERR "Arguments:\n";
    print STDERR
      "\t-c <client_id>\t\t\tThe client_id of the client to process\n";
    print STDERR
"\t-r <run_id>\tThe id of the uk mechanical run to convert to text\n";
    print STDERR
"\t-p <output path>\tThe output path. Optional. If not provided, we'll make one up\n";
}

sub report
{
    my ( $string, $verbosity ) = @_;
    $verbosity = 1 unless defined $verbosity;

    if ( $gVerbosityLevel >= $verbosity )
    {
        print STDERR $string . "\n";
    }
}

