#!/usr/bin/perl
#------------------------------------------------------------
# Copyright (C) 2006 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::RoyaltyRun::Status;
use RPS::Statement::Artist::StatementFull;
use RPS::Payor::Payor;
use RPS::ArtistPayee::ArtistPayee;
use RPS::ArtistRoyalty::ArtistRoyaltyRun;
use RPS::DB::Item::ContractRateType;
use RPS::DB::Item::IncomeSource;
use RPS::DB::Item::LicenseIncomeType;
use RPS::DB::Item::Region;
use RPS::DB::Item::Channel;
use RPS::DB::Item::PriceLevel;
use RPS::DB::Item::ArtistRoyaltyStatementSettings;

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


my $gShowRoyaltyRate = 0;
my $gShowSalesPrice = 0;

my $gShowProration         = 0;
my $gShowProratedRate      = 0;
my $gShowRateReduction     = 0;
my $gShowPackaging         = 0;
my $gShowGrossUnits        = 0;
my $gShowGrossSales        = 0;
my $gShowPercentageOfSales = 0;
my $gShowFreeGoods         = 0;
my $gShowReserves          = 0;
my $gShowLiquidations      = 0;
my $gShowReturns           = 0;


$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.4/in;
use constant kBottomMargin  => 0.5/in;
use constant kRightMargin   => (kPageWidth - (0.4/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.  This is PDF-specific,
# and must be reset if you create more than one PDF.
#
my @gPages;


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

my $mode = $options{mode};

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

# Instantiate the ArtistStatement object.
# This object will contain all the info we need to output the statement document.
#
my $statement = RPS::Statement::Artist::StatementFull->new(artistRoyaltyStatementID => $options{artistStatementID});

my $payeeID = $statement->PayeeID();

# If an output file path was not specified, create one out of the statement id.
#
my $outFilePath = $options{outputFile};


if (! $outFilePath)
{
    $outFilePath = "artist_statement_$payeeID.pdf";
}




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


if ( $mode eq 'f' || $mode eq 'b' ) {

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

    createStatementPDF($statement, 'f', $outFilePath );
}


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




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

    my $artistStatementID = $statement->ArtistRoyaltyStatementID();


    # Grab the statement settings (some columns are optional), and record these
    # in some _global_ variables.  The optional nature only applies to payee
    # statements.
    #
    my $settings = RPS::DB::Item::ArtistRoyaltyStatementSettings->GetCurrentSettings();
    if ($settings)
    {
        $gShowRoyaltyRate       = $settings->show_royalty_rate; # base rate
        $gShowSalesPrice        = $settings->show_sales_price;
        $gShowProration         = $settings->show_proration;
        $gShowProratedRate      = $settings->show_prorated_rate;
        $gShowRateReduction     = $settings->show_rate_reduction;
        $gShowPackaging         = $settings->show_packaging;
        $gShowGrossUnits        = $settings->show_gross_units;
        $gShowGrossSales        = $settings->show_gross_sales;
        $gShowPercentageOfSales = $settings->show_percent_sales;
        $gShowFreeGoods         = $settings->show_free_goods;
        $gShowReserves          = $settings->show_reserves;
        $gShowLiquidations      = $settings->show_liquidations;
        $gShowReturns           = $settings->show_returns;
    }

    if ( $mode eq 'f' ) {
        # show everything in the full statement
        $gShowRoyaltyRate       = 1;
        $gShowSalesPrice        = 1;
        $gShowProration         = 1;
        $gShowProratedRate      = 1;
        $gShowRateReduction     = 1;
        $gShowPackaging         = 1;
        $gShowGrossUnits        = 1;
        $gShowGrossSales        = 1;
        $gShowPercentageOfSales = 1;
        $gShowFreeGoods         = 1;
        $gShowReserves          = 1;
        $gShowLiquidations      = 1;
        $gShowReturns           = 1;
    }


    # 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'),
        },
        # embeded external font
        DejaVuSans => {
            Roman => $pdf->ttfont('/app/tools/rps/fonts/DejaVuSans.ttf'),
        }
    };


    # 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 = newPage($pdf);


    # Add label logo to the first page, if desired
    #
    my $payor = $statement->Payor();
    my $showLogo = $payor->ShowLogo();

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


    # Each section of the statement will have its own subroutine.
    # Note: 'mode' is only passed where needed.
    #
    ($x, $y, $page) = header($pdf, $statement, $mode, $x, $y, $page);
    ($x, $y, $page) = summary($pdf, $statement, $mode, $x, $y, $page );
    ($x, $y, $page) = details($pdf, $statement, $mode, $x, $y, $page );


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


    # 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::ArtistRoyalty::ArtistRoyaltyRun->new(artistRoyaltyRunID => $statement->ArtistRoyaltyRunID(), loadSubs => 0);
    if (RPS::RoyaltyRun::Status::kCommitted != $run->Status()
     && RPS::RoyaltyRun::Status::kClosed != $run->Status())
    {
        addDraftWatermark($pdf);
    }


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


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


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

    # Step 1 - Put the text 'Artist Royalty Statement' at the top of the page.
    #
    $y = printTextCentered($text, $y, 'Artist Royalty Statement', $font->{Helvetica}{Bold}, 12/pt);


    # Print the 'From: Payor' info
    #
    my $payor = $statement->Payor();

    my $cityStateLine = $payor->City();
    if ($payor->City() && $payor->StateProvince())
    {
        $cityStateLine .= ",";
    }
    $cityStateLine .= $payor->StateProvince();

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

    if ( $mode eq 'p' ) {
        # The 'From:' block only appears on the payee statement
        $y = printText($text, $x, $y, 'From:', $font->{Helvetica}{Roman}, 9/pt);
        $y = printText($text, $x, $y, $payor->Name(), $font->{Helvetica}{Roman}, 9/pt);
        $y = printText($text, $x, $y, $payor->StreetAddress(), $font->{Helvetica}{Roman}, 9/pt);
        $y = printText($text, $x, $y, $payor->StreetAddress2(), $font->{Helvetica}{Roman}, 9/pt);
        $y = printText($text, $x, $y, $payor->StreetAddress3(), $font->{Helvetica}{Roman}, 9/pt);
        $y = printText($text, $x, $y, $cityStateLine, $font->{Helvetica}{Roman}, 9/pt);
        $y = printText($text, $x, $y, $payor->CountryCode(), $font->{Helvetica}{Roman}, 9/pt);

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

    my $payee = $statement->ArtistPayee();

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

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

    if ( $mode eq 'f' ) {
        $y = printTextWithLink(
            page     => $page,
            display  => $payee->Name(),
            link     => '/rps/artist_payee?ArtistPayeeID=' . $payee->ArtistPayeeID() . '&c=show',
            x        => $x,
            y        => $y,
            font     => $font->{Helvetica}{Roman},
            fontSize => 9/pt
        );
    } else {
        $y = printText($text, $x, $y, $payee->Name(), $font->{Helvetica}{Roman}, 9/pt);
    }

    $y = printText($text, $x, $y, 'Client #: ' . $payee->ClientAccountID(), $font->{Helvetica}{Roman}, 9/pt) if $payee->ClientAccountID();
    $y = printText($text, $x, $y, $payee->StreetAddress(), $font->{Helvetica}{Roman}, 9/pt);
    $y = printText($text, $x, $y, $payee->StreetAddress2(), $font->{Helvetica}{Roman}, 9/pt);
    $y = printText($text, $x, $y, $payee->StreetAddress3(), $font->{Helvetica}{Roman}, 9/pt);
    $y = printText($text, $x, $y, $cityStateLine, $font->{Helvetica}{Roman}, 9/pt);
    $y = printText($text, $x, $y, $payee->CountryCode(), $font->{Helvetica}{Roman}, 9/pt);

    # Now print the run label. Which means we need to fetch the run...
    #
    my $run = RPS::ArtistRoyalty::ArtistRoyaltyRun->new(artistRoyaltyRunID => $statement->ArtistRoyaltyRunID(), loadSubs => 0);
    $y -= 9/pt;
    $y = printText($text, $x, $y, $run->Label(), $font->{Helvetica}{Bold}, 9/pt);

    $y -= 18/pt;

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



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


    # The PDF::Table class doesn't seem to work correctly if there is more than
    # 1 table on a page.  So, going to have to draw this in myself.
    #
    ($x, $y, $page) = drawBalanceTable($pdf, $statement, $x, $y, $page);

    $y -= 18;


    ($x, $y, $page) = drawProductTotalsTable($pdf, $statement, $mode, $x, $y, $page);

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

sub drawBalanceTable
{
    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 @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},
        repeat => 1,    # !!! This is a special flag that only affects the header line.
                        # !!! It tells the Table class to repeat the header on new pages.
    };


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


    # Get any transactions
    #
    my $transactionList = $statement->ArtistStatementTransactionList();
    if ($transactionList)
    {
        my $list = $transactionList->getList();
        foreach my $transaction (@$list)
        {
            my $type;
            if ($transaction->TypeCode() == 2)
            {
                $type = 'Adjustment';
            }
            elsif ($transaction->TypeCode() == 3)
            {
                $type = 'Advance';
            }
            elsif ($transaction->TypeCode() == 4)
            {
                $type = 'Payment';
            }
            my $amount = formatMoney($transaction->Amount());
            $amount = ' ' unless defined $amount;
            my $checkNum = $transaction->CheckNumber();
            $checkNum = ' ' unless defined $checkNum;
            my $date = $transaction->TransactionDate();
            $date = ' ' unless defined $date;
            my $memo = $transaction->Memo();
            $memo = ' ' unless defined $memo;

            # Let's format the date
            if ($date != ' ')
            {
                $date =  formatDate($date);
            }

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


    push @balanceArray, ['Current Period Royalties:', formatMoney($statement->Total()), ' ', ' ', ' '];
    push @rowProperties,
    {
        lines =>
        [
            {
                # pen_size => 1,
                # color => 'black',
                start_col => 0,
                end_col => 1,
                top_pad => 2,
                bottom_pad => 0,
            },
        ],
    };


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

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

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

    my $tableObj = TableObjSupportEmbededFonts->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   => \&newPage,
        font            => $font->{Helvetica}{Roman},
        font_size       => $fontSize,
        font_dejavusans => $font->{DejaVuSans},
        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.5/in,
                max_w => 1.5/in,
            },
            {
                font => $font->{Helvetica}{Roman},
                font_size => $fontSize,
#                justify => 'right',
                min_w => 1.0/in,
                max_w => 1.5/in,
                pad => (1/4)/in,
            },
            {
                font => $font->{Helvetica}{Roman},
                font_size => $fontSize,
#                justify => 'right',
                min_w => 1.0/in,
                max_w => 1.25/in,
            },
            {
                font => $font->{Helvetica}{Roman},
                font_size => $fontSize,
                #justify => 'right',
                min_w => 1.0/in,
                max_w => 4.0/in,
            },
        ],
        row_props => \@rowProperties,
    );


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

    $y -= 2;

    my @amountDueArray;
    my @amountDueProperties;


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

    # If this publisher is 'on hold', we display 'ON HOLD' rather than the amount due value.
    #
    my $amountDueDisplay;
    my $currencyDisplay;
    if ($statement->OnHold)
    {
        $amountDueDisplay = 'ON-HOLD';
        $currencyDisplay = ' ';
    }
    else
    {
        $amountDueDisplay = formatMoney($statement->AmountDue);
        $currencyDisplay = ' ('.Common::Client::Current()->Locale()->currencyFormat()->currencyCode().')';
    }
    push @amountDueArray, ['Amount Payable:', $amountDueDisplay, $currencyDisplay];
    push @amountDueProperties, {
        font => $font->{Helvetica}{Bold},
        lines =>
        [
            {
                # pen_size => 1,
                # color => 'black',
                start_col => 0,
                end_col => 2,
                top_pad => 5,
                bottom_pad => 0,
            },
        ],
    };

    # !!! It's weird that we specify a font_size for the table, and a font size for every column.
    # !!! AND a finial font (but no size) for the amountDue row.
    # !!! Considering that this table contains just one row...  That seems triple-redundant to me.
    #
    my $bigFontSize = 10/pt;

    my $tableObj = TableObjSupportEmbededFonts->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   => \&newPage,
        font            => $font->{Helvetica}{Roman},
        font_size       => $bigFontSize,
        font_dejavusans => $font->{DejaVuSans},
        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.5/in,
                max_w => 1.5/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);
}

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


    # Now output the 'product totals' data
    #
    my @productsArray;
    my @linkArray;
    my @rowProps;

    my @crossedArray;
    my @uncrossedArray;

    my @crossedArrayLink;
    my @uncrossedArrayLink;

    # Add the header
    #
    push @productsArray, [' ', 'Type', 'Album', 'Sales', 'License Income', 'Expenses', 'Previous Balance', 'Total'];  # the header
    push @linkArray,     [ '',     '',      '',      '',               '',         '',                 '',      ''];
    push @rowProps,      { font => $font->{Helvetica}{Bold}, repeat => 1 };


    # Grab all the album summary info.
    # We'll sort them into two buckets - crossed and uncrossed products.
    #
    my $albumList = $statement->ArtistStatementAlbumList()->getList();
    foreach my $album (@$albumList)
    {
        # We need to skip the albums whose income items use the non-payable rate type exclusively.
        #
        my $incomeItemList = $album->IncomeItemList()->getList();
        my $skipAlbum      = 1;
        foreach my $incomeItem (@$incomeItemList)
        {
            if ($incomeItem->ContractRateTypeID() != RPS::DB::Item::ContractRateType::kRateTypeNonPayable)
            {
                $skipAlbum = 0;
                last;
            }
        }

        # If there are no sales, then we're dealing with license income
        # and/or expenses, which should be included on the statement.
        # We'll also include albums with a previous balance.
        #
        if (   @$incomeItemList == 0
            || $album->LicenseIncomeSubtotal() != 0
            || $album->TotalExpenses() != 0
            || $album->PreviousBalance() != 0 )
        {
           $skipAlbum = 0;
        }

        next if $skipAlbum == 1;

        my @row = (
            'Product Sales',
            $album->Album()->Title(),
            formatMoney($album->TotalIncome()),
            formatMoney($album->LicenseIncomeSubtotal()),
            formatMoney($album->TotalExpenses()),
            formatMoney($album->PreviousBalance()),
            formatMoney($album->Total())
        );

        my @rowlink = (
            '',
            '#ALBUM_DEST_' . $album->Album->AlbumID . '_' . $album->ArtistContractID,
            (('',) x 5)
        );

        if ($album->IsCrossCollateralized() != 0)
        {
            if (0 == scalar @crossedArray)
            {
                unshift @row, 'Crossed Totals';
            }
            else
            {
                unshift @row, ' ';
            }
            push    @crossedArray, \@row;
            unshift @rowlink, '';
            push    @crossedArrayLink, \@rowlink;
        }
        else
        {
            if (0 == scalar @uncrossedArray)
            {
                unshift @row, 'Uncrossed Totals';
            }
            else
            {
                unshift @row, ' ';
            }
            push    @uncrossedArray, \@row;
            unshift @rowlink, '';
            push    @uncrossedArrayLink, \@rowlink;
        }
    }


    # Now combine the two arrays.
    #
    if (0 != scalar @crossedArray)
    {
        my $i=0;
        foreach my $crossedRow (@crossedArray)
        {
            push @productsArray, $crossedRow;
            push @linkArray,     @crossedArrayLink[$i++];
            push @rowProps,      {};
        }

    }

    #
    # Now add a row for contract level license income.
    if ($statement->ContractLevelLicenseIncomeSubtotal() != 0 || $statement->ContractLevelLicenseIncomePreviousBalance() != 0)
    {
        my $sectionLabel = 'Crossed Totals';
        if (0 != scalar @crossedArray)
        {
            $sectionLabel = '';
        }

        push @productsArray,
        [
            $sectionLabel,
            'Contract License Income',
            ' ',
            ' ',
            formatMoney($statement->ContractLevelLicenseIncomeSubtotal()),
            ' ',
            formatMoney($statement->ContractLevelLicenseIncomePreviousBalance()),
            formatMoney($statement->ContractLevelLicenseIncomeTotal())
        ];
        push @linkArray, [
            '',
            '',
            '',
            '',
            '',
            '',
            '',
            ''
        ];
        push @rowProps, {};
    }

    if ((0 != scalar @crossedArray) || ($statement->ContractLevelLicenseIncomeSubtotal() != 0 || $statement->ContractLevelLicenseIncomePreviousBalance() != 0))
    {

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


        push @productsArray,
        [
            ' ',
            'Total:',
            ' ',
            formatMoney($statement->CrossCollateralizedIncomeSubTotal()),
            formatMoney($statement->LicenseIncomeSubtotal()),
            formatMoney($statement->CrossCollateralizedExpenseSubTotal()),
            formatMoney($statement->CrossCollateralizedPreviousBalance()),
            formatMoney($statement->CrossCollateralizedSubTotal())
        ];
        push @linkArray, [
            '',
            '',
            '',
            '',
            '',
            '',
            '',
            ''
        ];
        push @rowProps, { font => $font->{Helvetica}{Bold} };
    }

    if (((0 != scalar @crossedArray) || ($statement->ContractLevelLicenseIncomeSubtotal() != 0)) && (0 != scalar @uncrossedArray))
    {
        # Add an empty row.
        # !!! We might be able to do the same thing with a row padding on the next row.
        #
        push @productsArray, [' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '];
        push @linkArray,     [ '',  '',  '',  '',  '',  '',  '',  ''];
        push @rowProps,      {};
    }

    if (0 != scalar @uncrossedArray)
    {
        my $i=0;
        foreach my $uncrossedRow (@uncrossedArray)
        {
            push @productsArray, $uncrossedRow;
            push @linkArray,     @uncrossedArrayLink[$i++];
            push @rowProps,      {};
        }


        # We want a little line under the last item.
        #
        my $numRows =            scalar(@rowProps);
        my $lastRowPropHashref = $rowProps[($numRows - 1)];
        $lastRowPropHashref->{lines} =
        [
            {
                start_col  => 0,
                end_col    => 7,
                top_pad    => 2,
                bottom_pad => 0,
            },
        ];
        push @productsArray, ['Total:', ' ', ' ', ' ', ' ', ' ', ' ', formatMoney($statement->Total())];
        push @linkArray,     [      '',  '',  '',  '',  '',  '',  '', ''];
        push @rowProps,      { font => $font->{Helvetica}{Bold} };

    }

    # We're only going to create this table if there's someting to display
    #

    if ( (0 != scalar @crossedArray) ||
         ($statement->ContractLevelLicenseIncomeSubtotal() != 0) ||
         (0 != scalar @uncrossedArray) )
    {
        my %args = (
            bottom_margin   => kBottomMargin+20,   # adding a bit of padding to avoid weirdness
            new_page_y      => kTopMargin-20,      # ditto
            new_page_func   => \&newPage,
            font            => $font->{Helvetica}{Roman},
            font_size       => $fontSize,
            font_dejavusans => $font->{DejaVuSans},
            column_props    =>
            [
                {
                    font      => $font->{Helvetica}{Bold},
                    min_w     => 1/in,
                    max_w     => 1/in,
                    font_size => $fontSize,
                },
                {
                    font      => $font->{Helvetica}{Roman},
                    font_size => $fontSize,
                    min_w     => 2/in,
                    max_w     => 2/in,
                },
                {
                    font      => $font->{Helvetica}{Roman},
                    font_size => $fontSize,
                    min_w     => 2/in,
                    max_w     => 2/in,
                },
                {
                    font      => $font->{Helvetica}{Roman},
                    font_size => $fontSize,
                    justify   => 'right',
                    min_w     => 1/in,
                    max_w     => 1/in,
                },
                {
                    font      => $font->{Helvetica}{Roman},
                    font_size => $fontSize,
                    justify   => 'right',
                    min_w     => 1/in,
                    max_w     => 1/in,
                },
                {
                    font      => $font->{Helvetica}{Roman},
                    font_size => $fontSize,
                    justify   => 'right',
                    min_w     => 1/in,
                    max_w     => 1/in,
                },
                {
                    font      => $font->{Helvetica}{Roman},
                    font_size => $fontSize,
                    justify   => 'right',
                    min_w     => 1/in,
                    max_w     => 1/in,
                },
                {
                    font      => $font->{Helvetica}{Roman},
                    font_size => $fontSize,
                    justify   => 'right',
                    min_w     => 1/in,
                    max_w     => 1/in,
                },
            ],
            row_props => \@rowProps,
        );

        $args{row_links} = \@linkArray if ( $mode eq 'f' );

        my $tableObj = TableObjSupportEmbededFonts->new( $pdf, $page, \@productsArray, %args );

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

    }

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


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

    my $withLegend = 0;

    # Let's grab the distinct income source ids so that we can make a legend.
    #
    my $incomeSourceList = $statement->ArtistStatementIncomeSourceListSkipNonPayable()->getList();
    if (defined $incomeSourceList)
    {
        ($page, $y) = addLegend($pdf, $incomeSourceList);
        $withLegend = 1;
    }


    my $albumList = $statement->ArtistStatementAlbumList()->getList();
    foreach my $album (@$albumList)
    {

        # We need to skip the albums whose income items use the non-payable rate type exclusively.
        #
        my $incomeItemList = $album->IncomeItemList()->getList();
        my $skipAlbum = 1;
        foreach my $incomeItem (@$incomeItemList)
        {
            if ($incomeItem->ContractRateTypeID() != RPS::DB::Item::ContractRateType::kRateTypeNonPayable)
            {
                $skipAlbum = 0;
                last;
            }
        }

        # If there are no sales, then we're dealing with license income
        # and/or expenses, which should be included on the statement.
        #
        if (   @$incomeItemList == 0
            || $album->LicenseIncomeSubtotal() != 0
            || $album->TotalExpenses() != 0 )
        {
           $skipAlbum = 0;
        }

        next if $skipAlbum == 1;

        ($page, $y) = albumDetails($pdf, $page, $mode, $x, $y, $album, $withLegend);

        # Create a GoTo link to the album page
        my $destName = 'ALBUM_DEST_' . $album->Album->AlbumID . '_' . $album->ArtistContractID;
        my $dest = $pdf->named_destination('Dests', $destName);
        $dest->link($page);
        TableObjSupportEmbededFonts::updatePendingLinks( $pdf, $destName, $dest );

        $withLegend = 0;
    }

    ($page, $y) = contractLicenseIncomeDetails($pdf, $page, $mode, $x, $y, $statement );

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


