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

# select * from mechanical_statement_row left join mechanical_statement_cell on (mechanical_statement_row.mechanical_statement_row_id=mechanical_statement_cell.mechanical_statement_row_id) where mechanical_statement_id=15 and mechanical_statement_row.payee_index >= 10 and mechanical_statement_row.payee_index <= 20;

#use warnings;

use Data::Dumper;
use Getopt::Std;
use Carp;

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

use lib '/app/tools/rps/lib';
use RPS::DB::Item::Album;
use RPS::DB::Item::Channel;
use RPS::DB::Item::ContractRateType;
use RPS::DB::Item::MechanicalStatementCrossedLicenseTransaction;
use RPS::DB::Item::IncomeSource;
use RPS::DB::Item::MechanicalRun;
use RPS::DB::Item::MechanicalStatement;
use RPS::DB::Item::MechanicalStatementCell;
use RPS::DB::Item::MechanicalStatementItem;
use RPS::DB::Item::MechanicalStatementLicense;
use RPS::DB::Item::MechanicalStatementLicenseTransaction;
use RPS::DB::Item::MechanicalStatementPublisher;
use RPS::DB::Item::MechanicalStatementPublisherTransaction;
use RPS::DB::Item::MechanicalStatementRow;
use RPS::DB::Item::MechanicalStatementTrack;
use RPS::DB::Item::MechanicalStatementTransaction;
use RPS::DB::Item::Payor;
use RPS::DB::Item::PriceLevel;
use RPS::DB::Item::Product;
use RPS::DB::Item::ProductType;
use RPS::DB::Item::Publisher;
use RPS::DB::Item::Region;
use RPS::DB::Item::StatRate;

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

# !!! ENABLE DEBUGGING
#
#$ENV{DEV_SERVER} = 1;

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

#
# A lot of this code is taken from this tutorial: http://rick.measham.id.au/pdf-api2/
#

# These constants will make it easier to convert between 'regular' units
# and postscript points.
# There are 72 postscript points in an inch, and 25.4 millimeters in an inch.
# All the PDF::API2 methods expect their size or coordinate arguments to be in points.
#
use constant mm => ( 25.4 / 72 );
use constant in => ( 1 / 72 );
use constant pt => 1;

# The standard page dimensions.  Note that we're creating _landscape_ docs.
#
use constant kPageHeight => ( 8.5 / in );
use constant kPageWidth  => ( 11 / in );

# The margins of the page.
# Remember that (0,0) in PDF coordinates is the bottom-left of the page.
#
use constant kLeftMargin   => 0.5 / in;
use constant kBottomMargin => 0.5 / in;
use constant kRightMargin  => ( kPageWidth - ( 0.5 / in ) );
use constant kTopMargin    => ( kPageHeight - ( 0.5 / in ) );

# We are going to keep track of every page we create, so that we
# can go back later and add page numbers
#
my @gPages;

# We'll store a hashref with font info here, once we have a PDF object to work with.
#
my $font;

# We'll store the bookmarks here if I can ever figure out how this works
#
my $outline_root;

# 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 PDF!
#
my %options;
parseCommandLine( \%options );
createStatement( $options{clientID}, $options{mechanicalStatementID} );

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

sub createStatement {
    my ( $clientID, $mechanicalStatementID ) = @_;

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

    # We need to get rid of any old data hanging out for this statement.
    #
    Common::Log::DebugMem('deleting old statement data');
    RPS::DB::Item::MechanicalStatementCell->DeleteByMechanicalStatementID($mechanicalStatementID);
    RPS::DB::Item::MechanicalStatementRow->DeleteByMechanicalStatementID($mechanicalStatementID);

    # Grab the statement item and off we go.
    #
    my $statementItem = RPS::DB::Item::MechanicalStatement->Lookup( mechanical_statement_id => $mechanicalStatementID );
    Common::Log::DebugMem('instantiated RPS::DB::Item::MechanicalStatement');

    # !!! So, all I really need to keep track of this stuff properly is
    # the indexes.

    my ( $payeeIndex, $fullIndex );

    # Each section of the statement will have its own subroutine.
    # Note that I am passing the two indexes by reference, so we can
    # increment them as we go.
    #
    Common::Log::DebugMem('calling header');
    header( $statementItem, \$payeeIndex, \$fullIndex );
    Common::Log::DebugMem('calling summary');
    summary( $statementItem, \$payeeIndex, \$fullIndex );

    # Set up the header
    #
    addRow( $statementItem, undef, \$fullIndex, 'LEFT_TEXT', 'BOLD', { value => 'Current Period Details:' } );

    addRow(
        $statementItem,
        undef,
        \$fullIndex,
        'DETAILS_TABLE',
        'UNDERLINE OVERLINE BOLD REPEAT',
        { value => undef },
        { value => 'Track Title', class => 'BOLD' },
        { value => 'Album Title', class => 'BOLD' },
        { value => 'UPC', class => 'BOLD CENTER' },
        { value => 'Product Config', class => 'BOLD CENTER' },
        { value => 'Region', class => 'BOLD' },
        { value => 'Rate Period', class => 'BOLD CENTER' },
        { value => 'Base Rate', class => 'BOLD RIGHT' },
        { value => '% of Rate', class => 'BOLD RIGHT' },
        { value => 'Share', class => 'BOLD RIGHT' },
        { value => 'Net Rate', class => 'BOLD RIGHT' },
        { value => 'Gross Units', class => 'BOLD RIGHT' },
        { value => '% of Sales', class => 'BOLD RIGHT' },
        { value => 'Free Goods', class => 'BOLD RIGHT' },
        { value => 'Misc.', class => 'BOLD RIGHT' },
        { value => 'Reserves', class => 'BOLD RIGHT' },
        { value => 'Liquidations', class => 'BOLD RIGHT' },
        { value => 'Returns', class => 'BOLD RIGHT' },
        { value => 'Carryover', class => 'BOLD RIGHT' },
        { value => 'Net Units', class => 'BOLD RIGHT' },
        { value => 'Amount Due', class => 'BOLD RIGHT' },
    );

    # Determine if this is an admin or regular publisher.
    my $publisherItem = RPS::DB::Item::Publisher->Lookup( publisher_id => $statementItem->publisher_id );

    if ( $publisherItem->is_admin || $publisherItem->is_agency ) {

        # Loop through all publishers for admins.
        #
        my $publisherList = RPS::DB::Item::MechanicalStatementPublisher->GetSortedByMechanicalStatementID($mechanicalStatementID);
        Common::Log::DebugMem('got statement publisher list');
        while ( my $subPublisherItem = $publisherList->next() ) {

            # !!! wait, we pass in 'publisher'?
            # Well, we pass in 'mechanical_statement_publisher'.  It's a little different.
            Common::Log::DebugMem('calling details w/sub publisher');
            details( $subPublisherItem, \$payeeIndex, \$fullIndex );
        }
    } else {

        # Not an admin, so just do this once.
        Common::Log::DebugMem('calling details');
        details( $statementItem, \$payeeIndex, \$fullIndex );
    }
}

