#!/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 some clients, we'll use product code to disambiguate
    # different products when available.
    # 288 = RSTestUK, 315 = VP Records UK, , 443 = Jungle Records, 461 = The Orchard UK
    if ( Common::RSApp::GetClientID() =~ /^(288|315|443|461)$/ )
    {
        $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";
    }
}