sub albumDetails
{
    my ($pdf, $page, $mode, $x, $y, $album, $withLegend) = @_;
    my $fontSize = 7/pt;

    # Start each album detail on a fresh page (if we are not starting on the page with the legend).
    #
    if ($withLegend != 1)
    {
      $page = newPage($pdf);
      $y = kTopMargin-10;
    }

    ($page, $y) = albumTitle($pdf, $page, $mode, $x, $y, $album);
    ($page, $y) = unitRoyalties($pdf, $page, $mode, $x, $y, $album);
    ($page, $y) = netRevenueRoyalties($pdf, $page, $mode, $x, $y, $album);
    ($page, $y) = licenseIncomeRoyalties($pdf, $page, $mode, $x, $y, $album);
    ($page, $y) = recoupableExpenses($pdf, $page, $x, $y, $album);
    ($page, $y) = netExpenses($pdf, $page, $x, $y, $album);

    # Last, print the album previous balance and album total
    #
    my $table = TableObjSupportEmbededFonts->new
    (
        $pdf, $page,
        [
            [' ', ' ' ],
            ['Album Previous Balance:', formatMoney($album->PreviousBalance()) ],
            ['Album Total:',formatMoney( $album->Total()) ],
        ],
        bottom_margin   => kBottomMargin+20,   # adding a bit of padding to avoid weirdness
        new_page_y      => kTopMargin-10,      # ditto
        new_page_func   => \&newPage,
        font            => $font->{Helvetica}{Bold},
        font_size       => $fontSize,
        font_dejavusans => $font->{DejaVuSans},
        column_props    =>
        [
            {
                justify => 'right',
                min_w => 9/in,
                max_w => 9/in,
                pad => 0,
            },
            {
                justify => 'right',
                min_w => 1/in,
                max_w => 1/in,
                pad => 0,
            },
        ],
        row_props =>
        [
            {

            },
            {
                lines =>
                [
                    {
                        start_col => 1,
                        end_col => 1,
                        top_pad => 2,
                        bottom_pad => 0,
                    },
                ],
            },
            {
#                lines =>
#                [
#                    {
#                        pen_size => 2,
#                        start_col => 0,
#                        end_col => 1,
#                        top_pad => 2,
#                        bottom_pad => 2,
#                    },
#                ],
            },
        ],
    );
    ($page, $y) = $table->print($x, $y);


    return ($page, $y);
}# albumDetails


