#!/usr/bin/perl
#------------------------------------------------------------
# Copyright (C) 2006 RoyaltyShare, Inc.   All Rights Reserved
# $Id$
#------------------------------------------------------------
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::Label::StatementFull;
use RPS::Payor::Payor;
use RPS::LabelPayee::LabelPayee;
use RPS::LabelRoyalty::LabelRoyaltyRun;

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

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

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

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

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

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

# We are going to keep track of every page we create, so that we
# can go back later and add page numbers
#
my @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{labelStatementID}, $options{outputFile} );

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

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

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

    # Instantiate the ArtistStatement object.
    # This object will contain all the info we need to output the statement document.
    #
    my $statement = RPS::Statement::Label::StatementFull->new( labelRoyaltyStatementID => $labelStatementID );

    # If an output file path was not specified, create one out of the statement id.
    #
    if ( !$outFilePath ) {
        $outFilePath = "label_statement_$labelStatementID.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 $logoName = $options{clientID} . '/' . $payor->PayorID();
        ( $pdf, $page ) = EmbedImage::labelLogo( $pdf, $page, $logoName, 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::LabelRoyalty::LabelRoyaltyRun->new( labelRoyaltyRunID => $statement->LabelRoyaltyRunID(), 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 'Label Royalty Statement' at the top of the page.
    #
    $y = printTextCentered( $text, $y, 'Label 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->LabelPayee();

    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, $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::LabelRoyalty::LabelRoyaltyRun->new( labelRoyaltyRunID => $statement->LabelRoyaltyRunID(), 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;

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

            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   => 4,

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

    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,
                min_w     => 1.25 / in,
                max_w     => 1.25 / in,

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

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

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

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

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

    $y -= 2;

    my @amountDueArray;
    my @amountDueProperties;

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

	my ($amountDueDisplay, $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, {
        lines => [ {
                # pen_size => 1,
                # color => 'black',
                start_col  => 0,
                end_col    => 2,
                top_pad    => 5,
                bottom_pad => 0,
            },
        ],
    };
    my $bigFontSize = 10 / pt;

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

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

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

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

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

    my @labelRows;

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

    # The column headers
    #
    push @labelRows, [ 'Label', 'Service', 'Country', 'Distribution Rate', 'Gross Sales', 'Distribution Fee', 'Net Sales' ];
    push @rowProps, {
        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.
        lines  => [ {
                # pen_size => 1,
                # color => 'black',
                start_col => 0,
                end_col   => 6,
                top_pad   => 4,

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

    my $labelList = $statement->LabelStatementLabelList()->getList();

    # We need to print out some totals at the end,
    # so let's capture them here.
    my $grossSalesTotal;
    my $feeTotal;

    foreach my $label (@$labelList) {
        $grossSalesTotal += $label->GrossSales();
        $feeTotal        += $label->Fee();

        push @labelRows,
          [
            $label->Label->LabelName(),
            $label->ServiceName(),
            $label->CountryName(),
            formatPercent( $label->DistributionFee() ),
            formatMoney( $label->GrossSales() ),
            formatMoney( $label->Fee() ),
            formatMoney( $label->NetSales() ),
          ];

        push @rowProps, {};
    }

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

            # bottom_pad => 2,
        },
    ];

    # And now we want the grand totals.
    push @labelRows, [ 'Total:', '', '', '', formatMoney($grossSalesTotal), formatMoney($feeTotal), formatMoney( $statement->Total() ), ];

    push @rowProps, {};

    my $tableObj = TableObj->new(
        $pdf, $page, \@labelRows,
        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  => [ {                                         # label
                font      => $font->{Helvetica}{Bold},
                font_size => $fontSize,
                min_w     => 2.75 / in,
                max_w     => 2.75 / in,
                justify   => 'left',
            },
            {                                         # service
                font      => $font->{Helvetica}{Roman},
                font_size => $fontSize,
                justify   => 'left',
                min_w     => 1.25 / in,
                max_w     => 1.25 / in,
            },
            {                                         # country
                font      => $font->{Helvetica}{Roman},
                font_size => $fontSize,
                justify   => 'left',
                min_w     => 1.25 / in,
                max_w     => 1.25 / in,
            },
            {                                         # dist rate
                font      => $font->{Helvetica}{Roman},
                font_size => $fontSize,
                justify   => 'right',
                min_w     => 1.00 / in,
                max_w     => 1.00 / in,
            },
            {                                         # gross sales
                font      => $font->{Helvetica}{Roman},
                font_size => $fontSize,
                justify   => 'right',
                min_w     => 1.25 / in,
                max_w     => 1.25 / in,
            },
            {                                         # dist fee
                font      => $font->{Helvetica}{Roman},
                font_size => $fontSize,
                justify   => 'right',
                min_w     => 1.25 / in,
                max_w     => 1.25 / in,

                #pad => (1/4)/in,
            },
            {    # net sales
                font      => $font->{Helvetica}{Roman},
                font_size => $fontSize,
                justify   => 'right',
                min_w     => 1.25 / in,
                max_w     => 1.25 / in,
            },
        ],
        row_props => \@rowProps,
    );

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

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

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

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

#
# 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->{labelStatementID} = $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 <label_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-r <label_statement_id>\tThe id of the label 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";
    }
}