sub header {
    my ( $statement, $payeeIndexRef, $fullIndexRef ) = @_;

    Common::Log::DebugMem(' entering header');

    # Step 1 - Put the text 'United States Mechanical Royalty Statement' at the top of the page.
    #
    addRow( $statement, $payeeIndexRef, undef, 'STATEMENT_HEADER', undef, { value => 'United States Mechanical Royalty Statement' } );

    # Now print the run label. Which means we need to fetch the run...
    #
    my $run = RPS::DB::Item::MechanicalRun->Lookup( mechanical_run_id => $statement->mechanical_run_id );

    addRow( $statement, $payeeIndexRef, $fullIndexRef, 'LEFT_TEXT', 'BOLD',  { value => $run->label } );
    addRow( $statement, $payeeIndexRef, $fullIndexRef, 'BLANK',     'BLANK', { value => '' } );

    # Add a blank line.
    #
    addRow( $statement, $payeeIndexRef, $fullIndexRef, 'BLANK', 'BLANK', { value => '' } );

    # Add links to download PDF and text versions (for full statement only)
    #
    addRow(
        $statement,
        undef,
        $fullIndexRef,
        'LEFT_TEXT',
        undef,
        {
            value => '<img src="/production/images/pdficon_small.gif" class="pdfIcon" />Publisher View',
            link  => '/rps/statement?c=pdf_mechanical&MechanicalStatementID=' . $statement->mechanical_statement_id,
            title => 'Download PDF'
        }
    );

    addRow( $statement, undef, $fullIndexRef, 'BLANK', 'BLANK', { value => '' } );

    addRow(
        $statement,
        undef,
        $fullIndexRef,
        'LEFT_TEXT',
        undef,
        {
            value => '<img src="/production/images/icons/office/Document-(16x16).gif" class="pdfIcon" />Electronic Format',
            link  => '/rps/statement?c=text_mechanical&MechanicalStatementID=' . $statement->mechanical_statement_id,
            title => 'Download Electronic Format'
        }
    );

    addRow( $statement, undef, $fullIndexRef, 'BLANK', 'BLANK', { value => '' } );

    # Print the 'From: Payor' info - This _only_ appears on the 'Payee' version of the statement.
    #
    my $payor = RPS::DB::Item::Payor->Lookup( payor_id => $statement->payor_id );

    my $cityStateLine = $payor->city;
    if ( $payor->city && $payor->state_province ) {
        $cityStateLine .= ",";
    }
    $cityStateLine .= $payor->state_province . " " . $payor->postal_code;

    addRow( $statement, $payeeIndexRef, undef, 'LEFT_TEXT', 'BOLD', { value => 'From:' } );
    addRow( $statement, $payeeIndexRef, undef, 'LEFT_TEXT', undef,  { value => $payor->name } );

    if ( $payor->street_address ) {
        addRow( $statement, $payeeIndexRef, undef, 'LEFT_TEXT', undef, { value => $payor->street_address } );
    }
    if ( $payor->city || $payor->state_province ) {
        addRow( $statement, $payeeIndexRef, undef, 'LEFT_TEXT', undef, { value => $cityStateLine } );
    }

    addRow( $statement, $payeeIndexRef, undef, 'LEFT_TEXT', undef, { value => $payor->country_code } );

    addRow( $statement, $payeeIndexRef, undef, 'BLANK', 'BLANK', { value => '' } );

    # Now we add the 'To: Payee' stuff.
    #
    addRow( $statement, $payeeIndexRef, undef, 'LEFT_TEXT', 'BOLD', { value => 'To:' } );

    my $payee = RPS::DB::Item::Publisher->Lookup( publisher_id => $statement->publisher_id );

    my $cityStateLine = $payee->city;
    if ( $payee->city && $payee->state_province ) {
        $cityStateLine .= ",";
    }
    $cityStateLine .= $payee->state_province . " " . $payee->postal_code;

    addRow( $statement, $payeeIndexRef, $fullIndexRef, 'LEFT_TEXT', undef,
        { value => $payee->publisher_name, link => '/rps/publisher?c=show&PublisherID=' . $payee->publisher_id } );
    if ( $payee->street_address ) {
        my @streetAddressLines = $payee->street_address . split('\n');
        foreach my $streetAddressLine (@streetAddressLines) {
            addRow( $statement, $payeeIndexRef, $fullIndexRef, 'LEFT_TEXT', undef, { value => $streetAddressLine } );
        }
    }
    if ( $payee->city || $payee->state_province ) {
        addRow( $statement, $payeeIndexRef, $fullIndexRef, 'LEFT_TEXT', undef, { value => $cityStateLine } );
    }
    addRow( $statement, $payeeIndexRef, $fullIndexRef, 'LEFT_TEXT', undef, { value => $payee->country_code } );

    addRow( $statement, $payeeIndexRef, $fullIndexRef, 'BLANK', 'BLANK', { value => '' } );

    Common::Log::DebugMem(' exiting header');
}

sub summary {
    my ( $statement, $payeeIndexRef, $fullIndexRef ) = @_;
    Common::Log::DebugMem(' entering summary');

    my $payee = RPS::DB::Item::Publisher->Lookup( publisher_id => $statement->publisher_id );

    drawBalanceTable( $statement, $payeeIndexRef, $fullIndexRef, $payee->status );

    # Determine if this is an admin (or an agent) or regular publisher.
    if ( $payee->is_admin == 1 || $payee->is_agency == 1 ) {
        addRow( $statement, $payeeIndexRef, $fullIndexRef, 'BLANK', 'BLANK', { value => '' } );
        drawPublisherTable( $statement, $payeeIndexRef, $fullIndexRef );
    }
    Common::Log::DebugMem(' exiting summary');
}

sub drawBalanceTable {
    my ( $statement, $payeeIndexRef, $fullIndexRef, $payeeStatus ) = @_;

    Common::Log::DebugMem(' entering drawBalanceTable');

    # The column headers
    #
    addRow(
        $statement, $payeeIndexRef, $fullIndexRef, 'BALANCE_TABLE', 'BOLD',
        { value => '',        class => 'BLANK' },        # !!! Is the 'class' necessary, really?
        { value => 'Amount',  class => 'BOLD RIGHT' },
        { value => 'Check #', class => 'BOLD' },
        { value => 'Date',    class => 'BOLD' },
        { value => 'Memo',    class => 'BOLD' },
    );

    # The first line
    #
    addRow(
        $statement,
        $payeeIndexRef,
        $fullIndexRef,
        'BALANCE_TABLE',
        undef,
        { value => 'Previous Period Balance:',                  class => 'BOLD' },
        { value => formatMoney( $statement->previous_balance ), class => 'RIGHT' },
        { value => ' ',                                         class => 'BLANK' },
        { value => ' ',                                         class => 'BLANK' },
        { value => ' ',                                         class => 'BLANK' },
    );

    # Get any transactions
    #
    my $transactionCollection =
      RPS::DB::Item::MechanicalStatementTransaction->GetByMechanicalStatementID( $statement->mechanical_statement_id );

    while ( $transactionCollection->hasNext() ) {
        my $transaction = $transactionCollection->next();

        my $type;
        if ( $transaction->type_code == 2 ) {
            $type = 'Adjustment';
        } elsif ( $transaction->type_code == 3 ) {
            $type = 'Advance';
        } elsif ( $transaction->type_code == 4 ) {
            $type = 'Payment';
        }
        my $amount = formatMoney( $transaction->amount );
        $amount = ' ' unless defined $amount;
        my $checkNum = $transaction->check_number;
        $checkNum = ' ' unless defined $checkNum;
        my $date = $transaction->transaction_date;
        $date = ' ' unless defined $date;
        my $memo = $transaction->memo;
        $memo = ' ' unless defined $memo;

        addRow(
            $statement, $payeeIndexRef, $fullIndexRef, 'BALANCE_TABLE',
            undef, { value => $type }, { value => $amount }, { value => $checkNum },
            { value => $date }, { value => $memo },
        );
    }

    addRow(
        $statement,    $payeeIndexRef,
        $fullIndexRef, 'BALANCE_TABLE',
        'UNDERLINE_0_1', { value => 'Current Period Royalties:', class => 'BOLD' },
        { value => formatMoney( $statement->subtotal ), class => 'RIGHT' }, { value => ' ', class => 'BLANK' },
        { value => ' ',                                 class => 'BLANK' }, { value => ' ', class => 'BLANK' },
    );

    if ( $statement->previous_advance_balance != 0 ) {
        addRow(
            $statement,
            $payeeIndexRef,
            $fullIndexRef,
            'BALANCE_TABLE',
            undef,
            { value => 'Previous Advance Balance:',                         class => 'BOLD' },
            { value => formatMoney( $statement->previous_advance_balance ), class => 'RIGHT' },
            { value => ' ',                                                 class => 'BLANK' },
            { value => ' ',                                                 class => 'BLANK' },
            { value => ' ',                                                 class => 'BLANK' },
        );

        addRow(
            $statement,
            $payeeIndexRef,
            $fullIndexRef,
            'BALANCE_TABLE',
            undef,
            { value => 'Applied To Advance:',                         class => 'BOLD' },
            { value => formatMoney( $statement->applied_to_advance ), class => 'RIGHT' },
            { value => ' ',                                           class => 'BLANK' },
            { value => ' ',                                           class => 'BLANK' },
            { value => ' ',                                           class => 'BLANK' },
        );

        addRow(
            $statement,
            $payeeIndexRef,
            $fullIndexRef,
            'BALANCE_TABLE',
            'UNDERLINE_0_1 BOLD',
            { value => 'Current Advance Balance:',                 class => 'BOLD' },
            { value => formatMoney( $statement->advance_balance ), class => 'RIGHT' },
            { value => ' ',                                        class => 'BLANK' },
            { value => ' ',                                        class => 'BLANK' },
            { value => ' ',                                        class => 'BLANK' },
        );
    }

    # figure out what value to use for Ending Balance
    my $endingBalance;
    if ( $statement->balance != 0 ) {
        $endingBalance = $statement->balance;
    } elsif ( $statement->amount_due != 0 ) {
        $endingBalance = $statement->amount_due;
    } else {
        $endingBalance = $statement->statement_total;
    }

    addRow(
        $statement,
        $payeeIndexRef,
        $fullIndexRef,
        'BALANCE_TABLE',
        'BOLD',
        { value => 'Ending Balance:',           class => 'BOLD OVERLINE' },
        { value => formatMoney($endingBalance), class => 'BOLD RIGHT OVERLINE' },
        { value => ' ',                         class => 'BLANK' },
        { value => ' ',                         class => 'BLANK' },
        { value => ' ',                         class => 'BLANK' },
    );

    addRow(
        $statement,    $payeeIndexRef,
        $fullIndexRef, 'BALANCE_TABLE',
        undef, { value => ' ', class => 'BLANK' },
        { value => ' ', class => 'BLANK' }, { value => ' ', class => 'BLANK' },
        { value => ' ', class => 'BLANK' }, { value => ' ', class => 'BLANK' },
    );

    addRow(
        $statement,    $payeeIndexRef,
        $fullIndexRef, 'BALANCE_TABLE',
        undef, { value => 'Minimum Payment:', class => 'BOLD' },
        { value => formatMoney( $statement->min_payment ), class => 'RIGHT' }, { value => ' ', class => 'BLANK' },
        { value => ' ',                                    class => 'BLANK' }, { value => ' ', class => 'BLANK' },
    );

    my $amountPayable;
    if ( $payeeStatus == 2 ) {
        $amountPayable = 'ON-HOLD';
    } else {
        $amountPayable = formatMoney( $statement->amount_due );
    }

    addRow(
        $statement,    $payeeIndexRef,
        $fullIndexRef, 'BALANCE_TABLE',
        'BOLD', { value => 'Amount Payable:', class => 'BOLD' },
        { value => $amountPayable, class => 'BOLD RIGHT' }, { value => '(USD)' },
        { value => ' ', class => 'BLANK' }, { value => ' ', class => 'BLANK' },
    );

    addRow( $statement, $payeeIndexRef, $fullIndexRef, 'BLANK', 'BLANK', { value => '' } );

    Common::Log::DebugMem(' exiting drawBalanceTable');
}