sub unitRoyalties
{
    my ($pdf, $page, $mode, $x, $y, $album) = @_;
    my $fontSize = 7/pt;

    my @rows;
    my @rowLinks;
    my @rowProps;

    # We may have some additional columns, depending on some optional settings.
    # !!! So the trick here is going to be how we squeeze these in, and how we
    # adjust the geometry.   Right now the column sizes are hard-coded.  Now
    # they are going to need to be dynamic.
    # We've got lots of room, so that's not really a problem.
    #
    # So I think what I'll do is have a 'column template' array.  This will be an array
    # of hash references which will contain the column title, the size (when fully populated),
    # and some sort of indicator of where to get the data.
    # We'll scale the column sizes later based on the number of columns.

    # !!! Re-adjust these widths !!!
    # We've got 10 inches to work with.  The current totals are based on 9 columns.  Need
    # to re-do those to be based on 11 columns, and we'll scale them UP if there are fewer columns.
    #
    # I think instead that I'll deal in terms of 'units'.  We'll translate these units into 'inches' later.
    #
    my @columnTemplate;
    push @columnTemplate, {
        title    => 'Track Title',
        func     => '_outputTrackTitle',
        linkfunc => '_outputTrackLink',
        geometry => {
            width => 5,
            pad   => 0,
        },
    };

    push @columnTemplate, {
        title    => 'Source',
        func     => '_outputIncomeSource',
        geometry => {
            width => 2,
            pad   => 0,
        },
    };

    push @columnTemplate, {
        title    => 'Region',
        func     => '_outputRegion',
        geometry => {
            width => 2,
            pad   => 0,
        },
    };

    push @columnTemplate, {
        title    => 'Channel',
        func     => '_outputChannel',
        geometry => {
            width => 2,
            pad   => 0,
        },
    };

    push @columnTemplate, {
        title    => 'Price',
        title2   => 'Tier',
        func     => '_outputPriceTier',
        geometry => {
            width => 2,
            pad   => 0,
        },
    };

    push @columnTemplate, {
        title    => 'Sales',
        title2   => 'Price',
        func     => '_outputSalesPrice',
        geometry => {
            width => 2,
            pad   => 0,
        },
    } if $gShowSalesPrice; # Optional column

    push @columnTemplate, {
        title    => 'Rate',
        title2   => 'Type',
        func     => '_outputRateType',
        geometry => {
            width => 2,
            pad   => 0,
        },
    };

    push @columnTemplate, {
        title    => 'Base',
        title2   => 'Rate',
        func     => '_outputRoyaltyRate',
        geometry => {
            width => 2,
            pad   => 0,
        },
    } if $gShowRoyaltyRate; # Optional column

    push @columnTemplate, {
        title    => 'Proration',  # FB113
        func     => '_outputProration',
        geometry => {
            justify => 'center',
            width   => 2,
            pad     => 0,
        },
    } if $gShowProration; # Optional column

    push @columnTemplate, {
        title    => 'Prorated',  # FB113
        title2   => 'Rate',      # FB113
        func     => '_outputProratedRate',
        geometry => {
            width => 2,
            pad   => 0,
        },
    } if $gShowProratedRate; # Optional column

    push @columnTemplate, {
        title    => 'Rate ',  # FB109
        title2   => 'Redtn',  # FB109
        func     => '_outputRateReduction',
        geometry => {
            width => 2,
            pad   => 0,
        },
    } if $gShowRateReduction; # Optional column

    push @columnTemplate, {
        title    => 'Pack- ', # FB109
        title2   => 'aging',  # FB109
        func     => '_outputPackagingDeduction',
        geometry => {
            width => 2,
            pad   => 0,
        },
    } if $gShowPackaging; # Optional column

    push @columnTemplate, {
        title    => 'Effective',
        title2   => 'Rate',
        func     => '_outputEffectiveRate',
        geometry => {
            justify => 'center',
            width   => 2,
            pad     => 0,
        },
    };

    push @columnTemplate, {
        title    => 'Gross',  # FB109
        title2   => 'Units',  # FB109
        func     => '_outputGrossUnits',
        geometry => {
            justify => 'center',
            width   => 2,
            pad     => 0,
        },
    } if $gShowGrossUnits; # Optional column

    push @columnTemplate, {
        title    => '% of',  # FB109
        title2   => 'Sales', # FB109
        func     => '_outputPercentageOfSales',
        geometry => {
            width => 2,
            pad   => 0,
        },
    } if $gShowPercentageOfSales; # Optional column

    push @columnTemplate, {
        title    => 'Free',  # FB109
        title2   => 'Goods', # FB109
        func     => '_outputFreeGoodsDeduction',
        geometry => {
            width => 2,
            pad   => 0,
        },
    } if $gShowFreeGoods; # Optional column

    push @columnTemplate, {
        title    => 'Reserves',  # FB109
        func     => '_outputUnitsReserved',
        geometry => {
            justify => 'right',
            width   => 2,
            pad     => 0,
        },
    } if $gShowReserves; # Optional column

    push @columnTemplate, {
        title    => 'Liquid-', # FB109
        title2   => 'ations',  # FB109
        func     => '_outputUnitsLiquidated',
        geometry => {
            justify => 'right',
            width   => 2,
            pad     => 0,
        },
    } if $gShowLiquidations; # Optional column

    push @columnTemplate, {
        title    => 'Returns',  # FB109
        func     => '_outputReturns',
        geometry => {
            justify => 'right',
            width   => 2,
            pad     => 0,
        },
    } if $gShowReturns; # Optional column

    push @columnTemplate, {
        title    => 'Net Units',
        func     => '_outputNetUnits',
        geometry => {
            justify => 'right',
            width   => 2,
            pad     => 0,
        },
    };

    my $totalColumnWidth = 2;
    push @columnTemplate, {
        title    => 'Total',
        func     => '_outputTotal',
        geometry => {
            justify => 'right',
            width   => $totalColumnWidth,
            pad     => 0,
        },
    };
    my $numColumns = scalar @columnTemplate;

    # Set up the header
    #
    push @rows,     [ (' ') x $numColumns ];
    push @rowLinks, [ ('') x $numColumns ];
    push @rowProps, {};

    push @rows,     ['Unit Royalties', (' ') x ($numColumns - 1) ];
    push @rowLinks, [ ('') x $numColumns ];
    push @rowProps,
    {
        font => $font->{Helvetica}{Bold},
        lines =>
        [
            {
                start_col => 0,
                end_col => ($numColumns - 1),
                top_pad => 4,
            },
        ],
        repeatRows => 1,
    };

    # Put the column titles into a row.
    #
    my @tempRow;
    my @tempRowLinks;
    foreach my $colTempRecord (@columnTemplate)
    {
        push @tempRow,      $colTempRecord->{title};
        push @tempRowLinks, '';
    }
    push @rows,     \@tempRow;
    push @rowLinks, \@tempRowLinks;

    # Check if we need to generate a 2nd row of title information
    # if not, then just set the row properties for the single title row.
    # Otherwise, we need to a) generate a 2nd row of title info and
    # b) set the row properties for the 1st and 2nd rows
    #

    my @tempRow2;
    my @tempRow2Links;
    my $hasSecondRow;
    foreach my $colTempRecord (@columnTemplate)
    {
        if( exists $colTempRecord->{title2} )
        {
            push @tempRow2, $colTempRecord->{title2};
            $hasSecondRow = 1;
        }
        else
        {
            push @tempRow2, ' ';
        }
        push @tempRow2Links, '';
    }

    if( !$hasSecondRow )
    {
        # Just one title row, so set its properties
        #
        push @rowProps,
        {
            font  => $font->{Helvetica}{Bold},
            lines =>
            [
                {
                    start_col => 0,
                    end_col   => ($numColumns - 1),
                    top_pad   => 4,
                },
            ],
            repeat => 1,
        };
    }
    else
    {
        # Add second title row
        #
        push @rows,     \@tempRow2      if( $hasSecondRow );
        push @rowLinks, \@tempRow2Links if( $hasSecondRow );

        # Properties for first title row
        #
        push @rowProps,
        {
            font       => $font->{Helvetica}{Bold},
            repeatRows => 1,
        };

        # Properties for second title row
        #
        push @rowProps,
        {
            font  => $font->{Helvetica}{Bold},
            lines =>
            [
                {
                    start_col => 0,
                    end_col   => ($numColumns - 1),
                    top_pad   => 4,
                },
            ],
            repeatRows => 2,
        };
    }

    # Go through the entire income item list, and pull out those that are not net revenue or gross revenue rates.
    # Also, skip non-payable items.
    my $weHaveItems    = 0;
    my $incomeItemList = $album->IncomeItemList()->getList();
    foreach my $incomeItem (@$incomeItemList)
    {
        next if ($incomeItem->ContractRateTypeID() == RPS::DB::Item::ContractRateType::kRateTypePercentGrossRevenue);
        next if ($incomeItem->ContractRateTypeID() == RPS::DB::Item::ContractRateType::kRateTypePercentRevenue);
        next if ($incomeItem->ContractRateTypeID() == RPS::DB::Item::ContractRateType::kRateTypeNonPayable);
        $weHaveItems = 1;

        my @tempRow;
        my @tempRowLinks;
        foreach my $colTempRecord (@columnTemplate)
        {
            # Yes this is wacky.  Invoking a function based on the name.
            #
            no strict 'refs';
            my $funcName = $colTempRecord->{func};
            push @tempRow, &$funcName($incomeItem);

            if ( $colTempRecord->{linkfunc} ) {
                my $linkFunc = $colTempRecord->{linkfunc};
                push @tempRowLinks, &$linkFunc($incomeItem);
            } else {
                push @tempRowLinks, '';
            }
        }
        push @rows,     \@tempRow;
        push @rowLinks, \@tempRowLinks;
        push @rowProps, {};
    }


    # If there weren't any unit based income items, well, we don't even output this section.
    #
    if ($weHaveItems)
    {
        # We want a little line under the last income item.
        #
        my $numRows            = scalar(@rowProps);
        my $lastRowPropHashref = $rowProps[($numRows - 1)];
        $lastRowPropHashref->{lines} =
        [
            {
                start_col => ($numColumns - 1),
                end_col   => ($numColumns - 1),
                top_pad   => 2,
            },
        ];

        # Go through the column template to determine how wide the columns need to _actually_ be.
        #
        my $totalUnits;
        foreach my $colTempRecord (@columnTemplate)
        {
            $totalUnits += $colTempRecord->{geometry}{width};
        }

        my $unitSize = (9.75/in) / $totalUnits,

        # So, due to some rounding issues w/ unitSize, we need to add (or subtract) a couple of points from the first
        # column when there are more or less than 10 columns.  Otherwise it won't line up nicely with the last line.
        #
        my $fudge = (10 - $numColumns) * 2;

        # Force the columns to be fixed width.
        #
        my @colProps;
        for (my $i = 0; $i < scalar @columnTemplate; $i++)
        {
            my $colTempRecord = $columnTemplate[$i];
            my %props;
            $props{justify} = $colTempRecord->{geometry}{justify} if $colTempRecord->{geometry}{justify};
            $props{min_w}   = $colTempRecord->{geometry}{width} * $unitSize;
            $props{max_w}   = $colTempRecord->{geometry}{width} * $unitSize;
            $props{pag}     = $colTempRecord->{geometry}{pad};

            if (0 == $i)
            {
                $props{min_w} += $fudge;
                $props{max_w} += $fudge;
            }

            push @colProps, \%props;
        }


        my %args = (
            bottom_margin   => kBottomMargin+20,   # adding a bit of padding to avoid weirdness
            new_page_y      => kTopMargin-20,      # ditto
            new_page_func   => \&newPage,
            font            => $font->{Helvetica}{Roman},
            font_size       => $fontSize,
            font_dejavusans => $font->{DejaVuSans},
            column_props    => \@colProps,
            row_props       => \@rowProps,
        );
        $args{row_links} = \@rowLinks if ( $mode eq 'f' );

        my $tableObj = TableObjSupportEmbededFonts->new( $pdf, $page, \@rows, %args );

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


        # We need to set the min_w/max_w for the unit level income line based
        # on the data above it in order for things to line up.  The "-2" is the
        # width of the "Total" column within the columnTemplate, above.
        #
        my $min_w = (($totalUnits-$totalColumnWidth) * $unitSize) + $fudge;
        my $max_w = (($totalUnits-$totalColumnWidth) * $unitSize) + $fudge;

        # To get the 'Unit Level Income' line, we'll use yet another table
        # (that has just 1 line).
        #

        my $bottomTable = TableObjSupportEmbededFonts->new
        (
            $pdf, $page,
            [
                ['Unit Level Income:', formatMoney($album->UnitLevelIncome()) ],
            ],
            bottom_margin   => kBottomMargin+20,   # adding a bit of padding to avoid weirdness
            new_page_y      => kTopMargin-20,      # ditto
            new_page_func   => \&newPage,
            font            => $font->{Helvetica}{Bold},
            font_size       => $fontSize,
            font_dejavusans => $font->{DejaVuSans},
            column_props    =>
            [
                {
                    justify => 'right',
                    min_w   => $min_w,
                    max_w   => $max_w,
                    pad     => 0,
                },
                {
                    justify => 'right',
                    min_w   => (1/in),
                    max_w   => (1/in),
                    pad     => 0,
                },
            ],
            row_props =>
            [
                {
                    lines =>
                    [
                        {
                            start_col  => 0,
                            end_col    => 1,
                            top_pad    => 4,
                            bottom_pad => 2,
                        },
                    ],
                },
            ],
        );
        ($page, $y) = $bottomTable->print($x, $y);

    }

    return ($page, $y);
}# unitRoyalties

