#!/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 TableObj;
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
#
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);
createStatementPDF($options{clientID}, $options{artistStatementID}, $options{outputFile});


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




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

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


    # Grab the statement settings (some columns are optional), and record these
    # in some _global_ variables.
    #
    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;
    }


    # 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 => $artistStatementID);


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


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


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

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

    # Create the first (blank) page.
    # Each output subroutine will take the current page as a parameter, and will
    # return the page that they ended on.
    #
    my $page = newPage($pdf);
    
    
	# Add label logo to the first page, if desired
	#	
	my $payor = $statement->Payor();
	my $showLogo = $payor->ShowLogo();
		 
    if ($showLogo == 1) 
    {
         my $clientNameClean = Common::Client::Current()->ClientNameClean();
    	($pdf, $page) = EmbedImage::labelLogo($pdf, $page, $clientNameClean, kTopMargin, kRightMargin);
    }    


    # Each section of the statement will have its own subroutine.
    #
    ($x, $y, $page) = header($pdf, $statement, $x, $y, $page);
    ($x, $y, $page) = summary($pdf, $statement, $x, $y, $page);
    ($x, $y, $page) = details($pdf, $statement, $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
    #
    $pdf->save();
    $pdf->end();
}


sub header
{
    my ($pdf, $statement, $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);
    
#    report("payor: " . Dumper($statement->{Payor}));		

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

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

    $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, $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, $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 = TableObj->new
    (
        $pdf, $page, \@balanceArray,
        bottom_margin => kBottomMargin+20,   # adding a bit of padding to avoid weirdness
        new_page_y => kTopMargin-20,         # ditto
        new_page_func => \&newPage,
        font => $font->{Helvetica}{Roman},
        font_size => $fontSize,
        column_props =>
        [
            {
                font => $font->{Helvetica}{Bold},
                font_size => $fontSize,
#                justify => 'center',
                min_w => 1.25/in,
                max_w => 1.25/in,
            },
            {
                font => $font->{Helvetica}{Roman},
                font_size => $fontSize,
                justify => 'right',
                min_w => 1.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 = TableObj->new
    (
        $pdf, $page, \@amountDueArray,
        bottom_margin => kBottomMargin+20,   # adding a bit of padding to avoid weirdness
        new_page_y => kTopMargin-20,         # ditto
        new_page_func => \&newPage,
        font => $font->{Helvetica}{Roman},
        font_size => $bigFontSize,
        column_props =>
        [
            {
                font => $font->{Helvetica}{Bold},
                font_size => $bigFontSize,
                min_w => 1.25/in,
                max_w => 1.25/in,
            },
            {
                font => $font->{Helvetica}{Roman},
                font_size => $bigFontSize,
                justify => 'right',
                min_w => 1.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, $x, $y, $page) = @_;
    my $fontSize = 7/pt;


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

    my @crossedArray;
    my @uncrossedArray;

    # Add the header
    #
    push @productsArray, [' ', 'Type', 'Album', 'Sales', 'License Income', 'Expenses', 'Previous Balance', 'Total'];  # the header
    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.
        #
        if ( @$incomeItemList == 0 ||
             $album->TotalExpenses() != 0 )
        {
           $skipAlbum = 0;
        }

        if ($skipAlbum == 1)
        {
            next;
        }
        
        my @row;

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

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


    # Now combine the two arrays.
    #
    if (0 != scalar @crossedArray)
    {
        foreach my $crossedRow (@crossedArray)
        {
            push @productsArray, $crossedRow;
            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 @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 @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 @rowProps, {};
    }

    if (0 != scalar @uncrossedArray)
    {
        foreach my $uncrossedRow (@uncrossedArray)
        {
            push @productsArray, $uncrossedRow;
            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 @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 $tableObj = TableObj->new
		    (
		        $pdf, $page, \@productsArray,
		        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,
		        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,
		    );
		
		
		    ($page, $y) = $tableObj->print($x, $y);
    
    }


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


sub details
{
    my ($pdf, $statement, $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->TotalExpenses() != 0 )
        {
           $skipAlbum = 0;
        }

        if ($skipAlbum == 1)
        {
            next;
        }        
        
        ($page, $y) = albumDetails($pdf, $page, $x, $y, $album, $withLegend);
        $withLegend = 0;
    }
    
    ($page, $y) = contractLicenseIncomeDetails($pdf, $page, $x, $y, $statement, $withLegend);

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


sub albumDetails
{
    my ($pdf, $page, $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, $x, $y, $album);
    ($page, $y) = unitRoyalties($pdf, $page, $x, $y, $album);
    ($page, $y) = netRevenueRoyalties($pdf, $page, $x, $y, $album);
    ($page, $y) = licenseIncomeRoyalties($pdf, $page, $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 = TableObj->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,
        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);
}


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

    my @rows;
    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', 
        geometry => {
            width => 5,
#            min_w => 2.08/in,
#            max_w => 2.08/in,
            pad => 0,
        },
    };
    push @columnTemplate, { 
        title => 'Source', 
        func => '_outputIncomeSource', 
        geometry => {
            width => 2,
#            min_w => 0.4165/in,
#            max_w => 0.4165/in,
            pad => 0,
        },
    };
    push @columnTemplate, { 
        title => 'Region', 
        func => '_outputRegion', 
        geometry => {
            width => 2,
#            min_w => 0.833/in,
#            max_w => 0.833/in,
            pad => 0,
        },
    };
    push @columnTemplate, { 
        title => 'Channel', 
        func => '_outputChannel', 
        geometry => {
            width => 2,
#            min_w => 0.833/in,
#            max_w => 0.833/in,
            pad => 0,
        },
    };
    push @columnTemplate, { 
        title => 'Price', 
        title2 => 'Tier', 
        func => '_outputPriceTier', 
        geometry => {
            width => 2,
#            min_w => 0.833/in,
#            max_w => 0.833/in,
            pad => 0,
        },
    };
    push @columnTemplate, { 
        title => 'Sales', 
        title2 => 'Price', 
        func => '_outputSalesPrice', 
        geometry => {
            width => 2,
#            min_w => 0.833/in,
#            max_w => 0.833/in,
            pad => 0,
        },
    } if $gShowSalesPrice; # Optional column
    push @columnTemplate, { 
        title => 'Rate', 
        title2 => 'Type', 
        func => '_outputRateType', 
        geometry => {
            width => 2,
#            min_w => 0.833/in,
#            max_w => 0.833/in,
            pad => 0,
        },
    };
    push @columnTemplate, { 
        title => 'Base', 
        title2 => 'Rate', 
        func => '_outputRoyaltyRate', 
        geometry => {
            width => 2,
#            min_w => 0.833/in,
#            max_w => 0.833/in,
            pad => 0,
        },
    } if $gShowRoyaltyRate; # Optional column
    push @columnTemplate, { 
        title => 'Proration',  # FB113
        func => '_outputProration', 
        geometry => {
            justify => 'center',
            width => 2,
#            min_w => 0.833/in,
#            max_w => 0.833/in,
            pad => 0,
        },
    } if $gShowProration; # Optional column
    push @columnTemplate, { 
        title => 'Prorated',  # FB113
        title2 => 'Rate',  # FB113
        func => '_outputProratedRate', 
        geometry => {
            width => 2,
#            min_w => 0.833/in,
#            max_w => 0.833/in,
            pad => 0,
        },
    } if $gShowProratedRate; # Optional column
    push @columnTemplate, { 
        title => 'Rate ',  # FB109
        title2 => 'Redtn',  # FB109
        func => '_outputRateReduction', 
        geometry => {
            width => 2,
#            min_w => 0.833/in,
#            max_w => 0.833/in,
            pad => 0,
        },
    } if $gShowRateReduction; # Optional column
    push @columnTemplate, { 
        title => 'Pack- ',  # FB109
        title2 => 'aging',  # FB109
        func => '_outputPackagingDeduction', 
        geometry => {
            width => 2,
#            min_w => 0.833/in,
#            max_w => 0.833/in,
            pad => 0,
        },
    } if $gShowPackaging; # Optional column

    push @columnTemplate, { 
        title => 'Effective', 
        title2 => 'Rate', 
        func => '_outputEffectiveRate', 
        geometry => {
            justify => 'center',
            width => 2,
#            min_w => 0.833/in,
#            max_w => 0.833/in,
            pad => 0,
        },
    };

    push @columnTemplate, { 
        title => 'Gross',  # FB109
        title2 => 'Units',  # FB109
        func => '_outputGrossUnits', 
        geometry => {
            justify => 'center',
            width => 2,
#            min_w => 0.833/in,
#            max_w => 0.833/in,
            pad => 0,
        },
    } if $gShowGrossUnits; # Optional column
    push @columnTemplate, { 
        title => '% of',  # FB109
        title2 => 'Sales',  # FB109
        func => '_outputPercentageOfSales', 
        geometry => {
            width => 2,
#            min_w => 0.833/in,
#            max_w => 0.833/in,
            pad => 0,
        },
    } if $gShowPercentageOfSales; # Optional column
    push @columnTemplate, { 
        title => 'Free',  # FB109
        title2 => 'Goods',  # FB109
        func => '_outputFreeGoodsDeduction', 
        geometry => {
            width => 2,
#            min_w => 0.833/in,
#            max_w => 0.833/in,
            pad => 0,
        },
    } if $gShowFreeGoods; # Optional column
    push @columnTemplate, { 
        title => 'Reserves',  # FB109
        func => '_outputUnitsReserved', 
        geometry => {
            justify => 'right',
            width => 2,
#            min_w => 0.833/in,
#            max_w => 0.833/in,
            pad => 0,
        },
    } if $gShowReserves; # Optional column
    push @columnTemplate, { 
        title => 'Liquid-',  # FB109
        title2 => 'ations',  # FB109
        func => '_outputUnitsLiquidated', 
        geometry => {
            justify => 'right',
            width => 2,
#            min_w => 0.833/in,
#            max_w => 0.833/in,
            pad => 0,
        },
    } if $gShowLiquidations; # Optional column
    push @columnTemplate, { 
        title => 'Returns',  # FB109
        func => '_outputReturns', 
        geometry => {
            justify => 'right',
            width => 2,
#            min_w => 0.833/in,
#            max_w => 0.833/in,
            pad => 0,
        },
    } if $gShowReturns; # Optional column

    push @columnTemplate, { 
        title => 'Net Units', 
        func => '_outputNetUnits', 
        geometry => {
            justify => 'right',
            width => 2,
#            min_w => 0.833/in,
#            max_w => 0.833/in,
            pad => 0,
        },
    };

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

    # Set up the header
    #
    push @rows, [ (' ') x $numColumns ];
    push @rowProps, {};
    push @rows, ['Unit Royalties', (' ') x ($numColumns - 1) ];
    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;
    foreach my $colTempRecord (@columnTemplate)
    {
        push @tempRow, $colTempRecord->{title};
    }
    push @rows, \@tempRow;

    # 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 $hasSecondRow;
    foreach my $colTempRecord (@columnTemplate)
    {
        if( exists $colTempRecord->{title2} )
        {
            push @tempRow2, $colTempRecord->{title2};
            $hasSecondRow = 1;
        }
        else
        {
            push @tempRow2, ' ';
        }
    }

    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,
                    # bottom_pad => 2,
                },
            ],
            repeat => 1,
        };
    }
    else
    {
        # Add second title row
        #
        push @rows, \@tempRow2 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,
                    # bottom_pad => 2,
                },
            ],
            repeatRows => 2,
        };
    }

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

        my @tempRow;
        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);
        }
        push @rows, \@tempRow;