sub drawPublisherTable {
    my ( $statement, $payeeIndexRef, $fullIndexRef ) = @_;
    Common::Log::DebugMem(' entering drawPublisherTable');

    # Get any publishers
    #
    my $publisherList =
      RPS::DB::Item::MechanicalStatementPublisher->GetSortedByMechanicalStatementID( $statement->mechanical_statement_id );
    if ( $publisherList->hasNext() ) {

        addRow(
            $statement, $payeeIndexRef, $fullIndexRef, 'PUBLISHER_TABLE',
            'BOLD UNDERLINE_0_1',
            { value => 'Publisher',  class => 'BOLD' },
            { value => 'Amount Due', class => 'BOLD' },
        );

        while ( my $publisher = $publisherList->next() ) {
            my $publisherItem = RPS::DB::Item::Publisher->Lookup( publisher_id => $publisher->publisher_id );
            my $name          = $publisherItem->publisher_name();
            my $amount        = formatMoney( $publisher->amount_due() );
            $amount = ' ' unless defined $amount;

            # We're going to set a temporary value for the link.
            # We'll come back later when we know which row the publisher's statement starts on.
            #
            addRow(
                $statement, $payeeIndexRef, $fullIndexRef, 'PUBLISHER_TABLE', undef,
                { value => $name,   link  => 'REPLACE_' . $publisher->publisher_id },
                { value => $amount, class => 'RIGHT' },
            );
        }

        addRow( $statement, $payeeIndexRef, $fullIndexRef, 'BLANK', 'BLANK', { value => '' } );
    }

    Common::Log::DebugMem(' exiting drawPublisherTable');
}