sub netRevenueRoyalties
{
    my ($pdf, $page, $mode, $x, $y, $album) = @_;
    my $fontSize = 7/pt;

    my @rows;
    my @rowLinks;
    my @rowProps;

    # Create the column templates.
    #
    my @columnTemplate;
    push @columnTemplate, {
        title    => 'Track Title',
        func     => '_outputTrackTitle',
        linkfunc => '_outputTrackLink',
        geometry => {
            width => 5,
            pad   => 0,
        },
    };
    push @columnTemplate, {
        title    => 'Source',
        func     => '_outputIncomeSource',
        geometry => {
            width => 1.75,
            pad   => 0,
        },
    };
    push @columnTemplate, {
        title    => 'Region',
        func     => '_outputRegion',
        geometry => {
            width => 2.1,
            pad   => 0,
        },
    };
    push @columnTemplate, {
        title    => 'Channel',
        func     => '_outputChannel',
        geometry => {
            width => 1.95,
            pad   => 0,
        },
    };
    push @columnTemplate, {
        title    => 'Rate',
        title2   => 'Type',
        func     => '_outputRateType',
        geometry => {
            width => 2.25,
            pad   => 0,
        },
    };
    push @columnTemplate, {
        title    => 'Base',
        title2   => 'Rate',
        func     => '_outputRoyaltyRate',
        geometry => {
            justify => 'right',
            width   => 2.2,
            pad     => 0,
        },
    } if $gShowRoyaltyRate; # Optional column

    push @columnTemplate, {
        title    => 'Pro-',
        title2   => 'ration',
        func     => '_outputProration',
        geometry => {
            justify => 'center',
            width   => 2,
            pad     => 0,
        },
    } if $gShowProration; # Optional column

    push @columnTemplate, {
        title    => 'Prorated',
        title2   => 'Rate',
        func     => '_outputProratedRate',
        geometry => {
            width => 2.1,
            pad   => 0,
        },
    } if $gShowProratedRate; # Optional column

    push @columnTemplate, {
        title    => 'Rate',  # FB109
        title2   => 'Redtn', # FB109
        func     => '_outputRateReduction',
        geometry => {
            width => 2,
            pad   => 0,
        },
    } if $gShowRateReduction; # Optional column

    push @columnTemplate, {
        title    => 'Pack-',  # FB109
        title2   => 'aging',  # FB109
        func     => '_outputPackagingDeduction',
        geometry => {
            width => 2,
            pad   => 0,
        },
    } if $gShowPackaging; # Optional column

    push @columnTemplate, {
        title    => 'Effective',
        title2   => 'Rate',
        func     => '_outputEffectiveRate',
        geometry => {
            justify => 'center',
            width   => 2.1,
            pad     => 0,
        },
    };

    push @columnTemplate, {
        title    => 'Gross',  # FB703
        title2   => 'Sales',  # FB703
        func     => '_outputGrossSales',
        geometry => {
            justify => 'right',
            width   => 3,
            pad     => 0,
        },
    } if $gShowGrossSales; # Optional column

    push @columnTemplate, {
        title    => '% of',  # FB109
        title2   => 'Sales', # FB109
        func     => '_outputPercentageOfSales',
        geometry => {
            width => 2,
            pad   => 0,
        },
    } if $gShowPercentageOfSales; # Optional column

    push @columnTemplate, {
        title    => 'Free',  # FB109
        title2   => 'Goods', # FB109
        func     => '_outputFreeGoodsDeduction',
        geometry => {
            width => 2,
            pad   => 0,
        },
    } if $gShowFreeGoods; # Optional column

    push @columnTemplate, {
        title    => 'Reserves',  # FB109
        func     => '_outputRevenueReserved',
        geometry => {
            width   => 2.5,
            justify => 'right',
            pad     => 0,
        },
    } if $gShowReserves; # Optional column

    push @columnTemplate, {
        title    => 'Liquid-', # FB109
        title2   => 'ations',  # FB109
        func     => '_outputRevenueLiquidated',
        geometry => {
            width   => 3,
            justify => 'right',
            pad     => 0,
        },
    } if $gShowLiquidations; # Optional column

    push @columnTemplate, {
        title    => 'Returns',  # FB109
        func     => '_outputReturns',
        geometry => {
            justify => 'right',
            width   => 2,
            pad     => 0,
        },
    } if $gShowReturns; # Optional column

    push @columnTemplate, {
        title    => 'Net Units',
        func     => '_outputNetUnits',
        geometry => {
            justify => 'right',
            width   => 3,
            pad     => 0,
        },
    };

    push @columnTemplate, {
        title    => 'Net Sales',
        func     => '_outputNetRevenue',
        geometry => {
            justify => 'right',
            width   => 3,
            pad     => 0,
        },
    };

    my $totalColumnWidth=3;
    push @columnTemplate, {
        title    => 'Total',
        func     => '_outputTotal',
        geometry => {
            justify => 'right',
            width   => $totalColumnWidth,
            pad     => 0,
        },
    };
    my $numColumns = scalar @columnTemplate;


    # Set up the header
    #
    push @rows,     [ (' ') x $numColumns ];
    push @rowLinks, [ ('') x $numColumns ];
    push @rowProps, {};

    push @rows,     ['Net Rev. Royalties', (' ') x ($numColumns - 1) ];
    push @rowLinks, [ ('') x $numColumns ];
    push @rowProps,
    {
        font => $font->{Helvetica}{Bold},
        lines =>
        [
            {
                start_col => 0,
                end_col => ($numColumns - 1),
                top_pad => 4,
                # bottom_pad => 2,
            },
        ],
        repeatRows => 1,
    };

    # Put the column titles into a row.
    #
    my @tempRow;
    my @tempRowLinks;
    foreach my $colTempRecord (@columnTemplate)
    {
        push @tempRow,      $colTempRecord->{title};
        push @tempRowLinks, '';
    }
    push @rows,     \@tempRow;
    push @rowLinks, \@tempRowLinks;


    # Check if we need to generate a 2nd row of title information
    # if not, then just set the row properties for the single title row.
    # Otherwise, we need to a) generate a 2nd row of title info and
    # b) set the row properties for the 1st and 2nd rows
    #

    my @tempRow2;
    my @tempRow2Links;
    my $hasSecondRow;
    foreach my $colTempRecord (@columnTemplate)
    {
        if( exists $colTempRecord->{title2} )
        {
            push @tempRow2, $colTempRecord->{title2};
            $hasSecondRow = 1;
        }
        else
        {
            push @tempRow2,      ' ';
            push @tempRow2Links, '';
        }
    }

    if( !$hasSecondRow )
    {
        # Just one title row, so set its properties
        #
        push @rowProps,
        {
            font  => $font->{Helvetica}{Bold},
            lines =>
            [
                {
                    start_col => 0,
                    end_col   => ($numColumns - 1),
                    top_pad   => 4,
                },
            ],
            repeat => 1,
        };
    }
    else
    {
        # Add second title row
        #
        push @rows,     \@tempRow2 if( $hasSecondRow );
        push @rowLinks, \@tempRow2Links if( $hasSecondRow );

        # Properties for first title row
        #
        push @rowProps,
        {
            font       => $font->{Helvetica}{Bold},
            repeatRows => 1,
        };

        # Properties for second title row
        #
        push @rowProps,
        {
            font  => $font->{Helvetica}{Bold},
            lines =>
            [
                {
                    start_col => 0,
                    end_col   => ($numColumns - 1),
                    top_pad   => 4,
                },
            ],
            repeatRows => 2,
        };
    }

    # Go through the entire income item list, and skip those that are not net revenue or gross revenue rates.
    #
    my $weHaveItems = 0;
    my $incomeItemList = $album->IncomeItemList()->getList();
    foreach my $incomeItem (@$incomeItemList)
    {
        next if ($incomeItem->ContractRateTypeID() != RPS::DB::Item::ContractRateType::kRateTypePercentRevenue
              && $incomeItem->ContractRateTypeID() != RPS::DB::Item::ContractRateType::kRateTypePercentGrossRevenue);
        $weHaveItems = 1;


        my @tempRow;
        my @tempRowLinks;
        foreach my $colTempRecord (@columnTemplate)
        {
            # Yes this is wacky.  Invoking a function based on the name.
            #
            no strict 'refs';
            my $funcName = $colTempRecord->{func};
            push @tempRow, &$funcName($incomeItem);

            if ( $colTempRecord->{linkfunc} ) {
                my $linkFunc = $colTempRecord->{linkfunc};
                push @tempRowLinks, &$linkFunc($incomeItem);
            } else {
                push @tempRowLinks, '';
            }
        }

        push @rows,     \@tempRow;
        push @rowLinks, \@tempRowLinks;
        push @rowProps, {};
    }


    # If there weren't any unit based income items, well, we don't even output this section.
    #
    if ($weHaveItems)
    {
        # We want a little line under the last income item.
        #
        my $numRows            = scalar(@rowProps);
        my $lastRowPropHashref = $rowProps[($numRows - 1)];
        $lastRowPropHashref->{lines} =
        [
            {
                start_col => ($numColumns - 1),
                end_col   => ($numColumns - 1),
                top_pad   => 2,
            },
        ];

        # Go through the column template to determine how wide the columns need to _actually_ be.
        #
        my $totalUnits;
        foreach my $colTempRecord (@columnTemplate)
        {
            $totalUnits += $colTempRecord->{geometry}{width};
        }

        my $unitSize = (9.75/in) / $totalUnits,


        # Force the columns to be fixed width.
        #
        my @colProps;
        foreach my $colTempRecord (@columnTemplate)
        {
            my %props;
            $props{justify} = $colTempRecord->{geometry}{justify} if $colTempRecord->{geometry}{justify};
            $props{min_w}   = $colTempRecord->{geometry}{width} * $unitSize;
            $props{max_w}   = $colTempRecord->{geometry}{width} * $unitSize;
            $props{pag}     = $colTempRecord->{geometry}{pad};

            push @colProps, \%props;
        }


        my %args = (
            bottom_margin   => kBottomMargin+20,   # adding a bit of padding to avoid weirdness
            new_page_y      => kTopMargin-20,      # ditto
            new_page_func   => \&newPage,
            font            => $font->{Helvetica}{Roman},
            font_size       => $fontSize,
            font_dejavusans => $font->{DejaVuSans},
            column_props    => \@colProps,
            row_props       => \@rowProps,
        );
        $args{row_links} = \@rowLinks if ( $mode eq 'f' );

        my $tableObj = TableObjSupportEmbededFonts->new( $pdf, $page, \@rows, %args );

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


        # We need to set the min_w/max_w for the net revenue income line based
        # on the data above it in order for things to line up.  The "-2" is the
        # width of the "Total" column within the columnTemplate, above.
        #
        my $min_w = ($totalUnits-2.35) * $unitSize;
        my $max_w = ($totalUnits-2.35) * $unitSize;

        # To get the 'Net Revenue Income' line, we'll use yet another table
        # (that has just 1 line).
        #
        my $bottomTable = TableObjSupportEmbededFonts->new
        (
            $pdf, $page,
            [
                ['Net Revenue Income:', formatMoney($album->NetRevenueIncome()) ],
            ],
            bottom_margin   => kBottomMargin+20,   # adding a bit of padding to avoid weirdness
            new_page_y      => kTopMargin-20,      # ditto
            new_page_func   => \&newPage,
            font            => $font->{Helvetica}{Bold},
            font_size       => $fontSize,
            font_dejavusans => $font->{DejaVuSans},
            column_props    =>
            [
                {
                    justify => 'right',
                    min_w   => $min_w,
                    max_w   => $max_w,
                    pad     => 0,
                },
                {
                    justify => 'right',
                    min_w   => 1/in,
                    max_w   => 1/in,
                    pad     => 0,
                },
            ],
            row_props =>
            [
                {
                    lines =>
                    [
                        {
                            start_col  => 0,
                            end_col    => 1,
                            top_pad    => 4,
                            bottom_pad => 2,
                        },
                    ],
                },
            ],
        );
        ($page, $y) = $bottomTable->print($x, $y);

    }

    return ($page, $y);
}# newRevenueRoyalties