#        push @rows,
#        [
#            $incomeItem->TrackName(),
#            incomeSourceIDToName($incomeItem->IncomeSourceID()),
#            regionIDToName($incomeItem->RegionID(),$incomeItem->UsesDefaultNetRate()),
#            channelIDToName($incomeItem->ChannelID()),
#            priceLevelIDToName($incomeItem->PriceLevelID()),
#            contractRateTypeIDToName($incomeItem->ContractRateTypeID()),
#            formatNumber($incomeItem->NetRate()),
#            formatNumber($incomeItem->NetUnits()),
#            formatMoney($incomeItem->Total()),
#        ];

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

        ($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 = TableObj->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,
            column_props => 
            [
                {
                    justify => 'right',
                    #min_w => (9/in),
                    #max_w => (9/in),
                    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);
}

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

    my @rows;
    my @rowProps;

    # Create the column templates.
    #
    my @columnTemplate;
    push @columnTemplate, { 
        title => 'Track Title', 
        func => '_outputTrackTitle', 
        geometry => {
            width => 5,
#            min_w => 2.25/in,
#            max_w => 2.25/in,
            pad => 0,
        },
    };
    push @columnTemplate, { 
        title => 'Source', 
        func => '_outputIncomeSource', 
        geometry => {
            width => 2,
#            min_w => 0.45/in,
#            max_w => 0.45/in,
            pad => 0,
        },
    };
    push @columnTemplate, { 
        title => 'Region', 
        func => '_outputRegion', 
        geometry => {
            width => 2,
#            min_w => 0.9/in,
#            max_w => 0.9/in,
            pad => 0,
        },
    };
    push @columnTemplate, { 
        title => 'Channel', 
        func => '_outputChannel', 
        geometry => {
            width => 2,
#            min_w => 0.9/in,
#            max_w => 0.9/in,
            pad => 0,
        },
    };
    push @columnTemplate, { 
        title => 'Rate', 
        title2 => 'Type', 
        func => '_outputRateType', 
        geometry => {
            width => 2,
#            min_w => 0.9/in,
#            max_w => 0.9/in,
            pad => 0,
        },
    };
    push @columnTemplate, { 
        title => 'Base', 
        title2 => 'Rate', 
        func => '_outputRoyaltyRate', 
        geometry => {
            justify => 'right',
            width => 2,
#            min_w => 0.9/in,
#            max_w => 0.9/in,
            pad => 0,
        },
    } if $gShowRoyaltyRate; # Optional column

    push @columnTemplate, { 
        title => 'Proration', 
        func => '_outputProration', 
        geometry => {
            justify => 'center',
            width => 2,
#            min_w => 0.833/in,
#            max_w => 0.833/in,
            pad => 0,
        },
    } if $gShowProration; # Optional column
    push @columnTemplate, { 
        title => 'Prorated', 
        title2 => 'Rate', 
        func => '_outputProratedRate', 
        geometry => {
            width => 2,
#            min_w => 0.833/in,
#            max_w => 0.833/in,
            pad => 0,
        },
    } if $gShowProratedRate; # Optional column

    push @columnTemplate, { 
        title => 'Rate',  # FB109
        title2 => 'Redtn',  # FB109
        func => '_outputRateReduction', 
        geometry => {
            width => 2,
#            min_w => 0.833/in,
#            max_w => 0.833/in,
            pad => 0,
        },
    } if $gShowRateReduction; # Optional column
    push @columnTemplate, { 
        title => 'Pack-',  # FB109
        title2 => 'aging',  # FB109
        func => '_outputPackagingDeduction', 
        geometry => {
            width => 2,
#            min_w => 0.833/in,
#            max_w => 0.833/in,
            pad => 0,
        },
    } if $gShowPackaging; # Optional column
    push @columnTemplate, { 
        title => 'Effective', 
        title2 => 'Rate', 
        func => '_outputEffectiveRate', 
        geometry => {
            justify => 'center',
            width => 2,
#            min_w => 0.9/in,
#            max_w => 0.9/in,
            pad => 0,
        },
    };

    push @columnTemplate, { 
        title => 'Gross',  # FB703
        title2 => 'Sales',  # FB703
        func => '_outputGrossSales', 
        geometry => {
            justify => 'center',
            width => 2,
#            min_w => 0.833/in,
#            max_w => 0.833/in,
            pad => 0,
        },
    } if $gShowGrossSales; # Optional column
    push @columnTemplate, { 
        title => '% of',  # FB109
        title2 => 'Sales',  # FB109
        func => '_outputPercentageOfSales', 
        geometry => {
            width => 2,
#            min_w => 0.833/in,
#            max_w => 0.833/in,
            pad => 0,
        },
    } if $gShowPercentageOfSales; # Optional column
    push @columnTemplate, { 
        title => 'Free',  # FB109
        title2 => 'Goods',  # FB109
        func => '_outputFreeGoodsDeduction', 
        geometry => {
            width => 2,
#            min_w => 0.833/in,
#            max_w => 0.833/in,
            pad => 0,
        },
    } if $gShowFreeGoods; # Optional column
    push @columnTemplate, { 
        title => 'Reserves',  # FB109
        func => '_outputRevenueReserved', 
        geometry => {
            width => 2,
            justify => 'right',
#            min_w => 0.833/in,
#            max_w => 0.833/in,
            pad => 0,
        },
    } if $gShowReserves; # Optional column
    push @columnTemplate, { 
        title => 'Liquid-',  # FB109
        title2 => 'ations',  # FB109
        func => '_outputRevenueLiquidated', 
        geometry => {
            width => 2,
            justify => 'right',
#            min_w => 0.833/in,
#            max_w => 0.833/in,
            pad => 0,
        },
    } if $gShowLiquidations; # Optional column
    push @columnTemplate, { 
        title => 'Returns',  # FB109
        func => '_outputReturns', 
        geometry => {
            justify => 'right',
            width => 2,
#            min_w => 0.833/in,
#            max_w => 0.833/in,
            pad => 0,
        },
    } if $gShowReturns; # Optional column
    push @columnTemplate, { 
        title => 'Net Units', 
        func => '_outputNetUnits', 
        geometry => {
            justify => 'right',
            width => 2,
#            min_w => 0.9/in,
#            max_w => 0.9/in,
            pad => 0,
        },
    };
    push @columnTemplate, { 
        title => 'Net Sales', 
        func => '_outputNetRevenue', 
        geometry => {
            justify => 'right',
            width => 2,
#            min_w => 0.9/in,
#            max_w => 0.9/in,
            pad => 0,
        },
    };
    my $totalColumnWidth=2;
    push @columnTemplate, { 
        title => 'Total', 
        func => '_outputTotal', 
        geometry => {
            justify => 'right',
            width => $totalColumnWidth,
#            min_w => 0.9/in,
#            max_w => 0.9/in,
            pad => 0,
        },
    };
    my $numColumns = scalar @columnTemplate;

    
    # Set up the header
    #
    push @rows, [ (' ') x $numColumns ];
    push @rowProps, {};
    push @rows, ['Net Revenue Royalties', (' ') x ($numColumns - 1) ];
    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;
    foreach my $colTempRecord (@columnTemplate)
    {
        push @tempRow, $colTempRecord->{title};
    }
    push @rows, \@tempRow;


    # 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 $hasSecondRow;
    foreach my $colTempRecord (@columnTemplate)
    {
        if( exists $colTempRecord->{title2} )
        {
            push @tempRow2, $colTempRecord->{title2};
            $hasSecondRow = 1;
        }
        else
        {
            push @tempRow2, ' ';
        }
    }

    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,
                    # bottom_pad => 2,
                },
            ],
            repeat => 1,
        };
    }
    else
    {
        # Add second title row
        #
        push @rows, \@tempRow2 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,
                    # bottom_pad => 2,
                },
            ],
            repeatRows => 2,
        };
    }

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

        my @tempRow;
        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);
        }
        push @rows, \@tempRow;

