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

#use warnings;

use PDF::API2;

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 lib '/app/tools/rps/lib';
use RPS::DB::Item::Album;
use RPS::DB::Item::Channel;
use RPS::DB::Item::ContractRateType;
use RPS::DB::Item::IncomeSource;
use RPS::DB::Item::Payor;
use RPS::DB::Item::PriceLevel;
use RPS::DB::Item::Product;
use RPS::DB::Item::ProductType;
use RPS::DB::Item::Region;

use RPS::DB::Item::CAMechanicalRun;
use RPS::DB::Item::CAMechanicalStatementCrossedLicenseTransaction;
use RPS::DB::Item::CAMechanicalStatement;
use RPS::DB::Item::CAMechanicalStatementAdjustmentItem;
use RPS::DB::Item::CAMechanicalStatementItem;
use RPS::DB::Item::CAMechanicalStatementLicense;
use RPS::DB::Item::CAMechanicalStatementLicenseTransaction;
use RPS::DB::Item::CAMechanicalStatementPublisher;
use RPS::DB::Item::CAMechanicalStatementPublisherTransaction;
use RPS::DB::Item::CAMechanicalStatementTrack;
use RPS::DB::Item::CAMechanicalStatementTransaction;
use RPS::DB::Item::StatRate;
use RPS::DB::Item::CAPublisher;

use RPS::RoyaltyRun::Status;

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

$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 @gPayeePages;
my @gFullPages;

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

# 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 );
createStatementPDF( $options{clientID}, $options{mechanicalStatementID}, $options{mode}, $options{outputFile} );

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

sub createStatementPDF {
    my ( $clientID, $mechanicalStatementID, $mode, $outFilePath ) = @_;

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

    # Grab the statement item and off we go.
    #
    my $statement = RPS::DB::Item::CAMechanicalStatement->Lookup( ca_mechanical_statement_id => $mechanicalStatementID );

    # If an output file path was not specified, create one out of the statement id.
    #
    if ( !$outFilePath ) {
        $outFilePath = "ca_mechanical_statement_$mechanicalStatementID.pdf";
    }

    # Mode can have three different values: p, f, and b.
    # p = Create payee version
    # f = Create full version
    # b = Create both
    #

    if ( $mode eq 'p' || $mode eq 'b' ) {
        createPayeeStatementPDF( $statement, $outFilePath );
    }

    if ( $mode eq 'f' || $mode eq 'b' ) {
        createFullStatementPDF( $statement, $outFilePath );
    }

}

##########################
# Payee Statement Stuff  #
##########################

sub createPayeeStatementPDF {
    my ( $statement, $outFilePath ) = @_;

    # Instantiate the PDF object.
    #
    my $pdf = PDF::API2->new( -file => $outFilePath );

    # Fetch the fonts that we might want to use.
    # Note that every time we define a font, it will get embedded in the document,
    # so we will grab them all here at the very beginning.
    # !!! Be sure to remove any of these we don't actually need...
    #
    # (These are the 'built-in' fonts that all pdf documents get for free. We can embed fancy
    #  fonts if we want to, but that will make the statement files larger...)
    #
    $font = {
        Helvetica => {
            Bold   => $pdf->corefont( 'Helvetica-Bold',    -encoding => 'latin1' ),
            Roman  => $pdf->corefont( 'Helvetica',         -encoding => 'latin1' ),
            Italic => $pdf->corefont( 'Helvetica-Oblique', -encoding => 'latin1' ),
        },
        Times => {
            Bold   => $pdf->corefont( 'Times-Bold',   -encoding => 'latin1' ),
            Roman  => $pdf->corefont( 'Times',        -encoding => 'latin1' ),
            Italic => $pdf->corefont( 'Times-Italic', -encoding => 'latin1' ),
        },
    };

    # Declare the 'cursor' coordinates.  As we go, we'll be updating these values so that the
    # next element shows up in the right place.
    #
    my $x = kLeftMargin;
    my $y = kTopMargin;

    # Create the first (blank) page.
    # Each output subroutine will take the current page as a parameter, and will
    # return the page that they ended on.
    #
    my $page = newPayeePage($pdf);

    # Add label logo to the first page, if desired
    #
    my $payor = RPS::DB::Item::Payor->Lookup( payor_id => $statement->payor_id );
    my $showLogo = $payor->show_logo;

    if ( $showLogo == 1 ) {
        my $logoName = $options{clientID} . '/' . $statement->payor_id;
        ( $pdf, $page ) = EmbedImage::labelLogo( $pdf, $page, $logoName, kTopMargin, kRightMargin );
    }

    # Each section of the statement will have its own subroutine.
    #
    ( $x, $y, $page ) = payeeHeader( $pdf, $statement, $x, $y, $page );
    ( $x, $y, $page ) = payeeSummary( $pdf, $statement, $x, $y, $page );

    my $publisher = RPS::DB::Item::CAPublisher->Lookup( ca_publisher_id => $statement->ca_publisher_id );

    # Determine if this is an admin or regular publisher.
    if (   $publisher->is_admin == 1
        || $publisher->is_agency == 1 ) {

        # Loop through all publishers for admins.
        my $publisherList =
          RPS::DB::Item::CAMechanicalStatementPublisher->GetSortedByMechanicalStatementID( $statement->ca_mechanical_statement_id );

        while ( my $subPublisher = $publisherList->next() ) {
            ( $x, $y, $page ) = payeeDetails( $pdf, $subPublisher, $x, $y, $page );
        }
    } else {

        # Not an admin, so just do this once.
        ( $x, $y, $page ) = payeeDetails( $pdf, $statement, $x, $y, $page );
    }

    # If the state of the run is not 'COMMITTED' or 'CLOSED', then we want to add a watermark
    # to the pages so that our users don't mail this statement out.
    #
    my $run = RPS::DB::Item::CAMechanicalRun->Lookup( ca_mechanical_run_id => $statement->ca_mechanical_run_id );
    if (   RPS::RoyaltyRun::Status::kCommitted != $run->status
        && RPS::RoyaltyRun::Status::kClosed != $run->status ) {
        addDraftWatermarkPayee($pdf);
    }

    # Now add the page numbers, and other 'footer' stuff
    #
    payeeFooter($pdf);

    # All done, save and clean up
    #
    $pdf->save();
    $pdf->end();
}

sub payeeHeader {
    my ( $pdf, $statement, $x, $y, $page ) = @_;

    my $text = $page->text();

    # Step 1 - Put the text 'Canadian Mechanical Royalty Statement' at the top of the page.
    #
    $y = printText(
        page     => $page,
        display  => 'Canadian Mechanical Royalty Statement',
        y        => $y,
        font     => $font->{Helvetica}{Bold},
        fontSize => 12 / pt,
        centered => 1
    );

    # Print the 'From: Payor' info
    #
    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;

    if ( $payor->postal_code ) {
        $cityStateLine .= " " . $payor->postal_code;
    }

    $y = printText(
        page     => $page,
        display  => 'From:',
        x        => $x,
        y        => $y,
        font     => $font->{Helvetica}{Roman},
        fontSize => 9 / pt
    );

    $y = printText(
        page     => $page,
        display  => $payor->name,
        x        => $x,
        y        => $y,
        font     => $font->{Helvetica}{Roman},
        fontSize => 9 / pt
    );

    $y = printText(
        page     => $page,
        display  => $payor->street_address,
        x        => $x,
        y        => $y,
        font     => $font->{Helvetica}{Roman},
        fontSize => 9 / pt
    );

    $y = printText(
        page     => $page,
        display  => $payor->street_address_2,
        x        => $x,
        y        => $y,
        font     => $font->{Helvetica}{Roman},
        fontSize => 9 / pt
    );

    $y = printText(
        page     => $page,
        display  => $payor->street_address_3,
        x        => $x,
        y        => $y,
        font     => $font->{Helvetica}{Roman},
        fontSize => 9 / pt
    );

    $y = printText(
        page     => $page,
        display  => $cityStateLine,
        x        => $x,
        y        => $y,
        font     => $font->{Helvetica}{Roman},
        fontSize => 9 / pt
    );

    $y = printText(
        page     => $page,
        display  => $payor->country_code,
        x        => $x,
        y        => $y,
        font     => $font->{Helvetica}{Roman},
        fontSize => 9 / pt
    );

    # Add in some space, then print the 'To:' info
    #
    $y -= 9 / pt;

    my $payee = RPS::DB::Item::CAPublisher->Lookup( ca_publisher_id => $statement->ca_publisher_id );

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

    if ( $payee->postal_code ) {
        $cityStateLine .= " " . $payee->postal_code;
    }

    $y = printText(
        page     => $page,
        display  => $payee->publisher_name,
        x        => $x,
        y        => $y,
        font     => $font->{Helvetica}{Roman},
        fontSize => 9 / pt
    );

    $y = printText(
        page     => $page,
        display  => $payee->street_address,
        x        => $x,
        y        => $y,
        font     => $font->{Helvetica}{Roman},
        fontSize => 9 / pt
    );

    $y = printText(
        page     => $page,
        display  => $payee->street_address_2,
        x        => $x,
        y        => $y,
        font     => $font->{Helvetica}{Roman},
        fontSize => 9 / pt
    );

    $y = printText(
        page     => $page,
        display  => $payee->street_address_3,
        x        => $x,
        y        => $y,
        font     => $font->{Helvetica}{Roman},
        fontSize => 9 / pt
    );

    $y = printText(
        page     => $page,
        display  => $cityStateLine,
        x        => $x,
        y        => $y,
        font     => $font->{Helvetica}{Roman},
        fontSize => 9 / pt
    );

    $y = printText(
        page     => $page,
        display  => $payee->country_code,
        x        => $x,
        y        => $y,
        font     => $font->{Helvetica}{Roman},
        fontSize => 9 / pt
    );

    # Now print the run label. Which means we need to fetch the run...
    #
    # !!! For CA Mechanicals, the 'label' is just the start and end date of the run.
    #
    my $run = RPS::DB::Item::CAMechanicalRun->Lookup( ca_mechanical_run_id => $statement->ca_mechanical_run_id );
    my $label = Common::Client::Current()->Locale()->formatDate( $run->start_date() ) . ' - '
      . Common::Client::Current()->Locale()->formatDate( $run->end_date() );

    $y -= 9 / pt;

    $y = printText(
        page     => $page,
        display  => $label,
        x        => $x,
        y        => $y,
        font     => $font->{Helvetica}{Bold},
        fontSize => 9 / pt
    );

    $y -= 18 / pt;

    return ( $x, $y, $page );
}