sub licenseIncomeRoyalties
{
    my ($pdf, $page, $mode, $x, $y, $album) = @_;
    my $fontSize = 7/pt;

    my @rows;
    my @rowLinks;
    my @rowProps;

    # Set up the header
    #
    push @rows,     [' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '];
    push @rowLinks, [ '',  '',  '',  '',  '',  '',  '',  ''];
    push @rowProps, {};
    push @rows,     ['License Income Royalties', ' ', ' ', ' ', ' ', ' ', ' ', ' '];
    push @rowLinks, [                        '',  '',  '',  '',  '',  '',  '',  ''];
    push @rowProps,
    {
        font  => $font->{Helvetica}{Bold},
        lines =>
        [
            {
                start_col => 0,
                end_col   => 7,
                top_pad   => 4,
                # bottom_pad => 2,
            },
        ],
    };
    push @rows,     ['Track Title', 'Income Type', 'Memo', 'Rate Type', 'Rate', 'Units', 'Sales', 'Total'];
    push @rowLinks, [           '',            '',     '',          '',     '',      '',      '',      ''];
    push @rowProps,
    {
        font  => $font->{Helvetica}{Bold},
        lines =>
        [
            {
                start_col => 0,
                end_col   => 7,
                top_pad   => 4,
                # bottom_pad => 2,
            },
        ],
        repeat => 1,
    };

    # Go through the entire license income item list.
    #
    my $weHaveItems    = 0;
    my $incomeItemList = $album->LicenseIncomeItemList()->getList();
    foreach my $incomeItem (@$incomeItemList)
    {
        $weHaveItems = 1;


        # !!! We may have to truncate titles?
        # !!! Either that, or we implement some sort of cell wrapping.
        #
        push @rows,
        [
            $incomeItem->TrackName(),
            licenseIncomeTypeIDToName($incomeItem->LicenseIncomeTypeID()),
            $incomeItem->Memo(),
            "% Net Revenue",
            formatPercent($incomeItem->Rate()),
            formatNumber($incomeItem->Units()),
            formatMoney($incomeItem->Revenue()),
            formatMoney($incomeItem->NetRevenue()),
        ];
        push @rowLinks,
        [
            _outputTrackLink($incomeItem),
            '',
            '',
            '',
            '',
            '',
            '',
            '',
        ];

        push @rowProps, {};
    }


    # If there weren't any license income items, well, we don't even output this section.
    #
    if ($weHaveItems)
    {
        # We want a little line under the last income item.
        #
        my $numRows            = scalar(@rowProps);
        my $lastRowPropHashref = $rowProps[($numRows - 1)];
        $lastRowPropHashref->{lines} =
        [
            {
                start_col => 7,
                end_col   => 7,
                top_pad   => 2,
                # bottom_pad => 2,
            },
        ];

        # Force the columns to be fixed width.
        #
        my @colProps =
        (
            {
                min_w => 2/in,
                max_w => 2/in,
                pad   => 0,
            },
            {
                min_w => 1.75/in,
                max_w => 1.75/in,
                pad   => 0,
            },
            {
                min_w => 2.25/in,
                max_w => 2.25/in,
                pad   => 0,
            },
            {
                min_w => 1/in,
                max_w => 1/in,
                pad   => 0,
            },
            {
                min_w => .5/in,
                max_w => .5/in,
                pad   => 0,
            },
            {
                justify => 'right',
                min_w   => .5/in,
                max_w   => .5/in,
                pad     => 0,
            },
            {
                justify => 'right',
                min_w   => 1/in,
                max_w   => 1/in,
                pad     => 0,
            },
            {
                justify => 'right',
                min_w   => 1/in,
                max_w   => 1/in,
                pad     => 0,
            },
        );

        my %args = (
            bottom_margin   => kBottomMargin+20,   # adding a bit of padding to avoid weirdness
            new_page_y      => kTopMargin-20,      # ditto
            new_page_func   => \&newPage,
            font            => $font->{Helvetica}{Roman},
            font_size       => $fontSize,
            font_dejavusans => $font->{DejaVuSans},
            column_props    => \@colProps,
            row_props       => \@rowProps,
        );
        $args{row_links} = \@rowLinks if ( $mode eq 'f' );

        my $tableObj = TableObjSupportEmbededFonts->new( $pdf, $page, \@rows, %args );

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

        # To get the 'License Income Total' line, we'll use yet another table
        # (that has just 1 line).
        #
        my $bottomTable = TableObjSupportEmbededFonts->new
        (
            $pdf, $page,
            [
                ['License Income Total:', formatMoney($album->LicenseIncomeSubtotal()) ],
            ],
            bottom_margin   => kBottomMargin+20,   # adding a bit of padding to avoid weirdness
            new_page_y      => kTopMargin-20,      # ditto
            new_page_func   => \&newPage,
            font            => $font->{Helvetica}{Bold},
            font_size       => $fontSize,
            font_dejavusans => $font->{DejaVuSans},
            column_props    =>
            [
                {
                    justify => 'right',
                    min_w   => 9/in,
                    max_w   => 9/in,
                    pad     => 0,
                },
                {
                    justify => 'right',
                    min_w   => 1/in,
                    max_w   => 1/in,
                    pad     => 0,
                },
            ],
            row_props =>
            [
                {
                    lines =>
                    [
                        {
                            start_col  => 0,
                            end_col    => 1,
                            top_pad    => 4,
                            bottom_pad => 2,
                        },
                    ],
                },
            ],
        );
        ($page, $y) = $bottomTable->print($x, $y);


    }


    return ($page, $y);
}