sub details {
    my ( $statement, $payeeIndexRef, $fullIndexRef ) = @_;

    Common::Log::DebugMem(' entering details');

    # We need to know if this is an admin statement
    my $whatAmI = ref($statement);

    # If Admin, print this Publisher's Name at the top.
    if ( 'RPS::DB::Item::MechanicalStatementPublisher' eq $whatAmI ) {
        my $publisherItem = RPS::DB::Item::Publisher->Lookup( publisher_id => $statement->publisher_id );

        # For the PDF
        #
        addRow( $statement, $payeeIndexRef, undef, 'SUBPUBLISHER_TABLE', 'BOLD', { value => $publisherItem->publisher_name() }, );
        addRow(
            $statement,
            $payeeIndexRef,
            undef,
            'DETAILS_TABLE',
            'UNDERLINE_0_9',
            { value => undef },
            { value => undef },
            { value => undef },
            { value => undef },
            { value => undef },
            { value => undef },
            { value => undef },
            { value => undef },
            { value => undef },
            { value => undef },
        );

        # And for the web page
        #
        addRow(
            $statement,
            undef,
            $fullIndexRef,
            'DETAILS_TABLE',
            'BOLD',
            { value => undef },
            {
                value => $publisherItem->publisher_name() . '<a name="' . $statement->publisher_id . '" />',
                link  => '/rps/publisher?c=show&PublisherID=' . $statement->publisher_id,
                class => 'PUBLISHER_ROW'
            },
        );

        # And now update the link in the publisher table.
        #
        my $rowIndex = $$fullIndexRef - 1;
        my $oldLink  = "REPLACE_" . $statement->publisher_id;
        my $newLink =
            "/rps/statement?c=show_mechanical_page&row="
          . $rowIndex
          . "&MechanicalStatementID="
          . $statement->mechanical_statement_id . "#"
          . $statement->publisher_id;
        RPS::DB::Item::MechanicalStatementCell->UpdateMechanicalStatementCellLink( $oldLink, $newLink,
            $statement->mechanical_statement_id );

    }

    # In the payee version, we're going to repeat this for every payee in the statement.
    # We don't want that to happen in the full version, so we'll set that up elsewhere.

    addRow(
        $statement,
        $payeeIndexRef,
        undef,
        'DETAILS_TABLE',
        'UNDERLINE_0_9 BOLD REPEAT',
        { value => 'Track Title',    class => 'BOLD' },
        { value => 'Album Title',    class => 'BOLD' },
        { value => 'UPC',            class => 'BOLD CENTER' },
        { value => 'Product Config', class => 'BOLD CENTER' },
        { value => 'Region',         class => 'BOLD' },
        { value => 'Rate Period',    class => 'BOLD CENTER' },
        { value => 'Share',          class => 'BOLD RIGHT' },
        { value => 'Net Rate',       class => 'BOLD RIGHT' },
        { value => 'Net Units',      class => 'BOLD RIGHT' },
        { value => 'Amount Due',     class => 'BOLD RIGHT' },
    );

    # Add the track details.
    #
    my $statementTrackCollection;
    if ( 'RPS::DB::Item::MechanicalStatementPublisher' eq $whatAmI ) {
        Common::Log::DebugMem( ' fetching StatementTrackList w/ publisher_id:' . $statement->publisher_id );
        $statementTrackCollection =
          RPS::DB::Item::MechanicalStatementTrack->GetSortedByMechanicalStatementIDPublisherID( $statement->mechanical_statement_id,
            $statement->publisher_id );
    } else {
        Common::Log::DebugMem(' fetching StatementTrackList');
        $statementTrackCollection =
          RPS::DB::Item::MechanicalStatementTrack->GetSortedByMechanicalStatementID( $statement->mechanical_statement_id );
    }

    while ( $statementTrackCollection->hasNext() ) {
        my $statementTrack = $statementTrackCollection->next();
        trackDetails( $statement, $payeeIndexRef, $fullIndexRef, $statementTrack );
    }

    # Last, print the grand total
    #

    my @totalRows;
    my @totalRowProps;

    if ( 'RPS::DB::Item::MechanicalStatementPublisher' eq $whatAmI ) {
        my $previousBalance = $statement->previous_balance();

        # Add the transactions in.
        #
        my $transactionCollection = RPS::DB::Item::MechanicalStatementPublisherTransaction->GetByMechanicalStatementAndPublisherID(
            $statement->mechanical_statement_id,
            $statement->publisher_id );

        my $transactionCount = RPS::DB::Item::MechanicalStatementPublisherTransaction->GetCountByMechanicalStatementAndPublisherID(
            $statement->mechanical_statement_id,
            $statement->publisher_id );

        if ( $previousBalance != 0 || $transactionCount > 0 ) {

            addRow( $statement, $payeeIndexRef, $fullIndexRef, 'DETAILS_TABLE', 'BLANK', { value => undef, class => 'BLANK_ROW' }, );

            addRow(
                $statement,
                $payeeIndexRef,
                $fullIndexRef,
                'DETAILS_TABLE',
                undef,
                { value => undef,                               payeeOnly => 1 },
                { value => undef,                               payeeOnly => 1 },
                { value => undef,                               payeeOnly => 1 },
                { value => undef,                               payeeOnly => 1 },
                { value => undef,                               payeeOnly => 1 },
                { value => undef,                               payeeOnly => 1 },
                { value => undef,                               payeeOnly => 1 },
                { value => undef,                               payeeOnly => 1 },
                { value => 'Publisher Subtotal:',               class     => 'SUBTOTAL_ROW' },
                { value => formatMoney( $statement->subtotal ), class     => 'RIGHT' },
            );

            if ( $previousBalance != 0 ) {
                addRow(
                    $statement,
                    $payeeIndexRef,
                    $fullIndexRef,
                    'DETAILS_TABLE',
                    undef,
                    { value => undef,                                       payeeOnly => 1 },
                    { value => undef,                                       payeeOnly => 1 },
                    { value => undef,                                       payeeOnly => 1 },
                    { value => undef,                                       payeeOnly => 1 },
                    { value => undef,                                       payeeOnly => 1 },
                    { value => undef,                                       payeeOnly => 1 },
                    { value => undef,                                       payeeOnly => 1 },
                    { value => undef,                                       payeeOnly => 1 },
                    { value => 'Previous Balance:',                         class     => 'SUBTOTAL_ROW' },
                    { value => formatMoney( $statement->previous_balance ), class     => 'RIGHT' },
                );
            }

            while ( $transactionCollection->hasNext() ) {
                my $transaction = $transactionCollection->next();
                my $type;
                if ( $transaction->type_code == 2 ) {
                    $type = 'Adjustment';
                } elsif ( $transaction->type_code == 3 ) {
                    $type = 'Advance';
                } elsif ( $transaction->type_code == 4 ) {
                    $type = 'Payment';
                }
                addRow(
                    $statement,
                    $payeeIndexRef,
                    $fullIndexRef,
                    'DETAILS_TABLE',
                    undef,
                    { value => undef,                               payeeOnly => 1 },
                    { value => undef,                               payeeOnly => 1 },
                    { value => undef,                               payeeOnly => 1 },
                    { value => undef,                               payeeOnly => 1 },
                    { value => undef,                               payeeOnly => 1 },
                    { value => undef,                               payeeOnly => 1 },
                    { value => undef,                               payeeOnly => 1 },
                    { value => undef,                               payeeOnly => 1 },
                    { value => $type,                               class     => 'SUBTOTAL_ROW' },
                    { value => formatMoney( $transaction->amount ), class     => 'RIGHT' },
                );
            }

            addRow(
                $statement,
                $payeeIndexRef,
                $fullIndexRef,
                'DETAILS_TABLE',
                'UNDERLINE_9 BOLD',
                { value => undef,                                      payeeOnly => 1 },
                { value => undef,                                      payeeOnly => 1 },
                { value => undef,                                      payeeOnly => 1 },
                { value => undef,                                      payeeOnly => 1 },
                { value => undef,                                      payeeOnly => 1 },
                { value => undef,                                      payeeOnly => 1 },
                { value => undef,                                      payeeOnly => 1 },
                { value => undef,                                      payeeOnly => 1 },
                { value => 'Ending Balance:',                          class     => 'SUBTOTAL_ROW' },
                { value => formatMoney( $statement->statement_total ), class     => 'OVERLINE BOLD RIGHT' },
            );
        }

        addRow( $statement, $payeeIndexRef, $fullIndexRef, 'DETAILS_TABLE', 'BLANK', { value => undef, class => 'BLANK_ROW' }, );

        addRow(
            $statement,
            $payeeIndexRef,
            $fullIndexRef,
            'DETAILS_TABLE',
            'BOLD',
            { value => undef,                                 payeeOnly => 1 },
            { value => undef,                                 payeeOnly => 1 },
            { value => undef,                                 payeeOnly => 1 },
            { value => undef,                                 payeeOnly => 1 },
            { value => undef,                                 payeeOnly => 1 },
            { value => undef,                                 payeeOnly => 1 },
            { value => undef,                                 payeeOnly => 1 },
            { value => undef,                                 payeeOnly => 1 },
            { value => 'Publisher Amount Due:',               class     => 'SUBTOTAL_ROW' },
            { value => formatMoney( $statement->amount_due ), class     => 'BOLD RIGHT' },
        );

    } else {
        addRow(
            $statement,
            $payeeIndexRef,
            $fullIndexRef,
            'DETAILS_TABLE',
            'OVERLINE BOLD',
            { value => undef,                               payeeOnly => 1 },
            { value => undef,                               payeeOnly => 1 },
            { value => undef,                               payeeOnly => 1 },
            { value => undef,                               payeeOnly => 1 },
            { value => undef,                               payeeOnly => 1 },
            { value => undef,                               payeeOnly => 1 },
            { value => undef,                               payeeOnly => 1 },
            { value => undef,                               payeeOnly => 1 },
            { value => 'Total:',                            class     => 'SUBTOTAL_ROW' },
            { value => formatMoney( $statement->subtotal ), class     => 'RIGHT' },
        );
    }

    addRow( $statement, $payeeIndexRef, undef, 'BLANK', 'BLANK', { value => '' } );

    Common::Log::DebugMem(' exiting details');
}

sub trackDetails {
    my ( $statement, $payeeIndexRef, $fullIndexRef, $statementTrack ) = @_;
    Common::Log::DebugMem(' entering trackDetails');

    crossedLicenseDetails( $statement, $payeeIndexRef, $fullIndexRef, $statementTrack );
    licenseDetails( $statement, $payeeIndexRef, $fullIndexRef, $statementTrack );
    Common::Log::DebugMem(' exiting trackDetails');
}