sub payeeSummary {
    my ( $pdf, $statement, $x, $y, $page ) = @_;

    ( $x, $y, $page ) = drawBalanceTable( $pdf, $statement, $x, $y, $page, \&newPayeePage );

    my $publisher = RPS::DB::Item::CAPublisher->Lookup( ca_publisher_id => $statement->ca_publisher_id );

    # Determine if this is an admin or regular publisher.
    if ( $publisher->is_admin == 1 || $publisher->is_agency == 1 ) {
        ( $x, $y, $page ) = payeePublisherTable( $pdf, $statement, $x, $y, $page );
    }

    #$y -= 18;

    return ( $x, $y, $page );
}

sub payeePublisherTable {
    my ( $pdf, $statement, $x, $y, $page ) = @_;
    my $fontSize = 7 / pt;

    my $text = $page->text();

    # We'll create a 2-d array that contains the data we want to display.
    #
    my @publisherArray;

    # This array will keep track of various row properties.
    # We're going to change the font for a couple of rows (such as the header),
    # and do a few more tricks.
    #
    my @rowProperties;

    # Get any publishers
    #
    my $publisherList =
      RPS::DB::Item::CAMechanicalStatementPublisher->GetSortedByMechanicalStatementID( $statement->ca_mechanical_statement_id );

    if ( $publisherList->hasNext() ) {

        # The column headers
        #
        push @publisherArray, [ ' ', ' ', ' ', ' ', ' ' ];
        push @rowProperties, {};
        push @publisherArray, [ ' ', ' ', ' ', ' ', ' ' ];
        push @rowProperties, {};
        push @publisherArray, [ 'Publisher', 'Amount Due' ];
        push @rowProperties,
          {
            font  => $font->{Helvetica}{Bold},
            lines => [ {
                    start_col => 0,
                    end_col   => 1,
                    top_pad   => 4,
                },
            ],
          };

        while ( my $subPublisher = $publisherList->next() ) {
            my $publisherItem = RPS::DB::Item::CAPublisher->Lookup( ca_publisher_id => $subPublisher->ca_publisher_id );
            my $name          = $publisherItem->publisher_name();
            my $amount        = formatMoney( $subPublisher->amount_due() );

            #            if ($statement->on_hold == 1 || $subPublisher->on_hold == 1)
            if ( $subPublisher->on_hold == 1 ) {
                $amount = "ON-HOLD";
            }

            $amount = ' ' unless defined $amount;

            push @publisherArray, [ $name, $amount ];
            push @rowProperties, {};
        }

        my $tableObj = TableObj->new(
            $pdf, $page,
            \@publisherArray,
            bottom_margin => kBottomMargin + 20,
            new_page_y    => kTopMargin - 20,
            new_page_func => \&newPayeePage,
            font          => $font->{Helvetica}{Roman},
            font_size     => $fontSize,
            column_props  => [ {
                    min_w     => 0.5 / in,
                    font      => $font->{Helvetica}{Roman},
                    font_size => $fontSize,
                },
                {
                    min_w     => 0.5 / in,
                    font      => $font->{Helvetica}{Roman},
                    font_size => $fontSize,
                    justify   => 'right',
                },
            ],
            row_props => \@rowProperties,
        );

        ( $page, $y ) = $tableObj->print( $x, $y );

    }

    return ( $x, $y, $page );
}

sub payeeDetails {
    my ( $pdf, $statement, $x, $y, $page ) = @_;
    my $fontSize = 7 / pt;

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

    # start details on a new page
    $page = newPayeePage($pdf);
    $y    = kTopMargin - 10;

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

        $y = printText(
            page     => $page,
            display  => $publisher->publisher_name,
            y        => $y,
            font     => $font->{Helvetica}{Bold},
            fontSize => 12 / pt,
            centered => 1
        );

    }

    my @rows;
    my @rowProps;

    # Set up the header
    #
    push @rows, [ ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ' ];
    push @rowProps, {
        font  => $font->{Helvetica}{Bold},
        lines => [ {
                start_col => 0,
                end_col   => 9,
                top_pad   => 4,

                # bottom_pad => 2,
            },
        ],
    };
    push @rows,
      [ 'Track Title', 'Product Title', 'UPC', 'Product Config', 'Region', 'Rate Period', 'Share', 'Net Rate', 'Net Units', 'Amount Due' ];
    push @rowProps, {
        font   => $font->{Helvetica}{Bold},
        repeat => 1,
        lines  => [ {
                start_col => 0,
                end_col   => 9,
                top_pad   => 4,

                # bottom_pad => 2,
            },
        ],
    };

    # Add the track details.
    #
    my $statementTrackCollection;
    if ( 'RPS::DB::Item::CAMechanicalStatementPublisher' eq $whatAmI ) {
        $statementTrackCollection =
          RPS::DB::Item::CAMechanicalStatementTrack->GetSortedByMechanicalStatementIDPublisherID( $statement->ca_mechanical_statement_id,
            $statement->ca_publisher_id );
    } else {
        $statementTrackCollection =
          RPS::DB::Item::CAMechanicalStatementTrack->GetSortedByMechanicalStatementID( $statement->ca_mechanical_statement_id );
    }

    while ( $statementTrackCollection->hasNext() ) {
        my $statementTrack = $statementTrackCollection->next();
        payeeTrackDetails( $statementTrack, $statement, \@rows, \@rowProps );
    }

    # Force the columns to be fixed width.
    #
    my @colProps = ( {

            # track title
            min_w => 2 / in,
            max_w => 2 / in,
            pad   => 0,
        },
        {

            # album title
            min_w => 1.8 / in,
            max_w => 1.8 / in,
            pad   => 0,
        },
        {

            # upc
            min_w => 0.75 / in,
            max_w => 0.75 / in,
            pad   => 0,
        },
        {

            # product config
            min_w => 0.75 / in,
            max_w => 0.75 / in,
            pad   => 0,
        },
        {

            # region
            min_w => 0.65 / in,
            max_w => 0.65 / in,
            pad   => 0,
        },
        {

            # rate period
            min_w => 0.60 / in,
            max_w => 0.60 / in,
            pad   => 0,
        },
        {

            # share
            min_w => 0.5 / in,
            max_w => 0.5 / in,
            pad   => 0,
        },
        {

            # net rate
            min_w => 0.5 / in,
            max_w => 0.5 / in,
            pad   => 0,
        },
        {

            # net units
            justify => 'right',
            min_w   => 1.45 / in,
            max_w   => 1.45 / in,
            pad     => 0,
        },
        {

            # amount due
            justify => 'right',
            min_w   => 1 / in,
            max_w   => 1 / in,
            pad     => 0,
        },
    );

    my $tableObj = TableObj->new(
        $pdf, $page, \@rows,
        bottom_margin => kBottomMargin + 20,          # adding a bit of padding to avoid weirdness
        new_page_y    => kTopMargin - 20,             # ditto
        new_page_func => \&newPayeePage,
        font          => $font->{Helvetica}{Roman},
        font_size     => $fontSize,
        column_props  => \@colProps,
        row_props     => \@rowProps,
    );

    ( $page, $y ) = $tableObj->print( $x, $y );

    # Last, print the grand total
    #

    my @totalRows;
    my @totalRowProps;

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

        # Add the transactions in.
        #
        my $transactionCollection = RPS::DB::Item::CAMechanicalStatementPublisherTransaction->GetByMechanicalStatementAndPublisherID(
            $statement->ca_mechanical_statement_id,
            $statement->ca_publisher_id );

        # JPK - Why make a second query, since we have the count in the collection?
        my $transactionCount = $transactionCollection->size();

#        my $transactionCount = RPS::DB::Item::CAMechanicalStatementPublisherTransaction->GetCountByMechanicalStatementAndPublisherID($statement->ca_mechanical_statement_id, $statement->ca_publisher_id);

        if ( $previousBalance != 0 || $transactionCount > 0 ) {
            push @totalRows, [ 'Publisher Subtotal:', formatMoney( $statement->subtotal ) ];

            if ( $previousBalance != 0 ) {
                push @totalRows, [ 'Previous Balance:', formatMoney( $statement->previous_balance ) ];
                push @totalRowProps, {};
            }

            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';
                }
                push @totalRows, [ "$type:", formatMoney( $transaction->amount ) ];
                push @totalRowProps, {};
            }

            push @totalRows, [ 'Ending Balance:', formatMoney( $statement->statement_total ) ];

            # Note that this will actually go above the ending balance, since we did't add an element to the
            # array for the subtotal.
            #
            push @totalRowProps,
              {
                repeat => 1,
                lines  => [ {
                        start_col  => 1,
                        end_col    => 1,
                        top_pad    => 2,
                        bottom_pad => -1,
                    },
                ],
              };

            push @totalRows, [];
            push @totalRowProps, { font => $font->{Helvetica}{Bold} };
        }

        my $amountDue;
        my $parentStatement =
          RPS::DB::Item::CAMechanicalStatement->Lookup( ca_mechanical_statement_id => $statement->ca_mechanical_statement_id );

        #        if ($parentStatement->on_hold == 1 || $statement->on_hold == 1)
        if ( $statement->on_hold == 1 ) {
            $amountDue = "ON-HOLD";
        } else {
            $amountDue = formatMoney( $statement->amount_due );
        }

        push @totalRows, [ 'Publisher Amount Due:', $amountDue ];
        push @totalRowProps, { font => $font->{Helvetica}{Bold} };

        # And now an extra one to make up for the subtotal row not having one.
        #
        push @totalRowProps, { font => $font->{Helvetica}{Bold} };

    } else {
        push @totalRows, [ 'Total:', formatMoney( $statement->subtotal ) ];
        push @totalRowProps, { font => $font->{Helvetica}{Bold} };
    }

    $y -= 5 / pt;

    my $table = TableObj->new(
        $pdf, $page, \@totalRows,
        bottom_margin => kBottomMargin + 20,          # adding a bit of padding to avoid weirdness
        new_page_y    => kTopMargin - 10,             # ditto
        new_page_func => \&newPayeePage,
        font          => $font->{Helvetica}{Roman},
        font_size     => $fontSize,
        column_props  => [ {
                justify => 'right',
                min_w   => 9 / in,
                pad     => 0,
                font    => $font->{Helvetica}{Bold},
            },
            {
                justify => 'right',
                min_w   => 1 / in,
                pad     => 0,
            },
        ],
        row_props => \@totalRowProps,
    );
    ( $page, $y ) = $table->print( $x, $y );

    return ( $x, $y, $page );
}