sub albumTitle
{
    my ($pdf, $page, $mode, $x, $y, $album) = @_;
    my $fontSize  = 7/pt;
    my $fontToUse = $font->{Helvetica}{Bold};

    # We'll use a text object to calculate display widths
    #
    my $text = $page->text();
    $text->font($fontToUse, $fontSize);

    # The album table and corresponding line will be it's own table.
    #
    my $albumTitle        = $album->Album()->Title();
    my $catalogNumber     = $album->Album()->CatalogNumber();
    my $albumTitleCatalog = "$albumTitle ($catalogNumber)";
    my $albumWidth        = $text->advancewidth($albumTitleCatalog);

    my $separator         = '  -  ';
    my $separatorWidth    = $text->advancewidth($separator);

    my $contractName      = $album->ContractName();
    my $contractWidth     = $text->advancewidth($contractName);

    my @rows = [
        $albumTitleCatalog,
        $separator,
        $contractName,
    ];

    my @rowLinks = [
        '/rps/catalog?AlbumID=' . $album->Album()->AlbumID() . '&c=show',
        '',
        '/rps/artist_contract?ArtistContractID=' . $album->ArtistContractID() . '&c=show',
    ];


    my %args = (
        bottom_margin   => kBottomMargin+20,   # adding a bit of padding to avoid weirdness
        new_page_y      => kTopMargin-20,      # ditto
        new_page_func   => \&newPage,
        font            => $fontToUse,
        font_size       => $fontSize,
        font_dejavusans => $font->{DejaVuSans},
        row_props       =>
        [
            {},
        ],
        column_props =>
        [
            {
                min_w => $albumWidth,
                max_w => $albumWidth,
                pad   => 0,
            },
            {
                min_w => $separatorWidth,
                max_w => $separatorWidth,
                pad   => 0,
            },
            {
                min_w => $contractWidth,
                max_w => $contractWidth,
                pad => 0,
            },
        ],
    );

    $args{row_links} = \@rowLinks if ( $mode eq 'f' );

    my $albumTitleTable = TableObjSupportEmbededFonts->new( $pdf, $page, \@rows, %args);

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


    return ($page, $y);
}


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


    my @rows;
    my @rowProps;

    # Set up the header
    #
    push @rows, [' ', ' ', ' ', ' ', ' ', ' '];
    push @rowProps, {};
    push @rows, ['Recoupable Expenses', ' ', ' ', ' ', ' ', ' '];
    push @rowProps,
    {
        font => $font->{Helvetica}{Bold},
        lines =>
        [
            {
                start_col => 0,
                end_col => 5,
                top_pad => 4,
                # bottom_pad => 2,
            },
        ],
    };
    push @rows, ['Description', 'Memo', 'Cost', '', 'Rate', 'Total'];
    push @rowProps,
    {
        font => $font->{Helvetica}{Bold},
        lines =>
        [
            {
                start_col => 0,
                end_col => 5,
                top_pad => 4,
                # bottom_pad => 2,
            },
        ],
    };


    # Pull out the recoupable expenses, which have the 'UsesDefaultNetRate' flag set to 0
    #
    my $weHaveItems = 0;
    my $expenseItemList = $album->ExpenseItemList()->getList();
    foreach my $expenseItem (@$expenseItemList)
    {
        next if (1 == $expenseItem->UsesDefaultNetRate());
        $weHaveItems = 1;


        push @rows,
        [
            $expenseItem->ExpenseName(),
            $expenseItem->Memo(),
            formatMoney($expenseItem->Cost()),
            '',
            formatPercent($expenseItem->Rate()),
            formatMoney($expenseItem->Total()),
        ];

        push @rowProps, {};
    }

    # If there weren't any expense items, well, we don't even output this section.
    #
    if ($weHaveItems)
    {
        # We want a little line under the last item.
        #
        my $numRows = scalar(@rowProps);
        my $lastRowPropHashref = $rowProps[($numRows - 1)];
        $lastRowPropHashref->{lines} =
        [
            {
                start_col => 5,
                end_col => 5,
                top_pad => 2,
                # bottom_pad => 2,
            },
        ];

        # Force the columns to be fixed width.
        #
        my @colProps =
        (
            {
                min_w => 3/in,
                max_w => 3/in,
                pad => 0,
            },
            {
                min_w => 4/in,
                max_w => 4/in,
                pad => 0,
            },
            {
                min_w => 0.75/in,
                max_w => 0.75/in,
                pad => 0,
                justify => 'right',
            },
            {
                min_w => 0.5/in,
                max_w => 0.5/in,
                pad => 0,
            },
            {
                min_w => 0.75/in,
                max_w => 0.75/in,
                pad => 0,
            },
            {
                min_w => 1/in,
                max_w => 1/in,
                pad => 0,
                justify => 'right',
            },
        );


        my $tableObj = TableObjSupportEmbededFonts->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   => \&newPage,
            font            => $font->{Helvetica}{Roman},
            font_size       => $fontSize,
            font_dejavusans => $font->{DejaVuSans},
            column_props    => \@colProps,
            row_props       => \@rowProps,
        );

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


        # To get the bottom 'total' line, we'll use a seperate table.
        # (that has just 1 line).
        #
        my $bottomTable = TableObjSupportEmbededFonts->new
        (
            $pdf, $page,
            [
                ['Recoupable Expenses Total:', formatMoney($album->RecoupableExpenses()) ],
            ],
            bottom_margin   => kBottomMargin+20,   # adding a bit of padding to avoid weirdness
            new_page_y      => kTopMargin-20,      # ditto
            new_page_func   => \&newPage,
            font            => $font->{Helvetica}{Bold},
            font_size       => $fontSize,
            font_dejavusans => $font->{DejaVuSans},
            column_props    =>
            [
                {
                    justify => 'right',
                    min_w => 9/in,
                    max_w => 9/in,
                    pad => 0,
                },
                {
                    justify => 'right',
                    min_w => 1/in,
                    max_w => 1/in,
                    pad => 0,
                },
            ],
            row_props =>
            [
                {
                    lines =>
                    [
                        {
                            start_col => 0,
                            end_col => 1,
                            top_pad => 4,
                            bottom_pad => 2,
                        },
                    ],
                },
            ],
        );
        ($page, $y) = $bottomTable->print($x, $y);

    }


    return ($page, $y);
}