sub crossedLicenseDetails {
    my ( $statement, $payeeIndexRef, $fullIndexRef, $statementTrack ) = @_;
    Common::Log::DebugMem(' entering crossedLicenseDetails');

    my $album = RPS::DB::Item::Album->Lookup( album_id => $statementTrack->album_id );
    my $track = RPS::DB::Item::Track->Lookup( track_id => $statementTrack->track_id );

    # Go through the entire (crossed) license list
    #
    my $licenseCollection =
      RPS::DB::Item::MechanicalStatementLicense->GetByMechanicalStatementIDTrackIDPublisherID( $statement->mechanical_statement_id,
        $statementTrack->track_id, $statement->publisher_id );

    my $licenseCount = 0;
    my $itemCount    = 0;

    my $lastRow;
    my $lastCellArray;

    while ( $licenseCollection->hasNext() ) {
        my $license = $licenseCollection->next();
        my $statementTrackLicense = RPS::DB::Item::TrackLicense->Lookup( track_license_id => $license->track_license_id );

        next if ( !$license->crossed );

        $licenseCount++;

        my $incomeItemCollection =
          RPS::DB::Item::MechanicalStatementItem->GetByStatementAndTrackLicense( $statement->mechanical_statement_id,
            $license->track_license_id );

        while ( $incomeItemCollection->hasNext() ) {
            my $incomeItem = $incomeItemCollection->next();
            $itemCount++;

            # We have to check the license product type id to see
            # if we're dealing with a ringtone.
            #
            my $productTypeID = _checkForRingtone( $statementTrackLicense, $incomeItem );

            my $statRateIDToUse = $incomeItem->applied_stat_rate_id;
            if ( !$statRateIDToUse ) {
                $statRateIDToUse = $incomeItem->sale_stat_rate_id;
            }
            my $ratePeriod = _getRatePeriod($statRateIDToUse);

            my $rowClass = 'OVERLINE';
            if ( $itemCount > 1 ) {
                $rowClass = undef;
            }
            ( $lastRow, $lastCellArray ) = addRow(
                $statement,
                $payeeIndexRef,
                $fullIndexRef,
                'DETAILS_TABLE',
                $rowClass,
                {
                    value    => '<img src="/production/images/icons/mini/briefcase.gif" />',
                    link     => '/rps/license?c=show&TrackLicenseID=' . $license->track_license_id,
                    title    => 'View License',
                    fullOnly => 1
                },
                { value => $track->title,                     link  => '/rps/track?c=show&TrackID=' . $statementTrack->track_id },
                { value => $album->title,                     link  => '/rps/catalog?c=show&AlbumID=' . $statementTrack->album_id },
                { value => $incomeItem->upc,                  class => 'CENTER' },
                { value => productCodeToName($productTypeID), class => 'CENTER' },
                { value => regionIDToName( $license->region_id ), },
                { value => $ratePeriod,                       class => 'CENTER' },
                { value => formatPercent( $incomeItem->base_rate ),                      class => 'RIGHT', fullOnly => 1 },
                { value => formatPercent( $statementTrackLicense->rate_percentage ),     class => 'RIGHT', fullOnly => 1 },
                { value => formatPercent( $statementTrackLicense->share ),               class => 'RIGHT' },
                { value => formatNumber( $incomeItem->net_rate ),                        class => 'RIGHT' },
                { value => formatNumber( $incomeItem->gross_units ),                     class => 'RIGHT', fullOnly => 1 },
                { value => formatPercent( $statementTrackLicense->percentage_of_sales ), class => 'RIGHT', fullOnly => 1 },
                { value => formatPercent( $statementTrackLicense->free_goods ),          class => 'RIGHT', fullOnly => 1 },
                { value => formatPercent( $statementTrackLicense->misc_deduction ),      class => 'RIGHT', fullOnly => 1 },
                { value => formatNumber( $incomeItem->reserved ),                        class => 'RIGHT', fullOnly => 1 },
                { value => formatNumber( $incomeItem->liquidated ),                      class => 'RIGHT', fullOnly => 1 },
                { value => formatNumber( $incomeItem->returns ),                         class => 'RIGHT', fullOnly => 1 },
                { value => formatNumber( $incomeItem->carryover ),                       class => 'RIGHT', fullOnly => 1 },
                { value => formatNumber( $incomeItem->net_units ),                       class => 'RIGHT' },
                { value => formatMoney( $incomeItem->amount_paid ),                      class => 'RIGHT' },
            );
        }

        if ( $itemCount == 0 ) {
            ( $lastRow, $lastCellArray ) = addRow(
                $statement,
                $payeeIndexRef,
                $fullIndexRef,
                'DETAILS_TABLE',
                'OVERLINE',
                {
                    value    => '<img src="/production/images/icons/mini/briefcase.gif" />',
                    link     => '/rps/license?c=show&TrackLicenseID=' . $license->track_license_id,
                    title    => 'View License',
                    fullOnly => 1
                },
                { value => $statementTrack->title, link     => '/rps/track?c=show&TrackID=' . $statementTrack->track_id },
                { value => $album->title,          link     => '/rps/catalog?c=show&AlbumID=' . $statementTrack->album_id },
                { value => undef, },
                { value => undef, },
                { value => regionIDToName( $license->region_id ), },
                { value => undef, },
                { value => undef,                  fullOnly => 1 },
                { value => undef,                  fullOnly => 1 },
                { value => undef, },
                { value => undef, },
                { value => undef,                  fullOnly => 1 },
                { value => undef,                  fullOnly => 1 },
                { value => undef,                  fullOnly => 1 },
                { value => undef,                  fullOnly => 1 },
                { value => undef,                  fullOnly => 1 },
                { value => undef,                  fullOnly => 1 },
                { value => undef,                  fullOnly => 1 },
                { value => undef,                  fullOnly => 1 },
                { value => undef, },
                { value => undef, },
            );
        }

        if ($lastRow) {
            my $lastRowClass = $lastRow->class();
            if ($lastRowClass) {
                $lastRowClass .= ' UNDERLINE_9';
            } else {
                $lastRowClass = 'UNDERLINE_9';
            }
            $lastRow->class($lastRowClass);
            $lastRow->save();
        }

        $lastRow       = undef;
        $lastCellArray = undef;

        # Add the license summary stuff.
        #
        # We are going to change a lot of things if there is no previous balance
        # OR license transactions.  So let's check for them now.

        my $previousBalance = $license->previous_advance_balance;

        addRow(
            $statement,
            $payeeIndexRef,
            $fullIndexRef,
            'DETAILS_TABLE',
            undef,
            { value => undef,                              payeeOnly => 1 },
            { value => undef,                              payeeOnly => 1 },
            { value => undef,                              payeeOnly => 1 },
            { value => undef,                              payeeOnly => 1 },
            { value => undef,                              payeeOnly => 1 },
            { value => undef,                              payeeOnly => 1 },
            { value => undef,                              payeeOnly => 1 },
            { value => undef,                              payeeOnly => 1 },
            { value => 'Current Period License Subtotal:', class     => 'SUBTOTAL_ROW' },
            { value => formatMoney( $license->subtotal ),  class     => 'RIGHT OVERLINE' },
        );

        addRow( $statement, $payeeIndexRef, $fullIndexRef, 'DETAILS_TABLE', undef, { value => undef, class => 'BLANK_ROW' }, );

        # Add the transactions in.
        #
        my $transactionCollection = RPS::DB::Item::MechanicalStatementLicenseTransaction->GetByMechanicalStatementLicenseID(
            $license->mechanical_statement_license_id );

        my $transactionCount = 0;

        while ( $transactionCollection->hasNext() ) {
            my $transaction = $transactionCollection->next();
            $transactionCount++;

            my $transactionType;
            if ( $transaction->type_code == 3 ) {
                $transactionType = 'Advance:';
            } else {
                $transactionType = 'Adjustment:';
            }

            my $memo = ' ';
            if ( $transaction->memo ) {
                $memo = '(' . $transaction->memo . ') ';
            }
            ( $lastRow, $lastCellArray ) = addRow(
                $statement,
                $payeeIndexRef,
                $fullIndexRef,
                'DETAILS_TABLE',
                undef,
                { value => undef,                               payeeOnly => 1 },
                { value => undef,                               payeeOnly => 1 },
                { value => undef,                               payeeOnly => 1 },
                { value => undef,                               payeeOnly => 1 },
                { value => undef,                               payeeOnly => 1 },
                { value => undef,                               payeeOnly => 1 },
                { value => undef,                               payeeOnly => 1 },
                { value => undef,                               payeeOnly => 1 },
                { value => $memo . $transactionType,            class     => 'SUBTOTAL_ROW' },
                { value => formatMoney( $transaction->amount ), class     => 'RIGHT' },
            );
        }

        if ( ( $previousBalance != 0 ) || ( $license->advance_balance != 0 ) ) {
            addRow(
                $statement,
                $payeeIndexRef,
                $fullIndexRef,
                'DETAILS_TABLE',
                undef,
                { value => undef,                         payeeOnly => 1 },
                { value => undef,                         payeeOnly => 1 },
                { value => undef,                         payeeOnly => 1 },
                { value => undef,                         payeeOnly => 1 },
                { value => undef,                         payeeOnly => 1 },
                { value => undef,                         payeeOnly => 1 },
                { value => undef,                         payeeOnly => 1 },
                { value => undef,                         payeeOnly => 1 },
                { value => 'Previous License Balance:',   class     => 'SUBTOTAL_ROW' },
                { value => formatMoney($previousBalance), class     => 'RIGHT' },
            );

            my $licenseBalance = $license->advance_balance;

            addRow(
                $statement,
                $payeeIndexRef,
                $fullIndexRef,
                'DETAILS_TABLE',
                undef,
                { value => undef,                        payeeOnly => 1 },
                { value => undef,                        payeeOnly => 1 },
                { value => undef,                        payeeOnly => 1 },
                { value => undef,                        payeeOnly => 1 },
                { value => undef,                        payeeOnly => 1 },
                { value => undef,                        payeeOnly => 1 },
                { value => undef,                        payeeOnly => 1 },
                { value => undef,                        payeeOnly => 1 },
                { value => 'License Balance Forward:',   class     => 'SUBTOTAL_ROW' },
                { value => formatMoney($licenseBalance), class     => 'OVERLINE UNDERLINE RIGHT' },
            );
        }

        # And really, truly finally, the adjusted subtotal if need be.
        #
        if ( ( $transactionCount != 0 ) || ( $previousBalance != 0 ) || ( $license->subtotal != $license->adjusted_subtotal ) ) {
            my $licenseBalance = $license->adjusted_subtotal;
            addRow(
                $statement,
                $payeeIndexRef,
                $fullIndexRef,
                'DETAILS_TABLE',
                undef,
                { value => undef,                        payeeOnly => 1 },
                { value => undef,                        payeeOnly => 1 },
                { value => undef,                        payeeOnly => 1 },
                { value => undef,                        payeeOnly => 1 },
                { value => undef,                        payeeOnly => 1 },
                { value => undef,                        payeeOnly => 1 },
                { value => undef,                        payeeOnly => 1 },
                { value => undef,                        payeeOnly => 1 },
                { value => 'License Adjusted Subtotal:', class     => 'SUBTOTAL_ROW' },
                { value => formatMoney($licenseBalance), class     => 'RIGHT' },
            );

            addRow( $statement, $payeeIndexRef, $fullIndexRef, 'DETAILS_TABLE', undef, { value => undef, class => 'BLANK_ROW' }, );

        }

    }

    if ( $licenseCount > 0 ) {

        # Add the license summary stuff.
        #
        # We are going to change a lot of things if there is no previous balance
        # OR license transactions.  So let's check for them now.
        my $previousBalance = $statementTrack->crossed_previous_advance_balance();

        ( $lastRow, $lastCellArray ) = addRow(
            $statement,
            $payeeIndexRef,
            $fullIndexRef,
            'DETAILS_TABLE',
            undef,
            { value => undef,                                              payeeOnly => 1 },
            { value => undef,                                              payeeOnly => 1 },
            { value => undef,                                              payeeOnly => 1 },
            { value => undef,                                              payeeOnly => 1 },
            { value => undef,                                              payeeOnly => 1 },
            { value => undef,                                              payeeOnly => 1 },
            { value => undef,                                              payeeOnly => 1 },
            { value => undef,                                              payeeOnly => 1 },
            { value => 'Current Period Crossed Subtotal:',                 class     => 'SUBTOTAL_ROW' },
            { value => formatMoney( $statementTrack->crossed_subtotal() ), class     => 'RIGHT' },
        );

        # Add the transactions in.
        #
        my $crossedTransactionCollection =
          RPS::DB::Item::MechanicalStatementCrossedLicenseTransaction->GetByMechanicalStatementIDPublisherIDTrackID(
            $statement->mechanical_statement_id,
            $statement->publisher_id, $statementTrack->track_id );

        my $crossedTransactionCount = 0;

        while ( $crossedTransactionCollection->hasNext() ) {
            my $crossedTransaction = $crossedTransactionCollection->next();
            $crossedTransactionCount++;

            my $transactionType;
            if ( $crossedTransaction->TypeCode == 3 ) {
                $transactionType = 'Advance:';
            } else {
                $transactionType = 'Adjustment:';
            }

            my $memo = ' ';
            if ( $crossedTransaction->memo ) {
                $memo = '(' . $crossedTransaction->memo . ') ';
            }

            ( $lastRow, $lastCellArray ) = addRow(
                $statement,
                $payeeIndexRef,
                $fullIndexRef,
                'DETAILS_TABLE',
                undef,
                { value => undef,                                        payeeOnly => 1 },
                { value => undef,                                        payeeOnly => 1 },
                { value => undef,                                        payeeOnly => 1 },
                { value => undef,                                        payeeOnly => 1 },
                { value => undef,                                        payeeOnly => 1 },
                { value => undef,                                        payeeOnly => 1 },
                { value => undef,                                        payeeOnly => 1 },
                { value => undef,                                        payeeOnly => 1 },
                { value => $memo . $transactionType,                     class     => 'SUBTOTAL_ROW' },
                { value => formatMoney( $crossedTransaction->amount() ), class     => 'RIGHT' },
            );
        }

        my $showCrossedAdjustedSubtotal;
        if (   ( $crossedTransactionCount != 0 )
            || ( $previousBalance != 0 )
            || ( $statementTrack->crossed_subtotal != $statementTrack->crossed_adjusted_subtotal ) ) {
            $showCrossedAdjustedSubtotal = 1;
        } else {
            $showCrossedAdjustedSubtotal = 0;
        }

        if ( ( $previousBalance != 0 ) || ( $statementTrack->crossed_advance_balance != 0 ) ) {
            ( $lastRow, $lastCellArray ) = addRow(
                $statement,
                $payeeIndexRef,
                $fullIndexRef,
                'DETAILS_TABLE',
                undef,
                { value => undef,                         payeeOnly => 1 },
                { value => undef,                         payeeOnly => 1 },
                { value => undef,                         payeeOnly => 1 },
                { value => undef,                         payeeOnly => 1 },
                { value => undef,                         payeeOnly => 1 },
                { value => undef,                         payeeOnly => 1 },
                { value => undef,                         payeeOnly => 1 },
                { value => undef,                         payeeOnly => 1 },
                { value => 'Previous Crossed Balance:',   class     => 'SUBTOTAL_ROW' },
                { value => formatMoney($previousBalance), class     => 'RIGHT' },
            );

            my $licenseBalance = $statementTrack->crossed_advance_balance;
            ( $lastRow, $lastCellArray ) = addRow(
                $statement,
                $payeeIndexRef,
                $fullIndexRef,
                'DETAILS_TABLE',
                undef,
                { value => undef,                        payeeOnly => 1 },
                { value => undef,                        payeeOnly => 1 },
                { value => undef,                        payeeOnly => 1 },
                { value => undef,                        payeeOnly => 1 },
                { value => undef,                        payeeOnly => 1 },
                { value => undef,                        payeeOnly => 1 },
                { value => undef,                        payeeOnly => 1 },
                { value => undef,                        payeeOnly => 1 },
                { value => 'Crossed Balance Forward:',   class     => 'SUBTOTAL_ROW' },
                { value => formatMoney($licenseBalance), class     => 'RIGHT' },
            );
        }

        # And really, truly finally, the adjusted subtotal if need be.
        #
        if ( $showCrossedAdjustedSubtotal == 1 ) {

            # We want a little line to show up in the PDF just before the next row.
            #
            if ($lastRow) {
                my $lastRowClass = $lastRow->class();
                if ($lastRowClass) {
                    $lastRowClass .= ' UNDERLINE_9';
                } else {
                    $lastRowClass = 'UNDERLINE_9';
                }
                $lastRow->class($lastRowClass);
                $lastRow->save();
            }

            my $licenseBalance = $statementTrack->crossed_adjusted_subtotal;
            ( $lastRow, $lastCellArray ) = addRow(
                $statement,
                $payeeIndexRef,
                $fullIndexRef,
                'DETAILS_TABLE',
                undef,
                { value => undef,                        payeeOnly => 1 },
                { value => undef,                        payeeOnly => 1 },
                { value => undef,                        payeeOnly => 1 },
                { value => undef,                        payeeOnly => 1 },
                { value => undef,                        payeeOnly => 1 },
                { value => undef,                        payeeOnly => 1 },
                { value => undef,                        payeeOnly => 1 },
                { value => undef,                        payeeOnly => 1 },
                { value => 'Crossed Adjusted Subtotal:', class     => 'SUBTOTAL_ROW' },
                { value => formatMoney($licenseBalance), class     => 'RIGHT OVERLINE' },
            );
        }

        if ($lastRow) {
            my $lastRowClass = $lastRow->class();
            if ($lastRowClass) {
                $lastRowClass .= ' UNDERLINE_0_9';
            } else {
                $lastRowClass = 'UNDERLINE_0_9';
            }
            $lastRow->class($lastRowClass);
            $lastRow->save();
        }

    }

    Common::Log::DebugMem(' exiting crossedLicenseDetails');
}