#        push @rows,
#        [
#            $incomeItem->TrackName(),
#            incomeSourceIDToName($incomeItem->IncomeSourceID()),
#            regionIDToName($incomeItem->RegionID(),$incomeItem->UsesDefaultNetRate()),
#            channelIDToName($incomeItem->ChannelID()),
#            contractRateTypeIDToName($incomeItem->ContractRateTypeID()),
#            formatNumber($incomeItem->NetRate())."%",
#            formatNumber($incomeItem->NetUnits()),
#            formatMoney($incomeItem->NetRevenue()),
#            formatMoney($incomeItem->Total()),
#        ];

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

        ($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-$totalColumnWidth) * $unitSize;
        my $max_w = ($totalUnits-$totalColumnWidth) * $unitSize;

        # To get the 'Net Revenue Income' line, we'll use yet another table
        # (that has just 1 line).
        #
        my $bottomTable = TableObj->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,
            column_props => 
            [
                {
                    justify => 'right',
                    #min_w => 9/in,
                    #max_w => 9/in,
                    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);
}


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

    my @rows;
    my @rowProps;

    # Set up the header
    #
    push @rows, [' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '];
    push @rowProps, {};
    push @rows, ['License Income Royalties', ' ', ' ', ' ', ' ', ' ', ' ', ' '];
    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 @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 @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 $tableObj = TableObj->new
        (
            $pdf, $page, \@rows,
            bottom_margin => kBottomMargin+20,   # adding a bit of padding to avoid weirdness
            new_page_y => kTopMargin-20,         # ditto
            new_page_func => \&newPage,
            font => $font->{Helvetica}{Roman},
            font_size => $fontSize,
            column_props => \@colProps,
            row_props => \@rowProps,
        );

        ($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 = TableObj->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,
            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, $x, $y, $album) = @_;
    my $fontSize = 7/pt;

    # The album table and corresponding line will be it's own table.
    #
    my $albumTitle = $album->Album()->Title();
    my $catalogNumber = $album->Album()->CatalogNumber();
    my $contractName = $album->ContractName();

    my $albumTitleTable = TableObj->new
    (
        $pdf, 
        $page, 
        [ 
#            [ ' ' ],  
            [ "$albumTitle ($catalogNumber)   -   $contractName"],
        ],
        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,
        row_props =>
        [
#            {
#                lines => 
#                [
#                    { 
#                        start_col => 0,
#                        end_col => 0,
#                        top_pad => 0,
#                    },
#                ],
#            },
            {},
        ],
        column_props =>
        [
            {
                min_w => (kRightMargin - kLeftMargin),
                max_w => (kRightMargin - kLeftMargin),
            },
        ],
    );

    ($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 = TableObj->new
        (
            $pdf, $page, \@rows,
            bottom_margin => kBottomMargin+20,   # adding a bit of padding to avoid weirdness
            new_page_y => kTopMargin-20,         # ditto
            new_page_func => \&newPage,
            font => $font->{Helvetica}{Roman},
            font_size => $fontSize,
            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 = TableObj->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,
            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 = TableObj->new
        (
            $pdf, $page, \@rows,
            bottom_margin => kBottomMargin+20,   # adding a bit of padding to avoid weirdness
            new_page_y => kTopMargin-20,         # ditto
            new_page_func => \&newPage,
            font => $font->{Helvetica}{Roman},
            font_size => $fontSize,
            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 = TableObj->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,
            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, $x, $y, $statement, $withLegend) = @_;
    my $fontSize = 7/pt;
    
    
    
    my @rows;
    my @rowProps;

    # Set up the header
    #
    push @rows, [' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '];
    push @rowProps, {};
    push @rows, ['Contract Level License Income Royalties', ' ', ' ', ' ', ' ', ' ', ' ', ' '];
    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 @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 @rowProps, {};
    }


    # If there weren't any license income items, well, we don't even output this section.
    #
    if ($weHaveItems)
    {
    		if ($withLegend != 1)
    		{
    		  $page = newPage($pdf);
					$y = kTopMargin-10;    		  
    		}
    		
        # 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 $tableObj = TableObj->new
        (
            $pdf, $page, \@rows,
            bottom_margin => kBottomMargin+20,   # adding a bit of padding to avoid weirdness
            new_page_y => kTopMargin-20,         # ditto
            new_page_func => \&newPage,
            font => $font->{Helvetica}{Roman},
            font_size => $fontSize,
            column_props => \@colProps,
            row_props => \@rowProps,
        );

        ($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 = TableObj->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,
            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 contractLicenseIncomeDetails
{
    my ($pdf, $page, $x, $y, $statement) = @_;
    my $fontSize = 7/pt;

    # Start on a fresh page.
    #
    
    $y = kTopMargin-10;
    
    
    my @rows;
    my @rowProps;

    # Set up the header
    #
    push @rows, [' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '];
    push @rowProps, {};
    push @rows, ['Contract Level License Income Royalties', ' ', ' ', ' ', ' ', ' ', ' ', ' '];
    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 @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 @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 $tableObj = TableObj->new
        (
            $pdf, $page, \@rows,
            bottom_margin => kBottomMargin+20,   # adding a bit of padding to avoid weirdness
            new_page_y => kTopMargin-20,         # ditto
            new_page_func => \&newPage,
            font => $font->{Helvetica}{Roman},
            font_size => $fontSize,
            column_props => \@colProps,
            row_props => \@rowProps,
        );

        ($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 = TableObj->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,
            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',
			'PI', 'Performance Income',
			'RING', 'Ringtone',
			'SYNC', 'Synchronization Use License',
			'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);
    
    #$y = printLegend($text, $y, 'BG = Background', $font->{Helvetica}{Roman}, 7/pt);
		#$y = printLegend($text, $y, 'DA = Digital Album Permanent Download', $font->{Helvetica}{Roman}, 7/pt);
		#$y = printLegend($text, $y, 'DS = Digital Stream', $font->{Helvetica}{Roman}, 7/pt);
		#$y = printLegend($text, $y, 'DT = Digital Track Permanent Download', $font->{Helvetica}{Roman}, 7/pt);
		#$y = printLegend($text, $y, 'DTETH = Digital Tethered Download', $font->{Helvetica}{Roman}, 7/pt);
		#$y = printLegend($text, $y, 'JB = Jukebox', $font->{Helvetica}{Roman}, 7/pt);
		#$y = printLegend($text, $y, 'VPD = Variable Priced Download', $font->{Helvetica}{Roman}, 7/pt);
		#$y = printLegend($text, $y, 'DA-P = Premium Digital Album Download', $font->{Helvetica}{Roman}, 7/pt);
		#$y = printLegend($text, $y, 'DT-P = Premium Digital Track Download', $font->{Helvetica}{Roman}, 7/pt);
		#$y = printLegend($text, $y, 'DA-U = Digital Album Download Upgrade', $font->{Helvetica}{Roman}, 7/pt);
		#$y = printLegend($text, $y, 'DT-U = Digital Track Download Upgrade', $font->{Helvetica}{Roman}, 7/pt);
		#$y = printLegend($text, $y, 'RING = Ringtone', $font->{Helvetica}{Roman}, 7/pt);
		
}


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, 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 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:', \%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};
    }
}


sub usage
{
    print STDERR "\nusage: $0 -c <client_id> -s <artist_statement_id> [-f <output file>]\n";
    print STDERR "\n";
    print STDERR "Arguments:\n";
    print STDERR "\t-c <client_id>\t\t\tThe client_id of the client to process\n";
    print STDERR "\t-s <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";
}


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 _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 formatNumber($incomeItem->RateReduction());
}

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

    return formatNumber($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 formatNumber($incomeItem->PercentageOfSales());
}

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

    return formatNumber($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());
}