sub _mysort {
   my $aName = $a->ExpenseName();
   my $bName = $b->ExpenseName();
   if( $aName =~ /^\d+[\.\- ]/ && $bName =~ /^\d+[\.\- ]/ )
   {
       $aName =~ s/^(\d+)[\.\- ].+/$1/;
       $bName =~ s/^(\d+)[\.\- ].+/$1/;
       return $aName <=> $bName;
   }
   return $aName cmp $bName;
}

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

    my @rows;
    my @rowProps;

    # Set up the header
    #
    push @rows, [' ', ' ', ' ', ' ', ' ', ' ' ];
    push @rowProps, {};
    push @rows, ['Net Expenses', ' ', ' ', ' ', ' ', ' '];
    push @rowProps,
    {
        font => $font->{Helvetica}{Bold},
        lines =>
        [
            {
                start_col => 0,
                end_col => 5,
                top_pad => 4,
                # bottom_pad => 2,
            },
        ],
    };
    push @rows, ['Description', 'Memo', 'Cost', ' ', 'Rate', 'Total'];
    push @rowProps,
    {
        font => $font->{Helvetica}{Bold},
        lines =>
        [
            {
                start_col => 0,
                end_col => 5,
                top_pad => 4,
                # bottom_pad => 2,
            },
        ],
    };


    # Pull out the net revenue expenses, which have the 'UsesDefaultNetRate' flag set to 1
    #
    my $weHaveItems = 0;
    my $expenseItemList = $album->ExpenseItemList()->getList();
    foreach my $expenseItem (sort _mysort @$expenseItemList)
    {
        next if (1 != $expenseItem->UsesDefaultNetRate());
        $weHaveItems = 1;


        push @rows,
        [
            $expenseItem->ExpenseName(),
            $expenseItem->Memo(),
            formatMoney($expenseItem->Cost()),
            '',
            formatPercent($expenseItem->Rate()),
            formatMoney($expenseItem->Total()),
        ];

        push @rowProps, {};
    }

    # If there weren't any expense items, well, we don't even output this section.
    #
    if ($weHaveItems)
    {
        # We want a little line under the last item.
        #
        my $numRows = scalar(@rowProps);
        my $lastRowPropHashref = $rowProps[($numRows - 1)];
        $lastRowPropHashref->{lines} =
        [
            {
                start_col => 5,
                end_col => 5,
                top_pad => 2,
                # bottom_pad => 2,
            },
        ];

        # Force the columns to be fixed width.
        #
        my @colProps =
        (
            {
                min_w => 3/in,
                max_w => 3/in,
                pad => 0,
            },
            {
                min_w => 4/in,
                max_w => 4/in,
                pad => 0,
            },
            {
                min_w => 0.75/in,
                max_w => 0.75/in,
                pad => 0,
                justify => 'right',
            },
            {
                min_w => 0.5/in,
                max_w => 0.5/in,
                pad => 0,
            },
            {
                min_w => 0.75/in,
                max_w => 0.75/in,
                pad => 0,
            },
            {
                min_w => 1/in,
                max_w => 1/in,
                pad => 0,
                justify => 'right',
            },
        );


        my $tableObj = TableObjSupportEmbededFonts->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   => \&newPage,
            font            => $font->{Helvetica}{Roman},
            font_size       => $fontSize,
            font_dejavusans => $font->{DejaVuSans},
            column_props    => \@colProps,
            row_props       => \@rowProps,
        );

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


        # We'll use a seperate table for the last 3 lines (the total, net rate, and adjusted total)
        #
        my $bottomTable = TableObjSupportEmbededFonts->new
        (
            $pdf, $page,
            [
                ['Net Expenses Total:', formatMoney($album->NetRevenueExpenses()) ],
                ['Net Rate:', formatPercent($album->DefaultNetRevenueRate()) ],
                ['Net Expenses Adjusted Total:', formatMoney($album->NetRevenueExpensesSubTotal()) ],
            ],
            bottom_margin   => kBottomMargin+20,   # adding a bit of padding to avoid weirdness
            new_page_y      => kTopMargin-20,      # ditto
            new_page_func   => \&newPage,
            font            => $font->{Helvetica}{Bold},
            font_size       => $fontSize,
            font_dejavusans => $font->{DejaVuSans},
            column_props    =>
            [
                {
                    justify => 'right',
                    min_w => 9/in,
                    max_w => 9/in,
                    pad => 0,
                },
                {
                    justify => 'right',
                    min_w => 1/in,
                    max_w => 1/in,
                    pad => 0,
                },
            ],
            row_props =>
            [
                {},
                {
                    lines =>
                    [
                        {
                            start_col => 1,
                            end_col => 1,
                            top_pad => 2,
                            bottom_pad => 2,
                        },
                    ],
                },
                {
                    lines =>
                    [
                        {
                            start_col => 0,
                            end_col => 1,
                            top_pad => 2,
                            bottom_pad => 2,
                        },
                    ],
                },
            ],
        );
        ($page, $y) = $bottomTable->print($x, $y);

    }


    return ($page, $y);
}


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

    # Start on a fresh page.
    #
    $y = kTopMargin-10;

    my @rows;
    my @rowLinks;
    my @rowProps;

    # Set up the header
    #
    push @rows,     [' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '];
    push @rowLinks, [ '',  '',  '',  '',  '',  '',  '',  ''];
    push @rowProps, {};

    push @rows,     ['Contract Level License Income Royalties', ' ', ' ', ' ', ' ', ' ', ' ', ' '];
    push @rowLinks, [                                       '',  '',  '',  '',  '',  '',  '',  ''];
    push @rowProps,
    {
        font => $font->{Helvetica}{Bold},
        lines =>
        [
            {
                start_col => 0,
                end_col   => 7,
                top_pad   => 4,
                # bottom_pad => 2,
            },
        ],
    };
    push @rows,     ['Contract Title', 'Income Type', 'Memo', 'Rate Type', 'Rate', 'Units', 'Sales', 'Total'];
    push @rowLinks, [              '',            '',     '',          '',     '',      '',      '',      ''];
    push @rowProps,
    {
        font  => $font->{Helvetica}{Bold},
        lines =>
        [
            {
                start_col => 0,
                end_col   => 7,
                top_pad   => 4,
                # bottom_pad => 2,
            },
        ],
        repeat => 1,
    };

    # Go through the entire license income item list.
    #
    my $weHaveItems    = 0;
    my $incomeItemList = $statement->ArtistStatementLicenseIncomeItemList()->getList();
    foreach my $incomeItem (@$incomeItemList)
    {
        $weHaveItems = 1;


        # !!! We may have to truncate titles?
        # !!! Either that, or we implement some sort of cell wrapping.
        #
        push @rows,
        [
            $incomeItem->ContractTitle(),
            licenseIncomeTypeIDToName($incomeItem->LicenseIncomeTypeID()),
            $incomeItem->Memo(),
            "% Net Revenue",
            formatNumber($incomeItem->Rate())."%",
            formatNumber($incomeItem->Units()),
            formatMoney($incomeItem->Revenue()),
            formatMoney($incomeItem->NetRevenue()),
        ];

        push @rowLinks,
        [
            '/rps/artist_contract?ArtistContractID=' . $incomeItem->ArtistContractID() . '&c=show',
            '',
            '',
            '',
            '',
            '',
            '',
            '',
        ];
        push @rowProps, {};
    }


    # If there weren't any license income items, well, we don't even output this section.
    #
    if ($weHaveItems)
    {
        $page = newPage($pdf);

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

        # Force the columns to be fixed width.
        #
        my @colProps =
        (
            {
                min_w => 2/in,
                max_w => 2/in,
                pad   => 0,
            },
            {
                min_w => 1.75/in,
                max_w => 1.75/in,
                pad   => 0,
            },
            {
                min_w => 2.25/in,
                max_w => 2.25/in,
                pad   => 0,
            },
            {
                min_w => 1/in,
                max_w => 1/in,
                pad   => 0,
            },
            {
                min_w => .5/in,
                max_w => .5/in,
                pad   => 0,
            },
            {
                justify => 'right',
                min_w   => .5/in,
                max_w   => .5/in,
                pad     => 0,
            },
            {
                justify => 'right',
                min_w   => 1/in,
                max_w   => 1/in,
                pad     => 0,
            },
            {
                justify => 'right',
                min_w   => 1/in,
                max_w   => 1/in,
                pad     => 0,
            },
        );


        my %args = (
            bottom_margin   => kBottomMargin+20,   # adding a bit of padding to avoid weirdness
            new_page_y      => kTopMargin-20,      # ditto
            new_page_func   => \&newPage,
            font            => $font->{Helvetica}{Roman},
            font_size       => $fontSize,
            font_dejavusans => $font->{DejaVuSans},
            column_props    => \@colProps,
            row_props       => \@rowProps,
        );

        $args{row_links} = \@rowLinks if ( $mode eq 'f' );

        my $tableObj = TableObjSupportEmbededFonts->new( $pdf, $page, \@rows, %args );

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

        # To get the 'License Income Total' line, we'll use yet another table
        # (that has just 1 line).
        #
        my $bottomTable = TableObjSupportEmbededFonts->new
        (
            $pdf, $page,
            [
                ['License Income Total:', formatMoney($statement->ContractLevelLicenseIncomeSubtotal()) ],
            ],
            bottom_margin   => kBottomMargin+20,   # adding a bit of padding to avoid weirdness
            new_page_y      => kTopMargin-20,      # ditto
            new_page_func   => \&newPage,
            font            => $font->{Helvetica}{Bold},
            font_size       => $fontSize,
            font_dejavusans => $font->{DejaVuSans},
            column_props    =>
            [
                {
                    justify => 'right',
                    min_w   => 9/in,
                    max_w   => 9/in,
                    pad     => 0,
                },
                {
                    justify => 'right',
                    min_w   => 1/in,
                    max_w   => 1/in,
                    pad     => 0,
                },
            ],
            row_props =>
            [
                {
                    #lines =>
                    #[
                    #    {
                    #        start_col => 0,
                    #        end_col => 1,
                    #        top_pad => 2,
                    #        bottom_pad => 2,
                    #    },
                    #],
                },
            ],
        );
        ($page, $y) = $bottomTable->print($x, $y);


    }


    return ($page, $y);
}