sub licenseDetails {
    my ( $statement, $payeeIndexRef, $fullIndexRef, $statementTrack ) = @_;
    Common::Log::DebugMem(' entering licenseDetails');

    my $album = RPS::DB::Item::Album->Lookup( album_id => $statementTrack->album_id );
    my $track = RPS::DB::Item::Track->Lookup( track_id => $statementTrack->track_id );

    # Go through the entire (uncrossed) license list
    #
    my $licenseCollection =
      RPS::DB::Item::MechanicalStatementLicense->GetByMechanicalStatementIDTrackIDPublisherID( $statement->mechanical_statement_id,
        $statementTrack->track_id, $statement->publisher_id );

    my $licenseCount = 0;

    while ( $licenseCollection->hasNext() ) {
        my $license = $licenseCollection->next();
        my $statementTrackLicense = RPS::DB::Item::TrackLicense->Lookup( track_license_id => $license->track_license_id );

        next if ( $license->crossed );

        $licenseCount++;

        my $incomeItemCollection =
          RPS::DB::Item::MechanicalStatementItem->GetByStatementAndTrackLicense( $statement->mechanical_statement_id,
            $license->track_license_id );

        my $lastRow;
        my $lastCellArray;
        my $itemCount = 0;

        while ( $incomeItemCollection->hasNext() ) {
            my $incomeItem = $incomeItemCollection->next();
            $itemCount++;

            # We have to check the license product type id to see
            # if we're dealing with a ringtone.
            #
            my $productTypeID = _checkForRingtone( $statementTrackLicense, $incomeItem );

            my $statRateIDToUse = $incomeItem->applied_stat_rate_id;
            if ( !$statRateIDToUse ) {
                $statRateIDToUse = $incomeItem->sale_stat_rate_id;
            }
            my $ratePeriod = _getRatePeriod($statRateIDToUse);

            my $rowClass = 'OVERLINE';
            if ( $itemCount > 1 ) {
                $rowClass = undef;
            }

            ( $lastRow, $lastCellArray ) = addRow(
                $statement,
                $payeeIndexRef,
                $fullIndexRef,
                'DETAILS_TABLE',
                $rowClass,
                {
                    value    => '<img src="/production/images/icons/mini/briefcase.gif" />',
                    link     => '/rps/license?c=show&TrackLicenseID=' . $license->track_license_id,
                    title    => 'View License',
                    fullOnly => 1
                },
                { value => $track->title,                     link  => '/rps/track?c=show&TrackID=' . $statementTrack->track_id },
                { value => $album->title,                     link  => '/rps/catalog?c=show&AlbumID=' . $statementTrack->album_id },
                { value => $incomeItem->upc,                  class => 'CENTER' },
                { value => productCodeToName($productTypeID), class => 'CENTER' },
                { value => regionIDToName( $license->region_id ), },
                { value => $ratePeriod,                       class => 'CENTER' },
                { value => formatPercent( $incomeItem->base_rate ),                      class => 'RIGHT', fullOnly => 1 },
                { value => formatPercent( $statementTrackLicense->rate_percentage ),     class => 'RIGHT', fullOnly => 1 },
                { value => formatPercent( $statementTrackLicense->share ),               class => 'RIGHT' },
                { value => formatNumber( $incomeItem->net_rate ),                        class => 'RIGHT' },
                { value => formatNumber( $incomeItem->gross_units ),                     class => 'RIGHT', fullOnly => 1 },
                { value => formatPercent( $statementTrackLicense->percentage_of_sales ), class => 'RIGHT', fullOnly => 1 },
                { value => formatPercent( $statementTrackLicense->free_goods ),          class => 'RIGHT', fullOnly => 1 },
                { value => formatPercent( $statementTrackLicense->misc_deduction ),      class => 'RIGHT', fullOnly => 1 },
                { value => formatNumber( $incomeItem->reserved ),                        class => 'RIGHT', fullOnly => 1 },
                { value => formatNumber( $incomeItem->liquidated ),                      class => 'RIGHT', fullOnly => 1 },
                { value => formatNumber( $incomeItem->returns ),                         class => 'RIGHT', fullOnly => 1 },
                { value => formatNumber( $incomeItem->carryover ),                       class => 'RIGHT', fullOnly => 1 },
                { value => formatNumber( $incomeItem->net_units ),                       class => 'RIGHT' },
                { value => formatMoney( $incomeItem->amount_paid ),                      class => 'RIGHT' },
            );
        }

        if ( $itemCount == 0 ) {

            # Even if there are no income items,
            # we still want to display the track info.
            ( $lastRow, $lastCellArray ) = addRow(
                $statement,
                $payeeIndexRef,
                $fullIndexRef,
                'DETAILS_TABLE',
                'OVERLINE',
                {
                    value    => '<img src="/production/images/icons/mini/briefcase.gif" />',
                    link     => '/rps/license?c=show&TrackLicenseID=' . $license->track_license_id,
                    title    => 'View License',
                    fullOnly => 1
                },
                { value => $statementTrack->title, link     => '/rps/track?c=show&TrackID=' . $statementTrack->track_id },
                { value => $album->title,          link     => '/rps/catalog?c=show&AlbumID=' . $statementTrack->album_id },
                { value => undef, },
                { value => undef, },
                { value => regionIDToName( $license->region_id ), },
                { value => undef, },
                { value => undef,                  fullOnly => 1 },
                { value => undef,                  fullOnly => 1 },
                { value => undef, },
                { value => undef, },
                { value => undef,                  fullOnly => 1 },
                { value => undef,                  fullOnly => 1 },
                { value => undef,                  fullOnly => 1 },
                { value => undef,                  fullOnly => 1 },
                { value => undef,                  fullOnly => 1 },
                { value => undef,                  fullOnly => 1 },
                { value => undef,                  fullOnly => 1 },
                { value => undef,                  fullOnly => 1 },
                { value => undef, },
                { value => undef, },
            );
        }

        if ($lastRow) {
            my $lastRowClass = $lastRow->class();
            if ($lastRowClass) {
                $lastRowClass .= ' UNDERLINE_9';
            } else {
                $lastRowClass = 'UNDERLINE_9';
            }
            $lastRow->class($lastRowClass);
            $lastRow->save();
        }

        $lastRow       = undef;
        $lastCellArray = undef;

        # Add the license summary stuff.
        #
        # We are going to change a lot of things if there is no previous balance
        # OR license transactions.  So let's check for them now.
        my $previousBalance = $license->previous_advance_balance();

        addRow(
            $statement,
            $payeeIndexRef,
            $fullIndexRef,
            'DETAILS_TABLE',
            'UNDERLINE_0_9',
            { value => undef,                               payeeOnly => 1 },
            { value => undef,                               payeeOnly => 1 },
            { value => undef,                               payeeOnly => 1 },
            { value => undef,                               payeeOnly => 1 },
            { value => undef,                               payeeOnly => 1 },
            { value => undef,                               payeeOnly => 1 },
            { value => undef,                               payeeOnly => 1 },
            { value => undef,                               payeeOnly => 1 },
            { value => 'Current Period License Subtotal:',, class     => 'SUBTOTAL_ROW' },
            { value => formatMoney( $license->subtotal ),   class     => 'RIGHT OVERLINE' },
        );

        # Add the transactions in.
        #
        my $transactionCollection = RPS::DB::Item::MechanicalStatementLicenseTransaction->GetByMechanicalStatementLicenseID(
            $license->mechanical_statement_license_id );

        my $transactionCount = 0;

        while ( $transactionCollection->hasNext() ) {
            my $transaction = $transactionCollection->next();
            $transactionCount++;

            my $transactionType;
            if ( $transaction->type_code == 3 ) {
                $transactionType = 'Advance:';
            } else {
                $transactionType = 'Adjustment:';
            }

            my $memo = ' ';
            if ( $transaction->memo ) {
                $memo = '(' . $transaction->memo . ') ';
            }

            addRow(
                $statement,
                $payeeIndexRef,
                $fullIndexRef,
                'DETAILS_TABLE',
                undef,
                { value => undef,                               payeeOnly => 1 },
                { value => undef,                               payeeOnly => 1 },
                { value => undef,                               payeeOnly => 1 },
                { value => undef,                               payeeOnly => 1 },
                { value => undef,                               payeeOnly => 1 },
                { value => undef,                               payeeOnly => 1 },
                { value => undef,                               payeeOnly => 1 },
                { value => undef,                               payeeOnly => 1 },
                { value => $memo . $transactionType,            class     => 'SUBTOTAL_ROW' },
                { value => formatMoney( $transaction->amount ), class     => 'RIGHT' },
            );
        }

        if ( ( $previousBalance != 0 ) || ( $license->advance_balance != 0 ) ) {
            addRow(
                $statement,
                $payeeIndexRef,
                $fullIndexRef,
                'DETAILS_TABLE',
                undef,
                { value => undef,                         payeeOnly => 1 },
                { value => undef,                         payeeOnly => 1 },
                { value => undef,                         payeeOnly => 1 },
                { value => undef,                         payeeOnly => 1 },
                { value => undef,                         payeeOnly => 1 },
                { value => undef,                         payeeOnly => 1 },
                { value => undef,                         payeeOnly => 1 },
                { value => undef,                         payeeOnly => 1 },
                { value => 'Previous License Balance:',   class     => 'SUBTOTAL_ROW' },
                { value => formatMoney($previousBalance), class     => 'RIGHT' },
            );

            my $licenseBalance = $license->advance_balance;

            addRow(
                $statement,
                $payeeIndexRef,
                $fullIndexRef,
                'DETAILS_TABLE',
                undef,
                { value => undef,                        payeeOnly => 1 },
                { value => undef,                        payeeOnly => 1 },
                { value => undef,                        payeeOnly => 1 },
                { value => undef,                        payeeOnly => 1 },
                { value => undef,                        payeeOnly => 1 },
                { value => undef,                        payeeOnly => 1 },
                { value => undef,                        payeeOnly => 1 },
                { value => undef,                        payeeOnly => 1 },
                { value => 'License Balance Forward:',   class     => 'SUBTOTAL_ROW' },
                { value => formatMoney($licenseBalance), class     => 'OVERLINE UNDERLINE RIGHT' },
            );
        }

        # And really, truly finally, the adjusted subtotal if need be.
        #
        if ( ( $transactionCount != 0 ) || ( $previousBalance != 0 ) || ( $license->subtotal != $license->adjusted_subtotal ) ) {
            my $licenseBalance = $license->adjusted_subtotal;
            addRow(
                $statement,
                $payeeIndexRef,
                $fullIndexRef,
                'DETAILS_TABLE',
                undef,
                { value => undef,                        payeeOnly => 1 },
                { value => undef,                        payeeOnly => 1 },
                { value => undef,                        payeeOnly => 1 },
                { value => undef,                        payeeOnly => 1 },
                { value => undef,                        payeeOnly => 1 },
                { value => undef,                        payeeOnly => 1 },
                { value => undef,                        payeeOnly => 1 },
                { value => undef,                        payeeOnly => 1 },
                { value => 'License Adjusted Subtotal:', class     => 'SUBTOTAL_ROW' },
                { value => formatMoney($licenseBalance), class     => 'RIGHT' },
            );
        }
    }
    Common::Log::DebugMem(' exiting licenseDetails');
}

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

    $value = Formats::formatNumber($value);

    return $value;
}

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

    # Use our own rounding algorithm.
    #
    my $rVal = Common::RSMath::round( $value, 2 );

    $rVal = Common::Client::Current()->Locale()->formatMoney( $rVal, "USD" );

    return $rVal;

    #my ($value) = @_;

    #$value = Formats::formatMoney($value);

    #return $value;
}

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

    $value = Formats::formatPercent($value);

    return $value;
}