sub payeeTrackDetails {
    my ( $track, $statement, $rows, $rowDetails ) = @_;

    payeeCrossedLicenseDetails( $track, $statement, $rows, $rowDetails );
    payeeLicenseDetails( $track, $statement, $rows, $rowDetails );
}

sub payeeCrossedLicenseDetails {
    my ( $statementTrack, $statement, $rows, $rowProps ) = @_;

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

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

    my $licenseCount = 0;

    my $lastRow;
    my $lastCellArray;

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

        next if ( !$license->crossed );

        $licenseCount++;

        my $incomeItemCollection =
          RPS::DB::Item::CAMechanicalStatementItem->GetByStatementAndTrackLicense( $statement->ca_mechanical_statement_id,
            $license->ca_track_license_id );

        my $itemCount = 0;

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

            # We need to grab the album title from the product.
            # If it's a track product, we'll need to grab it from the parent product.
            my $product = RPS::DB::Item::Product->Lookup( product_id => $incomeItem->product_id );
            my $albumTitle = _getAlbumTitle( $product, $album->title );

            # We have to check the license product type id to see
            # if we're dealing with a ringtone.
            #
            # JPK - I don't think ringtone is relevant for CA mechanicals, but this shouldn't hurt anything.
            #
            my $productTypeID = _checkForRingtone( $statementTrackLicense, $incomeItem );

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

            push @$rows,
              [
                $track->title,                                       $albumTitle,
                $incomeItem->upc,                                    productCodeToName($productTypeID),
                regionIDToName( $statementTrackLicense->region_id ), $ratePeriod,
                formatPercent( $statementTrackLicense->share ),      formatNumber( $incomeItem->net_rate ),
                formatNumber( $incomeItem->net_units ),              formatMoney( $incomeItem->amount_paid ),
              ];

            if ( ( $itemCount == 1 ) && ( $licenseCount != 1 ) ) {
                push @$rowProps, { pad => 8, };
            } else {
                push @$rowProps, {};
            }

        }

        if ( $itemCount == 0 ) {

            # Even if there are no income items,
            # we still want to display the track info.
            push @$rows, [ $track->title(), $album->title(), ' ', ' ', regionIDToName( $license->region_id() ), ' ', ' ', ' ', ' ', ' ', ];

            push @$rowProps, { pad => 8, };

        }

        # Add the controlled comp adjustments
        #

        my $adjustmentCollection = RPS::DB::Item::CAMechanicalStatementAdjustmentItem->GetByMechanicalStatementIDTrackLicenseID(
            $statement->ca_mechanical_statement_id,
            $license->ca_track_license_id );

        while ( $adjustmentCollection->hasNext() ) {
            my $adjustment = $adjustmentCollection->next();

            push @$rows, [ ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', 'Controlled Comp Adjustment:', formatMoney( $adjustment->amount ) ];

            push @$rowProps, {};

        }

        #  Now we want to display the subtotal, previous balance, transactions, etc. for this license
        #

        # We want a little line above the subtotal.
        #
        my $numRows = scalar(@$rowProps);
        my $lastRowPropHashref = $$rowProps[ ( $numRows - 1 ) ];
        $lastRowPropHashref->{lines} = [ {
                start_col => 9,
                end_col   => 9,
                top_pad   => 2,

                # bottom_pad => 2,
            },
        ];

        # 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();

        push @$rows, [ ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', 'Current Period License Subtotal:', formatMoney( $license->subtotal ) ];

        push @$rowProps, { pad => -1, };

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

        my $transactionCount = 0;

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

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

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

            push @$rows, [ ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', $memo . $transactionType, formatMoney( $licenseTransaction->amount ) ];
            push @$rowProps, {};
        }

        if ( $transactionCount > 0 ) {

            # We want a little line under the last transaction.
            #
            my $numRows = scalar(@$rowProps);
            my $lastRowPropHashref = $$rowProps[ ( $numRows - 1 ) ];
            $lastRowPropHashref->{lines} = [ {
                    start_col => 9,
                    end_col   => 9,
                    top_pad   => 2,

                    # bottom_pad => 2,
                },
            ];
        }

        if ( ( $previousBalance != 0 ) || ( $license->advance_balance != 0 ) ) {
            push @$rows, [ ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', 'Previous License Balance:', formatMoney($previousBalance) ];

            push @$rowProps, {
                lines => [ {
                        start_col => 9,
                        end_col   => 9,
                        top_pad   => 2,

                        # bottom_pad => 2,
                    },
                ],
            };

            my $licenseBalance = $license->advance_balance;

            push @$rows, [ ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', 'License Balance Forward:', formatMoney($licenseBalance) ];
            push @$rowProps, {
                pad   => -1,
                lines => [ {
                        start_col => 9,
                        end_col   => 9,
                        top_pad   => 2,

                        # bottom_pad => 2,
                    },
                ],
            };
        }

        # 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;

            push @$rows, [ ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', 'License Adjusted Subtotal:', formatMoney($licenseBalance) ];
            push @$rowProps, { pad => -1, };
        }

        $licenseCount++;
    }

    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;

        push @$rows,
          [ ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', 'Current Period Crossed Subtotal:', formatMoney( $statementTrack->crossed_subtotal ) ];
        push @$rowProps, { pad => 8, };

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

        my $crossedTransactionCount = 0;

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

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

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

            push @$rows, [ ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', $memo . $transactionType, formatMoney( $crossedTransaction->amount ) ];
            push @$rowProps, {};
        }

        if ( $crossedTransactionCount > 0 ) {

            # We want a little line under the last transaction.
            #
            my $numRows = scalar(@$rowProps);
            my $lastRowPropHashref = $$rowProps[ ( $numRows - 1 ) ];
            $lastRowPropHashref->{lines} = [ {
                    start_col => 9,
                    end_col   => 9,
                    top_pad   => 2,
                },
            ];
        }

        if ( ( $previousBalance != 0 ) || ( $statementTrack->crossed_advance_balance != 0 ) ) {
            push @$rows, [ ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', 'Previous Crossed Balance:', formatMoney($previousBalance) ];

            push @$rowProps,
              {
                lines => [ {
                        start_col => 9,
                        end_col   => 9,
                        top_pad   => 2,
                    },
                ],
              };

            my $licenseBalance = $statementTrack->crossed_advance_balance;

            push @$rows, [ ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', 'Crossed Balance Forward:', formatMoney($licenseBalance) ];

            push @$rowProps, { pad => -1, };

        }

        # And really, truly finally, the adjusted subtotal if need be.
        #
        if (   ( $crossedTransactionCount != 0 )
            || ( $previousBalance != 0 )
            || ( $statementTrack->crossed_subtotal != $statementTrack->crossed_adjusted_subtotal ) ) {
            my $licenseBalance = $statementTrack->crossed_adjusted_subtotal;

            push @$rows, [ ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', 'Crossed Adjusted Subtotal:', formatMoney($licenseBalance) ];
            push @$rowProps, {};
        }

        # We want the last item to have a line underneath it.
        pop(@$rowProps);
        push @$rowProps,
          {
            pad   => 8,
            lines => [ {
                    start_col => 0,
                    end_col   => 9,
                    top_pad   => 2,
                },
            ],
          };

    }
}

sub payeeLicenseDetails {
    my ( $statementTrack, $statement, $rows, $rowProps ) = @_;

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

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

    my $licenseCount = 0;

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

        next if ( $license->crossed );

        $licenseCount++;

        my $incomeItemCollection =
          RPS::DB::Item::CAMechanicalStatementItem->GetByStatementAndTrackLicense( $statement->ca_mechanical_statement_id,
            $license->ca_track_license_id );

        my $itemCount = 0;

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

            # We need to grab the album title from the product.
            # If it's a track product, we'll need to grab it from the parent product.
            my $product = RPS::DB::Item::Product->Lookup( product_id => $incomeItem->product_id );
            my $albumTitle = _getAlbumTitle( $product, $album->title );

            # 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);

            push @$rows,
              [
                $track->title,                                  $albumTitle,
                $incomeItem->upc,                               productCodeToName($productTypeID),
                regionIDToName( $license->region_id ),          $ratePeriod,
                formatPercent( $statementTrackLicense->share ), formatNumber( $incomeItem->net_rate ),
                formatNumber( $incomeItem->net_units ),         formatMoney( $incomeItem->amount_paid ),
              ];

            push @$rowProps, {};

        }

        if ( $itemCount == 0 ) {

            # Even if there are no income items,
            # we still want to display the track info.
            push @$rows, [ $track->title, $album->title, ' ', ' ', regionIDToName( $license->region_id ), ' ', ' ', ' ', ' ', ' ', ];

            push @$rowProps, {};

        }

        # Add the controlled comp adjustments
        #

        my $adjustmentCollection = RPS::DB::Item::CAMechanicalStatementAdjustmentItem->GetByMechanicalStatementIDTrackLicenseID(
            $statement->ca_mechanical_statement_id,
            $license->ca_track_license_id );

        while ( $adjustmentCollection->hasNext() ) {
            my $adjustment = $adjustmentCollection->next();

            push @$rows, [ ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', 'Controlled Comp Adjustment:', formatMoney( $adjustment->amount ) ];

            push @$rowProps, {};

        }

        # We want a little line above the total
        #
        my $numRows = scalar(@$rowProps);
        my $lastRowPropHashref = $$rowProps[ ( $numRows - 1 ) ];
        $lastRowPropHashref->{lines} = [ {
                start_col => 9,
                end_col   => 9,
                top_pad   => 2,

                # bottom_pad => 2,
            },
        ];

        # 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;

        push @$rows, [ ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', 'Current Period License Subtotal:', formatMoney( $license->subtotal ) ];

        push @$rowProps, { pad => -1, };

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

        my $transactionCount = 0;

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

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

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

            push @$rows, [ ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', $memo . $transactionType, formatMoney( $licenseTransaction->amount ) ];
            push @$rowProps, {};
        }

        if ( $transactionCount > 0 ) {

            # We want a little line under the last transaction.
            #
            my $numRows = scalar(@$rowProps);
            my $lastRowPropHashref = $$rowProps[ ( $numRows - 1 ) ];
            $lastRowPropHashref->{lines} = [ {
                    start_col => 9,
                    end_col   => 9,
                    top_pad   => 2,
                },
            ];
        }

        if ( ( $previousBalance != 0 ) || ( $license->advance_balance != 0 ) ) {
            push @$rows, [ ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', 'Previous License Balance:', formatMoney($previousBalance) ];

            push @$rowProps,
              {
                lines => [ {
                        start_col => 9,
                        end_col   => 9,
                        top_pad   => 2,
                    },
                ],
              };

            my $licenseBalance = $license->advance_balance;

            push @$rows, [ ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', 'License Balance Forward:', formatMoney($licenseBalance) ];
            push @$rowProps,
              {
                pad   => -1,
                lines => [ {
                        start_col => 9,
                        end_col   => 9,
                        top_pad   => 2,
                    },
                ],
              };
        }

        # 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;

            push @$rows, [ ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', 'License Adjusted Subtotal:', formatMoney($licenseBalance) ];
            push @$rowProps, {};
        }

        # We want the last item to have a line underneath it.
        pop(@$rowProps);
        push @$rowProps,
          {
            pad   => -1,
            lines => [ {
                    start_col => 0,
                    end_col   => 9,
                    top_pad   => 2,
                },
            ],
          };

    }
}

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

    my $pageCount = 1;
    my $numPages  = scalar @gPayeePages;
    foreach my $page (@gPayeePages) {
        my $text = $page->text;

        printText(
            page     => $page,
            display  => "$pageCount of $numPages",
            y        => kBottomMargin,
            font     => $font->{Helvetica}{Roman},
            fontSize => 12 / pt,
            centered => 1
        );

        $pageCount++;
    }
}

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

    my $page = $pdf->page;

    # the mediabox is the size of the paper.
    #
    $page->mediabox( kPageWidth, kPageHeight );

    # the cropbox defines the margins, essentially.
    # the args are the coordinates for the left, bottom, right, and top (in that order)
    #

    # !!! Rather than set the cropbox right to the margins, I am going to make it larger.
    # !!! Basically, I want to have this thing render in the viewer with the appearance of having
    # !!! margins.
    #
    #    $page->cropbox(kLeftMargin, kBottomMargin, kRightMargin, kTopMargin);
    $page->cropbox( 5, 5, kPageWidth - 5, kPageHeight - 5 );

    # Not currently declaring a 'bleedbox' or an 'artbox'.  Check out the tutorial if you want to
    # know what those are... the url for that is at the top of this file.
    #
    push @gPayeePages, $page;

    return $page;
}

##########################
# Shared Statement Stuff #
##########################

sub drawBalanceTable {
    my ( $pdf, $statement, $x, $y, $page, $newPageFuncRef ) = @_;
    my $fontSize = 7 / pt;

    my $text = $page->text();

    # We'll create a 2-d array that contains the data we want to display.
    #
    my @balanceArray;

    # This array will keep track of various row properties.
    # We're going to change the font for a couple of rows (such as the header),
    # and do a few more tricks.
    #
    my @rowProperties;

    # The column headers
    #
    push @balanceArray, [ ' ', 'Amount', 'Check #', 'Date', 'Memo' ];
    push @rowProperties, { font => $font->{Helvetica}{Bold}, };

    # The first line
    #
    push @balanceArray, [ "Previous Period Balance", formatMoney( $statement->previous_balance() ), ' ', ' ', ' ' ];
    push @rowProperties, {};

    # Get any transactions
    #
    my $transactionCollection =
      RPS::DB::Item::CAMechanicalStatementTransaction->GetByMechanicalStatementID( $statement->ca_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;

        push @balanceArray, [ $type, $amount, $checkNum, $date, $memo ];
        push @rowProperties, {};
    }

    push @balanceArray, [ 'Current Period Royalties:', formatMoney( $statement->subtotal ), ' ', ' ', ' ' ];
    push @rowProperties, {
        lines => [ {

                # pen_size => 1,
                # color => 'black',
                start_col => 0,
                end_col   => 1,
                top_pad   => 4,

                # bottom_pad => 2,
            },
        ],
    };

    if ( $statement->previous_advance_balance != 0 ) {
        push @balanceArray, [ 'Previous Advance Balance:', formatMoney( $statement->previous_advance_balance() ), ' ', ' ', ' ' ];
        push @rowProperties, {};
        push @balanceArray, [ 'Applied To Advance:', formatMoney( $statement->applied_to_advance() ), ' ', ' ', ' ' ];
        push @rowProperties, {};
        push @balanceArray, [ 'Current Advance Balance:', formatMoney( $statement->advance_balance() ), ' ', ' ', ' ' ];
        push @rowProperties, {
            lines => [ {

                    # pen_size => 1,
                    # color => 'black',
                    start_col => 0,
                    end_col   => 1,
                    top_pad   => 4,

                    # bottom_pad => 2,
                },
            ],
        };
    }

    # 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;
    }

    push @balanceArray, [ 'Ending Balance:', formatMoney($endingBalance), ' ', ' ', ' ' ];
    push @rowProperties, { font => $font->{Helvetica}{Bold} };

    push @balanceArray, [ ' ', ' ', ' ', ' ', ' ' ];
    push @rowProperties, {};

    push @balanceArray, [ 'Minimum Payment:', formatMoney( $statement->min_payment() ), ' ', ' ', ' ' ];
    push @rowProperties, {};

    my $tableObj = TableObj->new(
        $pdf, $page, \@balanceArray,
        bottom_margin => kBottomMargin + 20,          # adding a bit of padding to avoid weirdness
        new_page_y    => kTopMargin - 20,             # ditto
        new_page_func => $newPageFuncRef,
        font          => $font->{Helvetica}{Roman},
        font_size     => $fontSize,
        column_props  => [ {
                font      => $font->{Helvetica}{Bold},
                font_size => $fontSize,

                #                justify => 'center',
                min_w => 1.25 / in,
                max_w => 1.25 / in,
            },
            {
                font      => $font->{Helvetica}{Roman},
                font_size => $fontSize,
                justify   => 'right',
                min_w     => 1.25 / in,
                max_w     => 1.25 / in,
            },
            {
                font      => $font->{Helvetica}{Roman},
                font_size => $fontSize,

                #                justify => 'right',
                min_w => 1.0 / in,
                pad   => ( 1 / 4 ) / in,
            },
            {
                font      => $font->{Helvetica}{Roman},
                font_size => $fontSize,

                #                justify => 'right',
                min_w => 1.0 / in,
            },
            {
                font      => $font->{Helvetica}{Roman},
                font_size => $fontSize,

                #justify => 'right',
                min_w => 1.0 / in,
            },
        ],
        row_props => \@rowProperties,
    );

    ( $page, $y ) = $tableObj->print( $x, $y );

    $y -= 2;

    my @amountDueArray;
    my @amountDueProperties;

    my $amountDue    = formatMoney( $statement->amount_due );
    my $currencyCode = ' (CAD)';

    # If the main payee is on hold, clear that data out.
    #
    if ( $statement->on_hold == 1 ) {
        $amountDue    = "ON-HOLD";
        $currencyCode = '';
    }

    push @amountDueArray, [ ' ', ' ', ' ' ];
    push @amountDueProperties, {
        lines => [ {
                # pen_size => 1,
                # color => 'black',
                start_col  => 0,
                end_col    => 2,
                top_pad    => 2,
                bottom_pad => 0,
            },
        ],
    };

    push @amountDueArray, [ 'Amount Payable:', $amountDue, $currencyCode ];
    push @amountDueProperties, {
        font  => $font->{Helvetica}{Bold},
        lines => [ {
                # pen_size => 1,
                # color => 'black',
                start_col  => 0,
                end_col    => 2,
                top_pad    => 5,
                bottom_pad => 0,
            },
        ],
    };

    my $bigFontSize = 10 / pt;

    my $tableObj = TableObj->new(
        $pdf, $page, \@amountDueArray,
        bottom_margin => kBottomMargin + 20,          # adding a bit of padding to avoid weirdness
        new_page_y    => kTopMargin - 20,             # ditto
        new_page_func => $newPageFuncRef,
        font          => $font->{Helvetica}{Roman},
        font_size     => $bigFontSize,
        column_props  => [ {
                font      => $font->{Helvetica}{Bold},
                font_size => $bigFontSize,
                min_w     => 1.25 / in,
                max_w     => 1.25 / in,
            },
            {
                font      => $font->{Helvetica}{Roman},
                font_size => $bigFontSize,
                justify   => 'right',
                min_w     => 1.25 / in,
                max_w     => 1.25 / in,
            },
            {
                font      => $font->{Helvetica}{Roman},
                font_size => $bigFontSize,
                justify   => 'left',
                min_w     => 1.0 / in,
            },
        ],
        row_props => \@amountDueProperties,
    );

    ( $page, $y ) = $tableObj->print( $x, $y );

    return ( $x, $y, $page );
}

##########################
# Full Statement Stuff   #
##########################

sub createFullStatementPDF {
    my ( $statement, $outFilePath ) = @_;

    # We need to tweak the $outFilePath to avoid over-writing the payee version.
    #
    my @pathArray = split( "/", $outFilePath );
    $pathArray[$#pathArray] = "FULL_" . $pathArray[$#pathArray];
    $outFilePath = join( "/", @pathArray );

    # Instantiate the PDF object.
    #
    my $pdf = PDF::API2->new( -file => $outFilePath );

    # Fetch the fonts that we might want to use.
    # Note that every time we define a font, it will get embedded in the document,
    # so we will grab them all here at the very beginning.
    # !!! Be sure to remove any of these we don't actually need...
    #
    # (These are the 'built-in' fonts that all pdf documents get for free. We can embed fancy
    #  fonts if we want to, but that will make the statement files larger...)
    #
    $font = {
        Helvetica => {
            Bold   => $pdf->corefont( 'Helvetica-Bold',    -encoding => 'latin1' ),
            Roman  => $pdf->corefont( 'Helvetica',         -encoding => 'latin1' ),
            Italic => $pdf->corefont( 'Helvetica-Oblique', -encoding => 'latin1' ),
        },
        Times => {
            Bold   => $pdf->corefont( 'Times-Bold',   -encoding => 'latin1' ),
            Roman  => $pdf->corefont( 'Times',        -encoding => 'latin1' ),
            Italic => $pdf->corefont( 'Times-Italic', -encoding => 'latin1' ),
        },
    };

    # Declare the 'cursor' coordinates.  As we go, we'll be updating these values so that the
    # next element shows up in the right place.
    #
    my $x = kLeftMargin;
    my $y = kTopMargin;

    # Create the first (blank) page.
    # Each output subroutine will take the current page as a parameter, and will
    # return the page that they ended on.
    #
    my $page = newFullPage($pdf);

    # Each section of the statement will have its own subroutine.
    #
    ( $x, $y, $page ) = fullHeader( $pdf, $statement, $x, $y, $page );
    ( $x, $y, $page ) = fullSummary( $pdf, $statement, $x, $y, $page );

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

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

        # Loop through all publishers for admins.
        #
        my $publisherList =
          RPS::DB::Item::CAMechanicalStatementPublisher->GetSortedByMechanicalStatementID( $statement->ca_mechanical_statement_id );
        while ( my $subPublisherItem = $publisherList->next() ) {
            ( $x, $y, $page ) = fullDetails( $pdf, $subPublisherItem, $x, $y, $page );
        }
    } else {

        # Not an admin, so just do this once.
        ( $x, $y, $page ) = fullDetails( $pdf, $statement, $x, $y, $page );
    }

    # If the state of the run is not 'COMMITTED' or 'CLOSED', then we want to add a watermark
    # to the pages so that our users don't mail this statement out.
    #
    my $run = RPS::DB::Item::CAMechanicalRun->Lookup( ca_mechanical_run_id => $statement->ca_mechanical_run_id );
    if (   RPS::RoyaltyRun::Status::kCommitted != $run->status
        && RPS::RoyaltyRun::Status::kClosed != $run->status ) {
        addDraftWatermarkFull($pdf);
    }

    # Now add the page numbers, and other 'footer' stuff
    #
    fullFooter($pdf);

    # All done, save and clean up
    #
    $pdf->save();
    $pdf->end();
}

sub fullHeader {
    my ( $pdf, $statement, $x, $y, $page ) = @_;

    my $text = $page->text();

    $y = printText(
        page     => $page,
        display  => 'Canadian Mechanical Royalty Statement',
        y        => $y,
        font     => $font->{Helvetica}{Bold},
        fontSize => 12 / pt,
        centered => 1
    );

    # Now print the run label. Which means we need to fetch the run...
    #
    my $run = RPS::DB::Item::CAMechanicalRun->Lookup( ca_mechanical_run_id => $statement->ca_mechanical_run_id );
    $y -= 9 / pt;
    my $label = Common::Client::Current()->Locale()->formatDate( $run->start_date() ) . ' - '
      . Common::Client::Current()->Locale()->formatDate( $run->end_date() );

    $y = printText(
        page     => $page,
        display  => $label,
        x        => $x,
        y        => $y,
        font     => $font->{Helvetica}{Bold},
        fontSize => 9 / pt
    );

    # Print the links to download the payee pdf
    # and the text version.
    #

    #$y -= 15 / pt;

    #$y = printText(
    #    page        => $page,
    #    display     => "Publisher View",
    #    link        => '/rps/statement?c=pdf_mechanical&MechanicalStatementID=' . $statement-#>MechanicalStatementID(),
    #    x           => $x + 20,
    #    y           => $y,
    #    font        => $font->{Helvetica}{Roman},
    #    fontSize    => 9 / pt
    #);

    #EmbedImage::addIconGIF(
    #    page    =>  $page,
    #    pdf     =>  $pdf,
    #    source  =>  '/app/tools/common/production/images/pdficon_small.gif',
    #    link    =>  '/rps/statement?c=pdf_mechanical&MechanicalStatementID=' . $statement-#>MechanicalStatementID(),
    #    width   =>  17,
    #    height  =>  17,
    #    x       =>  $x,
    #    y       =>  $y
    #);

    #$y -= 15 / pt;

    #$y = printText(
    #    page        => $page,
    #    display     => "Electronic Format",
    #    link        => '/rps/statement?c=text_mechanical&MechanicalStatementID=' . $statement-#>MechanicalStatementID(),
    #    x           => $x + 20,
    #    y           => $y,
    #    font        => $font->{Helvetica}{Roman},
    #    fontSize    => 9 / pt
    #);

    #EmbedImage::addIconGIF(
    #    page    =>  $page,
    #    pdf     =>  $pdf,
    #    source  =>  '/app/tools/common/production/images/icons/office/Document-(16x16).gif',
    #    link    =>  '/rps/statement?c=text_mechanical&MechanicalStatementID=' . $statement-#>MechanicalStatementID(),
    #    width   =>  16,
    #    height  =>  16,
    #    x       =>  $x,
    #    y       =>  $y
    #);

    $y -= 9 / pt;

    my $payee = RPS::DB::Item::CAPublisher->Lookup( ca_publisher_id => $statement->ca_publisher_id );

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

    if ( $payee->postal_code ) {
        $cityStateLine .= " " . $payee->postal_code;
    }

    my $publisherID = $payee->ca_publisher_id;

    $y = printText(
        page     => $page,
        display  => $payee->publisher_name,
        link     => '/rps/ca_publisher?PublisherID=' . $publisherID . '&c=show',
        x        => $x,
        y        => $y,
        font     => $font->{Helvetica}{Roman},
        fontSize => 9 / pt
    );

    $y = printText(
        page     => $page,
        display  => $payee->street_address,
        x        => $x,
        y        => $y,
        font     => $font->{Helvetica}{Roman},
        fontSize => 9 / pt
    );

    $y = printText(
        page     => $page,
        display  => $payee->street_address_2,
        x        => $x,
        y        => $y,
        font     => $font->{Helvetica}{Roman},
        fontSize => 9 / pt
    );

    $y = printText(
        page     => $page,
        display  => $payee->street_address_3,
        x        => $x,
        y        => $y,
        font     => $font->{Helvetica}{Roman},
        fontSize => 9 / pt
    );

    $y = printText(
        page     => $page,
        display  => $cityStateLine,
        x        => $x,
        y        => $y,
        font     => $font->{Helvetica}{Roman},
        fontSize => 9 / pt
    );

    $y = printText(
        page     => $page,
        display  => $payee->country_code,
        x        => $x,
        y        => $y,
        font     => $font->{Helvetica}{Roman},
        fontSize => 9 / pt
    );

    $y -= 15 / pt;

    return ( $x, $y, $page );
}

sub fullSummary {
    my ( $pdf, $statement, $x, $y, $page ) = @_;

    ( $x, $y, $page ) = drawBalanceTable( $pdf, $statement, $x, $y, $page, \&newFullPage );

    my $publisher = RPS::DB::Item::CAPublisher->Lookup( ca_publisher_id => $statement->ca_publisher_id );

    # Determine if this is an admin or regular publisher.
    if ( $publisher->is_admin == 1 || $publisher->is_agency == 1 ) {
        ( $x, $y, $page ) = fullPublisherTable( $pdf, $statement, $x, $y, $page );
    }

    #$y -= 18;

    return ( $x, $y, $page );
}

sub fullPublisherTable {
    my ( $pdf, $statement, $x, $y, $page ) = @_;

    # First, a little room please.
    $y -= 16;

    my $fontSize = 7 / pt;

    my $text = $page->text();

    # We'll create a 2-d array that contains the data we want to display.
    #
    my @publisherArray;

    # We'll create a similar thing for hyper-links.
    #
    my @linkArray;

    # This array will keep track of various row properties.
    # We're going to change the font for a couple of rows (such as the header),
    # and do a few more tricks.
    #
    my @rowProperties;

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

        # The column headers
        #
        push @publisherArray, [ 'Publisher', 'Amount Due' ];
        push @linkArray,      [ '',          '' ];
        push @rowProperties,
          {
            font  => $font->{Helvetica}{Bold},
            lines => [ {
                    start_col => 0,
                    end_col   => 1,
                    top_pad   => 4,
                },
            ],
          };

        while ( my $publisher = $publisherList->next() ) {
            my $publisherItem = RPS::DB::Item::CAPublisher->Lookup( ca_publisher_id => $publisher->ca_publisher_id );
            my $name          = $publisherItem->publisher_name();
            my $amount        = formatMoney( $publisher->amount_due() );

            #            if ($statement->on_hold == 1 || $publisher->on_hold == 1)
            if ( $publisher->on_hold == 1 ) {
                $amount = 'ON-HOLD';
            }

            $amount = ' ' unless defined $amount;

            push @publisherArray, [ $name, $amount ];
            push @linkArray, [ '#PUBLISHER_DEST_' . $publisherItem->ca_publisher_id, '' ];
            push @rowProperties, {};
        }

        my $tableObj = TableObj->new(
            $pdf, $page, \@publisherArray,
            bottom_margin => kBottomMargin + 20,          # adding a bit of padding to avoid weirdness
            new_page_y    => kTopMargin - 20,             # ditto
            new_page_func => \&newFullPage,
            font          => $font->{Helvetica}{Roman},
            font_size     => $fontSize,
            column_props  => [ {
                    min_w     => 0.5 / in,
                    font      => $font->{Helvetica}{Roman},
                    font_size => $fontSize,
                },
                {
                    min_w     => 0.5 / in,
                    font      => $font->{Helvetica}{Roman},
                    font_size => $fontSize,
                    justify   => 'right',
                },
            ],
            row_props => \@rowProperties,
            row_links => \@linkArray,
        );

        ( $page, $y ) = $tableObj->print( $x, $y );
    }

    return ( $x, $y, $page );
}

sub fullDetails {
    my ( $pdf, $statement, $x, $y, $page ) = @_;
    my $fontSize = 7 / pt;

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

    # start details on a new page
    $page = newFullPage($pdf);

    $y = kTopMargin - 10;

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

        $y = printText(
            page     => $page,
            display  => $publisherItem->publisher_name,
            y        => $y,
            font     => $font->{Helvetica}{Bold},
            fontSize => 12 / pt,
            centered => 1,
            link     => '/rps/ca_publisher?c=show&PublisherID=' . $publisherItem->ca_publisher_id
        );

        # Also, set up the named destination for the internal link
        #
        my $destName = 'PUBLISHER_DEST_' . $publisherItem->ca_publisher_id;
        my $nd = $pdf->named_destination( 'Dests', $destName );
        $nd->link($page);
        TableObj::updatePendingLinks( $pdf, $destName, $nd );
    }

    my @rows;
    my @rowProps;
    my @links;

    # Set up the header
    #
    push @rows,  [ ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ' ];
    push @links, [ '',  '',  '',  '',  '',  '',  '',  '',  '',  '',  '',  '',  '',  '',  '',  '',  '',  '',  '',  '',  '' ];
    push @rowProps, {
        font  => $font->{Helvetica}{Bold},
        lines => [ {
                start_col => 0,
                end_col   => 20,
                top_pad   => 4,

                # bottom_pad => 2,
            },
        ],
    };
    push @rows,
      [
        '',     '',     '', '', 'Product', '', '',       'Base', '% of', '', 'Net', 'Gross',
        '% of', 'Free', '', '', 'Liquid-', '', 'Carry-', 'Net',  'Amount'
      ];
    push @links, [ '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '' ];
    push @rowProps,
      {
        font       => $font->{Helvetica}{Bold},
        repeatRows => 1,
      };
    push @rows,
      [
        '',       'Track Title', 'Product Title', 'UPC',   'Config', 'Region', 'Rate Period', 'Rate',
        'Rate',   'Share',       'Rate',        'Units', 'Sales',  'Goods',  'Misc',        'Reserves',
        'ations', 'Returns',     'over',        'Units', 'Due'
      ];
    push @links, [ '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '' ];
    push @rowProps, {
        font       => $font->{Helvetica}{Bold},
        repeatRows => 1,
        lines      => [ {
                start_col => 0,
                end_col   => 20,
                top_pad   => 4,

                # bottom_pad => 2,
            },
        ],
    };

    # Add the track details.
    #
    my $statementTrackCollection;
    if ( 'RPS::DB::Item::CAMechanicalStatementPublisher' eq $whatAmI ) {
        $statementTrackCollection =
          RPS::DB::Item::CAMechanicalStatementTrack->GetSortedByMechanicalStatementIDPublisherID( $statement->ca_mechanical_statement_id,
            $statement->ca_publisher_id );
    } else {
        $statementTrackCollection =
          RPS::DB::Item::CAMechanicalStatementTrack->GetSortedByMechanicalStatementID( $statement->ca_mechanical_statement_id );
    }

    while ( $statementTrackCollection->hasNext() ) {
        my $statementTrack = $statementTrackCollection->next();
        fullTrackDetails( $statementTrack, $statement, \@rows, \@rowProps, \@links );
    }

    # Force the columns to be fixed width.
    #
    my $colProps = fullDetailColProps();

    my $tableObj = TableObj->new(
        $pdf,
        $page,
        \@rows,
        bottom_margin => kBottomMargin + 20,
        new_page_y    => kTopMargin - 20,
        new_page_func => \&newFullPage,
        font          => $font->{Helvetica}{Roman},
        font_size     => $fontSize,
        column_props  => $colProps,
        row_props     => \@rowProps,
        row_links     => \@links,
    );

    ( $page, $y ) = $tableObj->print( $x, $y );

    # Last, print the grand total
    #

    my @totalRows;
    my @totalRowProps;

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

        # Add the transactions in.
        #
        my $transactionCollection = RPS::DB::Item::CAMechanicalStatementPublisherTransaction->GetByMechanicalStatementAndPublisherID(
            $statement->ca_mechanical_statement_id,
            $statement->ca_publisher_id );

        my $transactionCount = $transactionCollection->size();

        if ( $previousBalance != 0 || $transactionCount > 0 ) {
            push @totalRows, [ 'Publisher Subtotal:', formatMoney( $statement->subtotal() ) ];

            if ( $previousBalance != 0 ) {
                push @totalRows, [ 'Previous Balance:', formatMoney( $statement->previous_balance() ) ];
                push @totalRowProps, {};
            }

            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';
                }
                push @totalRows, [ "$type:", formatMoney( $transaction->amount ) ];
                push @totalRowProps, {};
            }

            push @totalRows, [ 'Ending Balance:', formatMoney( $statement->statement_total ) ];

            # Note that this will actually go above the ending balance, since we did't add an element to the
            # array for the subtotal.
            #
            push @totalRowProps,
              {
                lines => [ {
                        start_col  => 1,
                        end_col    => 1,
                        top_pad    => 2,
                        bottom_pad => -1,
                    },
                ],
              };

            push @totalRows, [];
            push @totalRowProps, { font => $font->{Helvetica}{Bold} };
        }

        my $amountDue;
        my $parentStatement =
          RPS::DB::Item::CAMechanicalStatement->Lookup( ca_mechanical_statement_id => $statement->ca_mechanical_statement_id );

        #        if ($parentStatement->on_hold == 1 || $statement->on_hold == 1)
        if ( $statement->on_hold == 1 ) {
            $amountDue = "ON-HOLD";
        } else {
            $amountDue = formatMoney( $statement->amount_due );
        }

        push @totalRows, [ 'Publisher Amount Due:', $amountDue ];
        push @totalRowProps, { font => $font->{Helvetica}{Bold} };

        # And now an extra one to make up for the subtotal row not having one.
        #
        push @totalRowProps, { font => $font->{Helvetica}{Bold} };

    } else {
        push @totalRows, [ 'Total:', formatMoney( $statement->subtotal ) ];
        push @totalRowProps, { font => $font->{Helvetica}{Bold} };
    }

    $y -= 5 / pt;

    my $table = TableObj->new(
        $pdf, $page,
        \@totalRows,
        bottom_margin => kBottomMargin + 20,
        new_page_y    => kTopMargin - 10,
        new_page_func => \&newFullPage,
        font          => $font->{Helvetica}{Roman},
        font_size     => $fontSize,
        column_props  => [ {
                justify => 'right',
                min_w   => 9.39 / in,
                max_w   => 9.39 / in,
                pad     => 0,
                font    => $font->{Helvetica}{Bold},
            },
            {
                justify => 'right',
                min_w   => .61 / in,
                max_w   => .61 / in,
                pad     => 0,
            },
        ],
        row_props => \@totalRowProps,
    );
    ( $page, $y ) = $table->print( $x, $y );

    return ( $x, $y, $page );
}

sub fullDetailColProps {
    return [ {

            # license icon
            min_w => .15 / in,
            max_w => .15 / in,
            pad   => 0,
            image => "gif",
        },
        {

            # track title
            min_w => 0.9 / in,
            max_w => 0.9 / in,
            pad   => 0,
        },
        {

            # album title
            min_w => 0.75 / in,
            max_w => 0.75 / in,
            pad   => 0,
        },
        {

            # upc
            min_w => 0.7 / in,
            max_w => 0.7 / in,
            pad   => 0,
        },
        {

            # product (config)
            min_w => 0.41 / in,
            max_w => 0.41 / in,
            pad   => 0,
        },
        {

            # region
            min_w => 0.36 / in,
            max_w => 0.36 / in,
            pad   => 0,
        },
        {

            # rate period
            min_w => 0.6 / in,
            max_w => 0.6 / in,
            pad   => 0,
        },
        {

            # base rate
            min_w => 0.35 / in,
            max_w => 0.35 / in,
            pad   => 0,
        },
        {

            # % of rate
            min_w => 0.5 / in,
            max_w => 0.5 / in,
            pad   => 0,
        },
        {

            # share
            min_w => 0.45 / in,
            max_w => 0.45 / in,
            pad   => 0,
        },
        {

            # net rate
            min_w => 0.4 / in,
            max_w => 0.4 / in,
            pad   => 0,
        },
        {

            # gross units
            justify => 'right',
            min_w   => 0.5 / in,
            max_w   => 0.5 / in,
            pad     => 0,
        },
        {

            # % of sales
            justify => 'right',
            min_w   => 0.42 / in,
            max_w   => 0.42 / in,
            pad     => 0,
        },
        {

            # free (goods)
            justify => 'right',
            min_w   => 0.35 / in,
            max_w   => 0.35 / in,
            pad     => 0,
        },
        {

            # misc
            justify => 'right',
            min_w   => 0.35 / in,
            max_w   => 0.35 / in,
            pad     => 0,
        },
        {

            # reserves
            justify => 'right',
            min_w   => 0.5 / in,
            max_w   => 0.5 / in,
            pad     => 0,
        },
        {

            # liquidations
            justify => 'right',
            min_w   => 0.4 / in,
            max_w   => 0.4 / in,
            pad     => 0,
        },
        {

            # returns
            justify => 'right',
            min_w   => 0.45 / in,
            max_w   => 0.45 / in,
            pad     => 0,
        },
        {

            # carryover
            justify => 'right',
            min_w   => 0.4 / in,
            max_w   => 0.4 / in,
            pad     => 0,
        },
        {

            # net units
            justify         => 'right',
            min_w           => 0.45 / in,
            max_w           => 0.45 / in,
            pad             => 0,
            do_not_truncate => 1,
        },
        {

            # amount due
            justify => 'right',
            min_w   => 0.61 / in,
            max_w   => 0.61 / in,
            pad     => 0,
        },
    ];
}

sub fullTrackDetails {
    my ( $track, $statement, $rows, $rowDetails, $links ) = @_;

    fullCrossedLicenseDetails( $track, $statement, $rows, $rowDetails, $links );
    fullLicenseDetails( $track, $statement, $rows, $rowDetails, $links );
}

sub fullCrossedLicenseDetails {
    my ( $statementTrack, $statement, $rows, $rowProps, $links ) = @_;

    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::CAMechanicalStatementLicense->GetByMechanicalStatementIDTrackIDPublisherID( $statement->ca_mechanical_statement_id,
        $statementTrack->track_id, $statement->ca_publisher_id );

    my $licenseCount = 0;

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

        next if ( !$license->crossed );

        $licenseCount++;

        my $incomeItemCollection =
          RPS::DB::Item::CAMechanicalStatementItem->GetByStatementAndTrackLicense( $statement->ca_mechanical_statement_id,
            $license->ca_track_license_id );

        my $itemCount = 0;

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

            # We need to grab the album title from the product.
            # If it's a track product, we'll need to grab it from the parent product.
            my $product = RPS::DB::Item::Product->Lookup( product_id => $incomeItem->product_id );
            my $albumTitle = _getAlbumTitle( $product, $album->title );

            # 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);

            push @$rows,
              [
                '/app/tools/common/production/images/icons/mini/micro_briefcase.gif',
                $track->title,
                $albumTitle,
                $incomeItem->upc,
                productCodeToName($productTypeID),
                regionIDToName( $license->region_id ),
                $ratePeriod,
                formatNumber( $incomeItem->base_rate ),
                formatPercent( $statementTrackLicense->rate_percentage ),
                formatPercent( $statementTrackLicense->share ),
                formatNumber( $incomeItem->net_rate ),
                formatNumber( $incomeItem->sales ),
                formatPercent( $statementTrackLicense->percentage_of_sales ),
                formatPercent( $statementTrackLicense->free_goods ),
                formatPercent( $statementTrackLicense->misc_deduction ),
                formatNumber( -1 * $incomeItem->reserved ),
                formatNumber( $incomeItem->liquidated ),
                formatNumber( -1 * $incomeItem->returns ),
                formatNumber( $incomeItem->carryover ),
                formatNumber( $incomeItem->net_units ),
                formatMoney( $incomeItem->amount_paid ),
              ];

            my $productLinkID = _getProductIDforLink($product);

            push @$links,
              [
                '/rps/ca_license?c=show&TrackLicenseID=' . $license->ca_track_license_id, '/rps/track?c=show&TrackID=' . $track->track_id,
                '/rps/product?c=show&ProductID=' . $productLinkID, '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', ''
              ];

            if ( ( $itemCount == 1 ) && ( $licenseCount != 1 ) ) {
                push @$rowProps, { pad => 8, };
            } else {
                push @$rowProps, {};
            }

            $itemCount++;
        }

        if ( $itemCount == 0 ) {

            # Even if there are no income items,
            # we still want to display the track info.
            push @$rows,
              [
                '/app/tools/common/production/images/icons/mini/micro_briefcase.gif',
                , $track->title, $album->title(), ' ', ' ', regionIDToName( $license->region_id ),
                ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ',
              ];
            push @$links,
              [
                '/rps/ca_license?c=show&TrackLicenseID=' . $license->ca_track_license_id, '/rps/track?c=show&TrackID=' . $track->track_id,
                '/rps/catalog?c=show&AlbumID=' . $track->album_id, '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', ''
              ];

#            push @$links, [ '', '/rps/track?c=show&TrackID=' . $track->track_id, '/rps/catalog?c=show&AlbumID=' . $track->album_id, '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '' ];

            push @$rowProps, { pad => 8, };

        }

        # Add the controlled comp adjustments
        #

        my $adjustmentCollection = RPS::DB::Item::CAMechanicalStatementAdjustmentItem->GetByMechanicalStatementIDTrackLicenseID(
            $statement->ca_mechanical_statement_id,
            $license->ca_track_license_id );

        while ( $adjustmentCollection->hasNext() ) {
            my $adjustment = $adjustmentCollection->next();

            push @$rows,
              [
                ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ',
                'Controlled Comp Adjustment:',
                formatMoney( $adjustment->amount )
              ];

            push @$links, [ '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '' ];

            push @$rowProps, {};

        }

        #  Now we want to display the subtotal, previous balance, transactions, etc. for this license
        #

        # We want a little line above the subtotal.
        #
        my $numRows = scalar(@$rowProps);
        my $lastRowPropHashref = $$rowProps[ ( $numRows - 1 ) ];
        $lastRowPropHashref->{lines} = [ {
                start_col => 20,
                end_col   => 20,
                top_pad   => 2,

                # bottom_pad => 2,
            },
        ];

        # 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;

        push @$rows,
          [
            ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ',
            'Current Period License Subtotal:',
            formatMoney( $license->subtotal )
          ];

        push @$links, [ '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '' ];

        push @$rowProps, { pad => -1, };

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

        my $transactionCount = 0;

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

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

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

            push @$rows,
              [
                ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ',
                $memo . $transactionType,
                formatMoney( $licenseTransaction->amount )
              ];

            push @$links, [ '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '' ];

            push @$rowProps, {};
        }

        if ( $transactionCount > 0 ) {

            # We want a little line under the last transaction.
            #
            my $numRows = scalar(@$rowProps);
            my $lastRowPropHashref = $$rowProps[ ( $numRows - 1 ) ];
            $lastRowPropHashref->{lines} = [ {
                    start_col => 20,
                    end_col   => 20,
                    top_pad   => 2,

                    # bottom_pad => 2,
                },
            ];
        }

        if ( ( $previousBalance != 0 ) || ( $license->advance_balance != 0 ) ) {
            push @$rows,
              [
                ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ',
                'Previous License Balance:',
                formatMoney($previousBalance)
              ];

            push @$links, [ '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '' ];

            push @$rowProps, {
                lines => [ {
                        start_col => 20,
                        end_col   => 20,
                        top_pad   => 2,

                        # bottom_pad => 2,
                    },
                ],
            };

            my $licenseBalance = $license->advance_balance;

            push @$rows,
              [
                ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ',
                'License Balance Forward:',
                formatMoney($licenseBalance)
              ];

            push @$links, [ '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '' ];

            push @$rowProps, {
                pad   => -1,
                lines => [ {
                        start_col => 20,
                        end_col   => 20,
                        top_pad   => 2,

                        # bottom_pad => 2,
                    },
                ],
            };
        }

        # 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;

            push @$rows,
              [
                ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ',
                'License Adjusted Subtotal:',
                formatMoney($licenseBalance)
              ];

            push @$links, [ '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '' ];

            push @$rowProps, { pad => -1, };
        }

        $licenseCount++;

    }

    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;

        push @$rows,
          [
            ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ',
            'Current Period Crossed Subtotal:',
            formatMoney( $statementTrack->crossed_subtotal )
          ];

        push @$links, [ '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '' ];

        push @$rowProps, { pad => 8, };

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

        my $crossedTransactionCount = 0;

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

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

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

            push @$rows,
              [
                ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ',
                $memo . $transactionType,
                formatMoney( $crossedTransaction->amount )
              ];

            push @$links, [ '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '' ];

            push @$rowProps, {};
        }

        # We want a little line under the last transaction.
        #
        if ( $crossedTransactionCount > 0 ) {
            my $numRows = scalar(@$rowProps);
            my $lastRowPropHashref = $$rowProps[ ( $numRows - 1 ) ];
            $lastRowPropHashref->{lines} = [ {
                    start_col => 20,
                    end_col   => 20,
                    top_pad   => 2,
                },
            ];
        }

        if ( ( $previousBalance != 0 ) || ( $statementTrack->crossed_advance_balance != 0 ) ) {
            push @$rows,
              [
                ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ',
                'Previous Crossed Balance:',
                formatMoney($previousBalance)
              ];

            push @$links, [ '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '' ];

            push @$rowProps,
              {
                lines => [ {
                        start_col => 20,
                        end_col   => 20,
                        top_pad   => 2,
                    },
                ],
              };

            my $licenseBalance = $statementTrack->crossed_advance_balance;

            push @$rows,
              [
                ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ',
                'Crossed Balance Forward:',
                formatMoney($licenseBalance)
              ];

            push @$links, [ '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '' ];

            push @$rowProps, { pad => -1, };

        }

        # And really, truly finally, the adjusted subtotal if need be.
        #
        if (   ( $crossedTransactionCount != 0 )
            || ( $previousBalance != 0 )
            || ( $statementTrack->crossed_subtotal != $statementTrack->crossed_adjusted_subtotal ) ) {
            my $licenseBalance = $statementTrack->crossed_adjusted_subtotal;

            push @$rows,
              [
                ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ',
                'Crossed Adjusted Subtotal:',
                formatMoney($licenseBalance)
              ];

            push @$links, [ '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '' ];

            push @$rowProps, {};
        }

        # We want the last item to have a line underneath it.
        pop(@$rowProps);
        push @$rowProps,
          {
            pad   => 8,
            lines => [ {
                    start_col => 0,
                    end_col   => 20,
                    top_pad   => 2,
                },
            ],
          };
    }

}

sub fullLicenseDetails {
    my ( $statementTrack, $statement, $rows, $rowProps, $links ) = @_;

    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::CAMechanicalStatementLicense->GetByMechanicalStatementIDTrackIDPublisherID( $statement->ca_mechanical_statement_id,
        $statementTrack->track_id, $statement->ca_publisher_id );

    my $licenseCount = 0;

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

        next if ( $license->crossed );

        $licenseCount++;

        my $incomeItemCollection =
          RPS::DB::Item::CAMechanicalStatementItem->GetByStatementAndTrackLicense( $statement->ca_mechanical_statement_id,
            $license->ca_track_license_id );

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

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

            # We need to grab the album title from the product.
            # If it's a track product, we'll need to grab it from the parent product.
            my $product = RPS::DB::Item::Product->Lookup( product_id => $incomeItem->product_id );
            my $albumTitle = _getAlbumTitle( $product, $album->title );

            # 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);

            push @$rows,
              [
                '/app/tools/common/production/images/icons/mini/micro_briefcase.gif',
                $track->title,
                $albumTitle,
                $incomeItem->upc,
                productCodeToName($productTypeID),
                regionIDToName( $license->region_id ),
                $ratePeriod,
                formatNumber( $incomeItem->base_rate ),
                formatPercent( $statementTrackLicense->rate_percentage ),
                formatPercent( $statementTrackLicense->share ),
                formatNumber( $incomeItem->net_rate ),
                formatNumber( $incomeItem->sales ),
                formatPercent( $statementTrackLicense->percentage_of_sales ),
                formatPercent( $statementTrackLicense->free_goods ),
                formatPercent( $statementTrackLicense->misc_deduction ),
                formatNumber( -1 * $incomeItem->reserved ),
                formatNumber( $incomeItem->liquidated ),
                formatNumber( -1 * $incomeItem->returns ),
                formatNumber( $incomeItem->carryover ),
                formatNumber( $incomeItem->net_units ),
                formatMoney( $incomeItem->amount_paid ),
              ];

            my $productLinkID = _getProductIDforLink($product);

            push @$links,
              [
                '/rps/ca_license?c=show&TrackLicenseID=' . $license->ca_track_license_id, '/rps/track?c=show&TrackID=' . $track->track_id,
                '/rps/product?c=show&ProductID=' . $productLinkID, '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', ''
              ];

            push @$rowProps, {};
        }

        if ( $itemCount == 0 ) {

            # Even if there are no income items,
            # we still want to display the track info.
            push @$rows,
              [
                '/app/tools/common/production/images/icons/mini/micro_briefcase.gif',
                , $track->title, $album->title, ' ', ' ', regionIDToName( $license->region_id ),
                ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ',
              ];

            push @$links,
              [
                '/rps/ca_license?c=show&TrackLicenseID=' . $license->ca_track_license_id, '/rps/track?c=show&TrackID=' . $track->track_id,
                '/rps/catalog?c=show&AlbumID=' . $track->album_id, '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', ''
              ];

            push @$rowProps, {};

        }

        # Add the controlled comp adjustments
        #

        my $adjustmentCollection = RPS::DB::Item::CAMechanicalStatementAdjustmentItem->GetByMechanicalStatementIDTrackLicenseID(
            $statement->ca_mechanical_statement_id,
            $license->ca_track_license_id );

        while ( $adjustmentCollection->hasNext() ) {
            my $adjustment = $adjustmentCollection->next();

            push @$rows,
              [
                ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ',
                'Controlled Comp Adjustment:',
                formatMoney( $adjustment->amount )
              ];

            push @$links, [ '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '' ];

            push @$rowProps, {};

        }

        # We want a little line above the subtotal.
        #
        my $numRows = scalar(@$rowProps);
        my $lastRowPropHashref = $$rowProps[ ( $numRows - 1 ) ];
        $lastRowPropHashref->{lines} = [ {
                start_col => 20,
                end_col   => 20,
                top_pad   => 2,

                # bottom_pad => 2,
            },
        ];

        # 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;

        push @$rows,
          [
            ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ',
            'Current Period License Subtotal:',
            formatMoney( $license->subtotal )
          ];

        push @$links, [ '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '' ];

        push @$rowProps, { pad => -1, };

        # Add the transactions in.
        #
        my $transactionCollection = RPS::DB::Item::CAMechanicalStatementLicenseTransaction->GetByMechanicalStatementLicenseID(
            $license->ca_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 . ') ';
            }

            push @$rows,
              [
                ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ',
                $memo . $transactionType,
                formatMoney( $transaction->amount )
              ];

            push @$links, [ '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '' ];

            push @$rowProps, {};
        }

        if ( $transactionCount > 0 ) {

            # We want a little line under the last transaction.
            #
            my $numRows = scalar(@$rowProps);
            my $lastRowPropHashref = $$rowProps[ ( $numRows - 1 ) ];
            $lastRowPropHashref->{lines} = [ {
                    start_col => 20,
                    end_col   => 20,
                    top_pad   => 2,
                },
            ];
        }

        if ( ( $previousBalance != 0 ) || ( $license->advance_balance != 0 ) ) {
            push @$rows,
              [
                ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ',
                'Previous License Balance:',
                formatMoney($previousBalance)
              ];

            push @$links, [ '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '' ];

            push @$rowProps,
              {
                lines => [ {
                        start_col => 20,
                        end_col   => 20,
                        top_pad   => 2,
                    },
                ],
              };

            my $licenseBalance = $license->advance_balance;

            push @$rows,
              [
                ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ',
                'License Balance Forward:',
                formatMoney($licenseBalance)
              ];

            push @$links, [ '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '' ];

            push @$rowProps,
              {
                pad   => -1,
                lines => [ {
                        start_col => 20,
                        end_col   => 20,
                        top_pad   => 2,
                    },
                ],
              };
        }

        # 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;

            push @$rows,
              [
                ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ',
                'License Adjusted Subtotal:',
                formatMoney($licenseBalance)
              ];

            push @$links, [ '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '' ];

            push @$rowProps, {};
        }

        # We want the last item to have a line underneath it.
        pop(@$rowProps);
        push @$rowProps,
          {
            pad   => -1,
            lines => [ {
                    start_col => 0,
                    end_col   => 20,
                    top_pad   => 2,
                },
            ],
          };

    }
}

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

    my $pageCount = 1;
    my $numPages  = scalar @gFullPages;
    foreach my $page (@gFullPages) {
        my $text = $page->text;

        printText(
            page     => $page,
            display  => "$pageCount of $numPages",
            y        => kBottomMargin,
            font     => $font->{Helvetica}{Roman},
            fontSize => 12 / pt,
            centered => 1
        );

        $pageCount++;
    }
}

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

    my $page = $pdf->page;

    # the mediabox is the size of the paper.
    #
    $page->mediabox( kPageWidth, kPageHeight );

    # the cropbox defines the margins, essentially.
    # the args are the coordinates for the left, bottom, right, and top (in that order)
    #

    # !!! Rather than set the cropbox right to the margins, I am going to make it larger.
    # !!! Basically, I want to have this thing render in the viewer with the appearance of having
    # !!! margins.
    #
    #    $page->cropbox(kLeftMargin, kBottomMargin, kRightMargin, kTopMargin);
    $page->cropbox( 5, 5, kPageWidth - 5, kPageHeight - 5 );

    # Not currently declaring a 'bleedbox' or an 'artbox'.  Check out the tutorial if you want to
    # know what those are... the url for that is at the top of this file.
    #
    push @gFullPages, $page;

    return $page;
}

# This just prints a line of text, left justified to the left margin
# unless "centered" is passed, in which case it centers the text.
#
sub printText {
    my %args = @_;

    my $page     = $args{page};
    my $display  = $args{display};
    my $link     = $args{link};
    my $x        = $args{x};
    my $y        = $args{y};
    my $font     = $args{font};
    my $fontSize = $args{fontSize};
    my $color    = $args{color};
    my $centered = $args{centered};

    $color = 'black' unless $color;

    if ($link) {

        # We want to do something to make links stand out.
        # For now, just make them blue.
        $color = 'blue';
    }

    my $text = $page->text();

    $text->font( $font, $fontSize );
    $text->fillcolor($color);

    my $top = $y;
    my $left;
    my $right;

    my @strings = split( /\n/, $display );
    foreach my $string (@strings) {

        # Remove any carriage returns that are left over from the new line splitting above.
        $string =~ s/\r//g;

        # Calculate how wide this string will be.
        #
        my $stringWidth = $text->advancewidth($string);

        if ($centered) {
            $x = ( ( kRightMargin - kLeftMargin ) / 2 ) + kLeftMargin - ( $stringWidth / 2 );
        }
        $left = $x;

        if ( !$right || $right < ( $stringWidth + $left ) ) {
            $right = $stringWidth + $left;
        }

        $y -= $fontSize;

        $text->translate( $x, $y );
        $text->text($string);

        # Move the cursor down the page.
        # add a little bitty bit of padding
        $y -= 1;
    }

    my $bottom = $y;

    if ($link) {
        my $clientNameClean = Common::Client::Current()->WebAlias();
        $clientNameClean = Common::Client::Current()->ClientNameClean() unless $clientNameClean;
        my $url = "https://" . $clientNameClean . ".royaltyshare.com" . $link;

        # And now we'll create the actual link
        my $annot = $page->annotation();
        $annot->url( $url, -rect => [ $left, $bottom, $right, $top ] );
    }

    return $y;
}

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, "CAD" );

    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 %parentTitles;

sub _getAlbumTitle {
    my ( $product, $albumTitle ) = @_;

    my $title;

    # If this is a track product, we need to get the title from the parent.
    if ( $product->product_type_id == RPS::DB::Item::Product::kProductTypeDigitalTrack && $product->parent_product_id ) {
        if ( !$parentTitles{ $product->parent_product_id } ) {
            my $parentProduct = RPS::DB::Item::Product->Lookup( product_id => $product->parent_product_id );
            $parentTitles{ $product->parent_product_id } = $parentProduct->title;
        }
        $title = $parentTitles{ $product->parent_product_id };
    } else {
        $title = $product->title;
    }

    # Just in case we don't find a product title, let's fall back to the album title.
    if ( !$title ) {
        $title = $albumTitle;
    }

    return $title;
}

sub _getProductIDforLink {
    my ($product) = @_;

    my $productID;

    # If this is a track product, we need to link to the parent product.
    if ( $product->product_type_id == RPS::DB::Item::Product::kProductTypeDigitalTrack && $product->parent_product_id ) {
        $productID = $product->parent_product_id;
    } else {
        $productID = $product->product_id;
    }

    return $productID;
}

my $gRatePeriodMap;

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

    return "" unless $id;

    if ( !$gRatePeriodMap ) {
        $gRatePeriodMap = RPS::DB::Item::StatRate->GetRatePeriodMap();
    }

    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 addDraftWatermarkPayee {
    my ( $pdf, $pages ) = @_;

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

    foreach my $page (@gPayeePages) {
        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 addDraftWatermarkFull {
    my ( $pdf, $pages ) = @_;

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

    foreach my $page (@gFullPages) {
        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');
    }
}

#
# Boring script stuff below...
#

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

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

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

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

sub usage {
    print STDERR "\nusage: $0 -c <client_id> -s <mechanical_statement_id> -m <mode> [-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 to pdf\n";
    print STDERR "\t-m <mode>\tThe mode that the script should run in (p = payee, f = full, b = both)\n";
    print STDERR "\t-f <output file>\tThe output file. 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";
    }
}