sub addLegend
{
    my ($pdf, $incomeSourceList) = @_;
    my $page = newPage($pdf);
    my $y = kTopMargin;
    my $text = $page->text;

    # Add source legend to the first page only.
    #

    # Create a hash of income source names and descriptions.
    # There's probably somewhere we can fetch these from,
    # but since some of them have to be hard-coded anyway,
    # we're just going to do that for all of them for now.
    my %incomeSourceDescriptions = (
        'BG', 'Background',
        'CAS', 'Cassette',
        'CD', 'Compact Disc',
        'CD2', '2-Disc CD',
        'CD5', 'CD Single',
        'DA', 'Digital Album Permanent Download',
        'DA-P', 'Premium Digital Album Download',
        'DA-U', 'Digital Album Download Upgrade',
        'DD', 'Dual Download',
        'DS', 'Digital Stream',
        'DT', 'Digital Track Permanent Download',
        'DT-P', 'Premium Digital Track Download',
        'DT-U', 'Digital Track Download Upgrade',
        'DTETH', 'Digital Tethered Download',
        'DVDCD', 'DVD CD Set',
        'BD', 'BluRay DVD',
        'JB', 'Jukebox',
        'LP', '12 Inch Vinyl',
        'LP5', 'Vinyl Single',
        'MASTER', 'Master Use License',
        'SYNC-LIC', 'Synchronization Use License',
        'PI', 'Performance Income',
        'EPH', 'Ephemeral Performance Income',
        'N-EPH', 'Non-Ephemeral Performance Income',
        'RING', 'Ringtone',
        'SYNC', 'Synchronization Revenue',
        'VPD', 'Variable Priced Download'
    );


    my @incomeSourceNames;
    my $i = 0;
    foreach my $incomeSourceID (@$incomeSourceList)
    {
        $incomeSourceNames[$i] = incomeSourceIDToName($incomeSourceID),
        $i++;
    }
    @incomeSourceNames = sort(@incomeSourceNames);

    my $x = kLeftMargin;
    my $shiftedX = $x + 35;

    $y = printText($text, $x, $y, 'Source Abbreviations', $font->{Helvetica}{Bold}, 8/pt);
    $y -= 3/pt;

    foreach my $incomeSourceName (@incomeSourceNames)
    {
        printText($text, $x, $y, $incomeSourceName, $font->{Helvetica}{Bold}, 7/pt);
        $y = printText($text, $shiftedX, $y, $incomeSourceDescriptions{$incomeSourceName}, $font->{Helvetica}{Roman}, 7/pt);
        $y -= 1/pt;
    }

    $y -= 20/pt;

    return ($page, $y);

}


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

    my $pageCount = 1;
    my $numPages = scalar @gPages;
    foreach my $page (@gPages)
    {
        my $text = $page->text;
        printTextCentered($text, kBottomMargin, "$pageCount of $numPages", $font->{Helvetica}{Roman}, 12/pt);
        $pageCount++;
    }
}


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

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

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

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


# All the pages will _probably_ need the same settings.
#
sub newPage
{
    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 apperance 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 @gPages, $page;

    return $page;
}


# A helper routine to print some text centered.
# 'y' will tell it where to start - 'x' is basically ignored.
#
sub printTextCentered
{
    my ($text, $y, $textToPrint, $fontToUse, $fontSize, $color) = @_;

    $color = 'black' unless $color;

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

    my @strings = split(/\n/, $textToPrint);
    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);

        $y -= $fontSize;

        # Calculate where 'X' needs to be
        #
        my $x = ((kRightMargin - kLeftMargin)/2) + kLeftMargin - ($stringWidth / 2);

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

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

    return $y;
}


# This just prints a line of text with a hyperlink, left justified
# to the left margin unless "centered" is passed, in which case it
# centers the text.
#
sub printTextWithLink {
    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;
}

# This just prints a line of text, left justified to the left margin
#
sub printText
{
    my ($text, $x, $y, $textToPrint, $fontToUse, $fontSize, $color) = @_;

    $color = 'black' unless $color;

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

    my @strings = split(/\n/, $textToPrint);
    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);

        $y -= $fontSize;

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

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

    return $y;
}


# This prints out the legend
#
sub printLegend
{
    my ($text, $x, $y, $textToPrint, $fontToUse, $fontSize, $color) = @_;

    $color = 'black' unless $color;

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

    my @strings = split(/\n/, $textToPrint);
    foreach my $string (@strings)
    {
        # Calculate how wide this string will be.
        #
        #my $stringWidth = $text->advancewidth($string);

        $y -= $fontSize;

        # Calculate where 'X' needs to be
        # This is just a random guess right now
        #my $x = kRightMargin - 150;

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

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

    return $y;
}

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

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

    return $value;
}

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

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

    return $value;
}

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

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

    return $value;
}


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

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

    return $value;
}

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

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

    return $value;
}


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

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

sub incomeSourceIDToName
{
    my ($id) = @_;
    return _genericMapAccessor('RPS::DB::Item::IncomeSource', 'income_source_id', $id);
}

sub licenseIncomeTypeIDToName
{
    my ($id) = @_;
    return _genericMapAccessor('RPS::DB::Item::LicenseIncomeType', 'license_income_type_id', $id);
}

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

    if ($id == 0 && $defaultRate == 1)
    {
        return 'All';
    }
    else
    {
        return _genericMapAccessor('RPS::DB::Item::Region', 'region_id', $id);
    }
}

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



#
# Boring script stuff below...
#

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

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

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

    if (defined $opt{V})
    {
        $gVerbosityLevel = $opt{V};
    }
    if ( defined $opt{m} )
    {
        if ( $opt{m} =~ /^(F|P|B).*$/i ) {
            $settings->{mode} = lc $1;
        } else {
            usage();
            exit(1);
        }
    } else {
        $settings->{mode} = 'p';  # default to payee
    }
}


sub usage
{
    print STDERR "\nusage: $0 -c <client_id> -s <artist_statement_id> [-f <output file>] [-m FULL|PAYEE|BOTH]\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 <artist_statement_id>\tThe id of the artist royalty statement to convert to pdf\n";
    print STDERR "\t-f <output file>\tThe output file. Optional. If not provided, we'll make one up\n";
    print STDERR "\t-m <output mode>\tOptional: BOTH=generate both full (label) and payee statements\n";
    print STDERR "\t\tFULL=generate label statements only\n";
    print STDERR "\t\tPAYEE=generate payee statements only (default if mode not specified)\n";
}


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

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

sub _outputTrackTitle
{
    my ($incomeItem) = @_;
    return $incomeItem->TrackName();
}

sub _outputTrackLink
{
    my ($incomeItem) = @_;
    if( $incomeItem->TrackID() != 0 ) {
        return '/rps/track?TrackID=' . $incomeItem->TrackID() . '&c=show';
    }
    return '';
}

sub _outputIncomeSource
{
    my ($incomeItem) = @_;
    return incomeSourceIDToName($incomeItem->IncomeSourceID());
}

sub _outputRegion
{
    my ($incomeItem) = @_;
    return regionIDToName($incomeItem->RegionID(),$incomeItem->UsesDefaultNetRate());
}


sub _outputChannel
{
    my ($incomeItem) = @_;

    return channelIDToName($incomeItem->ChannelID());
}


sub _outputPriceTier
{
    my ($incomeItem) = @_;

    return priceLevelIDToName($incomeItem->PriceLevelID());
}


sub _outputSalesPrice
{
    my ($incomeItem) = @_;

    # Price is always '1' (and irrelevant) for fixed rate items.
    #
    return ' ' if $incomeItem->ContractRateTypeID() == RPS::DB::Item::ContractRateType::kRateTypeFixed;

    return formatMoney($incomeItem->Price());
}


sub _outputRateType
{
    my ($incomeItem) = @_;

    return contractRateTypeIDToName($incomeItem->ContractRateTypeID());
}

sub _outputRoyaltyRate
{
    my ($incomeItem) = @_;

    return formatNumber($incomeItem->BaseRate()); # FB113
}

sub _outputProration # FB113
{
    my ($incomeItem) = @_;

    if( $incomeItem->TrackName() && '' ne $incomeItem->TrackName )
    {
        if( $incomeItem->ProrateTrackCount() != 0 )
        {
            return formatNumber($incomeItem->ProrateTrackCount());
        }
        else
        {
            #return formatNumber(1);
            return ' ';
        }
    }

    return ' ';
}

sub _outputProratedRate # FB113
{
    my ($incomeItem) = @_;

    return formatNumber($incomeItem->Rate())
       if( $incomeItem->TrackName() && '' ne $incomeItem->TrackName );

    return ' ';
}

sub _outputRateReduction # FB109
{
    my ($incomeItem) = @_;

    return formatNumberSmall($incomeItem->RateReduction());
}

sub _outputPackagingDeduction # FB109
{
    my ($incomeItem) = @_;

    return formatNumberSmall($incomeItem->PackagingDeduction());
}

sub _outputEffectiveRate
{
    my ($incomeItem) = @_;

    return formatNumber($incomeItem->NetRate());
}

sub _outputGrossUnits # FB109
{
    my ($incomeItem) = @_;

    return formatNumber($incomeItem->Sales());
}

sub _outputGrossSales # FB703
{
    my ($incomeItem) = @_;

    return formatMoney($incomeItem->Revenue());
}

sub _outputPercentageOfSales # FB109
{
    my ($incomeItem) = @_;

    return formatNumberSmall($incomeItem->PercentageOfSales());
}

sub _outputFreeGoodsDeduction # FB109
{
    my ($incomeItem) = @_;

    return formatNumberSmall($incomeItem->FreeGoodsDeduction());
}

sub _outputUnitsReserved # FB109
{
    my ($incomeItem) = @_;

    return formatNumber($incomeItem->UnitsReserved());
}

sub _outputRevenueReserved # FB109
{
    my ($incomeItem) = @_;

    return formatMoney($incomeItem->RevenueReserved());
}

sub _outputUnitsLiquidated # FB109
{
    my ($incomeItem) = @_;

    return formatNumber($incomeItem->UnitsLiquidated());
}

sub _outputRevenueLiquidated # FB109
{
    my ($incomeItem) = @_;

    return formatMoney($incomeItem->RevenueLiquidated());
}

sub _outputReturns # FB109
{
    my ($incomeItem) = @_;

    return formatNumber($incomeItem->Returns());
}

sub _outputNetUnits
{
    my ($incomeItem) = @_;
    return formatNumber($incomeItem->NetUnits());
}


sub _outputNetRevenue
{
    my ($incomeItem) = @_;
    return formatMoney($incomeItem->NetRevenue());
}

sub _outputTotal
{
    my ($incomeItem) = @_;

    return formatMoney($incomeItem->Total());
}