sub _checkForRingtone {
    my ( $license, $incomeItem ) = @_;

    my $productTypeID;

    if ( $license->product_type_id == RPS::DB::Item::Product::kProductTypeRingtone ) {
        $productTypeID = RPS::DB::Item::Product::kProductTypeRingtone;
    } else {
        my $product = RPS::DB::Item::Product->Lookup( product_id => $incomeItem->product_id );
        $productTypeID = $product->product_type_id;
    }

    return $productTypeID;
}

my $gRatePeriodMap;

sub _getRatePeriod {
    my ($id) = @_;

    return "" unless $id;

    if ( !$gRatePeriodMap ) {
        $gRatePeriodMap = {};

        my $statRates = RPS::DB::Item::StatRate->GetAll();
        while ( my $statRate = $statRates->next() ) {
            my ( $year, $month, $day ) = split( '-', $statRate->date_effective );
            my $nextYear;

            # Ringtones only have one stat rate, so we'll treat them differently.
            #
            if ( $statRate->stat_rate_type_id == 3 ) {
                $nextYear = 'Current';
            } else {
                if ( $year < 2006 ) {
                    $nextYear = $year + 1;
                } else {
                    $nextYear = 2011;
                }
            }
            my $range = "$year-$nextYear";
            $gRatePeriodMap->{ $statRate->stat_rate_id } = $range;
        }
    }

    return $gRatePeriodMap->{$id};
}

# 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 ne 'product_type_id' ) {
                $idMaps{$collectionAccessor}{ $item->$idName() } = $item->name;
            } else {
                $idMaps{$collectionAccessor}{ $item->$idName() } =
                  $item->description;
            }
        }
    }

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

sub incomeSourceIDToName {
    my ($id) = @_;

    return _genericMapAccessor( 'RPS::DB::Item::IncomeSource', 'income_source_id', $id );
}

sub regionIDToName {
    my ($id) = @_;

    # we don't want to show Rest of World here
    if ( $id != 0 ) {
        return _genericMapAccessor( 'RPS::DB::Item::Region', 'region_id', $id );
    } else {
        return ' ';
    }
}

sub channelIDToName {
    my ($id) = @_;
    return _genericMapAccessor( 'RPS::DB::Item::Channel', 'channel_id', $id );
}

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

sub contractRateTypeIDToName {
    my ($id) = @_;
    return _genericMapAccessor( 'RPS::DB::Item::ContractRateType', 'contract_rate_type_id', $id );
}

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

sub addDraftWatermark {
    my ($pdf) = @_;

    my $centerX = kPageWidth / 2;
    my $centerY = kPageHeight / 2;

    foreach my $page (@gPages) {
        my $text = $page->text(1);
        $text->font( $font->{Helvetica}{Bold}, 128 / pt );
        $text->transform(
            -translate => [ $centerX, $centerY ],
            -rotate    => 45
        );

        $text->fillcolor('lightgray');
        $text->text_center('DRAFT');
        $text->fillcolor('black');
    }
}

sub addRow {
    my ( $statement, $payeeIndexRowRef, $fullIndexRowRef, $tableClass, $rowClass, @cells ) = @_;

    Common::Log::DebugMem( "   entering addRow $payeeIndexRowRef $fullIndexRowRef $tableClass $rowClass " . join( ",", @cells ) );

    # Create the new row
    #
    my $row = RPS::DB::Item::MechanicalStatementRow->Create( mechanical_statement_id => $statement->mechanical_statement_id(), );
    $row->payee_index( $$payeeIndexRowRef++ ) if defined $payeeIndexRowRef;
    $row->full_index( $$fullIndexRowRef++ )   if defined $fullIndexRowRef;
    $row->table_class($tableClass)            if defined $tableClass;
    $row->class($rowClass)                    if defined $rowClass;

    if ( $rowClass =~ m/REPEAT/ ) {
        $row->repeat_row(1);
    }

    $row->save();

    # Create the columns
    #
    my $payeeIndexCell = 0;
    my $fullIndexCell  = 0;
    my $rowID          = $row->mechanical_statement_row_id();

    my @returnCells;

    foreach my $cellData (@cells) {
        my $cell = RPS::DB::Item::MechanicalStatementCell->Create(
            mechanical_statement_row_id => $rowID,
            display_value               => $cellData->{value},
            class                       => $cellData->{class},
            link_value                  => $cellData->{link},
            title_value                 => $cellData->{title},
        );

        if ( !$cellData->{fullOnly} ) {
            $cell->payee_index( $payeeIndexCell++ );
        }
        if ( !$cellData->{payeeOnly} ) {
            $cell->full_index( $fullIndexCell++ );
        }

        $cell->save();

        push @returnCells, $cell;
    }

    # So we're going to return references to the row and cell items.
    # Usually we'll simply ignore that, but occasionally we need this to
    # make further changes.

    Common::Log::DebugMem('   exiting addRow');
    return ( $row, \@returnCells );
}

#
# Boring script stuff below...
#

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

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

    if ( !$opt{s} || !$opt{c} ) {
        usage();
        exit(1);
    }
    $settings->{mechanicalStatementID} = $opt{s};
    $settings->{clientID}              = $opt{c};

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

sub usage {
    print STDERR "\nusage: $0 -c <client_id> -s <mechanical_statement_id> [-f <output file>]\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-s <mechanical_statement_id>\tThe id of the mechanical statement to convert\n";
}

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

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

