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

#use warnings;

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

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

use lib '/app/tools/rps/lib';
use RPS::Statement::Mechanical::US::StatementFull;
use RPS::Statement::Mechanical::US::StatementLicenseTransactionList;
use RPS::Payor::Payor;
use RPS::Publisher::US::Publisher;
use RPS::Mechanical::US::MechanicalRun;
use RPS::DB::Item::ContractRateType;
use RPS::DB::Item::IncomeSource;
use RPS::DB::Item::Region;
use RPS::DB::Item::Channel;
use RPS::DB::Item::PriceLevel;
use RPS::DB::Item::Product;
use RPS::DB::Item::ProductType;
use RPS::DB::Item::TrackLicense;

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

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

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

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

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

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

    # Instantiate the MechanicalStatement object.
    # This object will contain all the info we need to output the statement document.
    #
    my $statement = RPS::Statement::Mechanical::US::StatementFull->new(
        mechanicalStatementID => $mechanicalStatementID,
        focusPublisherID      => 'all'
    );

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

    # Open file for writing
    open( my $outFile, ">" . $outFilePath );
    binmode( $outFile, ':utf8' );

    # Write out column headings.
    my @headings = (
        'PUB NAME',
        'LICENSE ID',
        'ALBUM TITLE',
        'CATALOG #',
        'UPC',
        'SONG TITLE',
        'ISRC',
        'PRODUCT CONFIG',
        'REGION',
        'RATE PERIOD',
        'SHARE',
        'NET RATE (USD)',
        'NET UNITS',
        'BALANCE (USD)',
        'TRACK SUBTOTAL (USD)',
        'PREVIOUS LICENSE BALANCE (USD)',
        'LICENSE ADVANCE (USD)',
        'LICENSE ADJUSTMENT (USD)',
        'LICENSE BALANCE (USD)'
    );
    my $header = join( "\t", @headings );
    print $outFile "$header\n";

    # Determine if this is an admin or regular publisher.
    if ( $statement->Publisher()->IsAdmin() == 1 || $statement->Publisher()->IsAgency() == 1 ) {

        # Loop through all publishers for admins.
        my $publisherList = $statement->MechanicalStatementPublisherList()->getList();
        foreach my $publisher (@$publisherList) {
            details( $publisher, $outFile );
        }
    } else {

        # Not an admin, so just do this once.
        details( $statement, $outFile );
    }

    # All done, close it up.
    #
    close $outFile;
}

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

    my $trackList = $statement->MechanicalStatementTrackList()->getList();
    foreach my $track (@$trackList) {

        # Go through the crossed license list
        #
        my $crossedLicenseList = $track->MechanicalStatementCrossedLicenseList()->getList();

        if ( @$crossedLicenseList > 0 ) {

            my $balanceTotal = 0;

            # Add the transactions up into two buckets, adjustments and advances.
            #
            my $advanceTotal    = 0;
            my $adjustmentTotal = 0;

            foreach my $license (@$crossedLicenseList) {
                my $trackLicenseData = RPS::DB::Item::TrackLicense->Lookup( track_license_id => $license->TrackLicenseID() );
                my $issuerLicenseID = $trackLicenseData->issuer_license_id;

                my $xmlObj                  = $license->MechanicalStatementLicenseTransactionList();
                my $licenseTransactionList  = $xmlObj->getList();
                my $licenseTransactionCount = scalar(@$licenseTransactionList);

                if ( $licenseTransactionCount != 0 ) {
                    foreach my $licenseTransaction (@$licenseTransactionList) {
                        if ( $licenseTransaction->TypeCode == 3 ) {
                            $advanceTotal += $licenseTransaction->Amount();
                        } else {
                            $adjustmentTotal += $licenseTransaction->Amount();
                        }
                    }
                }

                my $incomeItemList       = $license->MechanicalStatementItemList()->getList();
                my $incomeItemListLength = scalar(@$incomeItemList);

                # If there are income items, print them out now.
                #
                if ( $incomeItemListLength != 0 ) {

                    foreach my $incomeItem (@$incomeItemList) {
                        $balanceTotal += $incomeItem->AmountPaid();

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

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

                        my @itemRow = (
                            $statement->Publisher()->PublisherName(),                $issuerLicenseID,
                            $albumTitle,                                             $track->CatalogNumber(),
                            $incomeItem->UPC(),                                      $track->SongTitle(),
                            $track->ISRC(),                                          productCodeToName($productTypeID),
                            regionIDToName( $incomeItem->TrackLicense->RegionID() ), $incomeItem->RatePeriod(),
                            formatPercent( $incomeItem->TrackLicense->Share() ),     formatNumber( $incomeItem->NetRate() ),
                            formatNumber( $incomeItem->NetUnits() ),                 formatMoney( $incomeItem->AmountPaid() ),
                        );
                        my $joinedItemRow = join( "\t", @itemRow );
                        print $outFile "$joinedItemRow\n";
                    }
                }
            }

            my $xmlObj                  = $track->MechanicalStatementCrossedLicenseTransactionList();
            my $licenseTransactionList  = $xmlObj->getList();
            my $licenseTransactionCount = scalar(@$licenseTransactionList);

            if ( $licenseTransactionCount != 0 ) {
                foreach my $licenseTransaction (@$licenseTransactionList) {
                    if ( $licenseTransaction->TypeCode == 3 ) {
                        $advanceTotal += $licenseTransaction->Amount();
                    } else {
                        $adjustmentTotal += $licenseTransaction->Amount();
                    }
                }
            }

            my @subtotalRow = (
                $statement->Publisher()->PublisherName(),               '',
                ,                                                       $track->AlbumName(),
                $track->CatalogNumber(),                                '',
                $track->SongTitle(),                                    $track->ISRC(),
                '',                                                     '',
                '',                                                     '',
                '',                                                     '',
                '',                                                     formatMoney($balanceTotal),
                formatMoney( $track->CrossedPreviousAdvanceBalance() ), formatMoney($advanceTotal),
                formatMoney($adjustmentTotal),                          formatMoney( $track->CrossedAdjustedSubtotal() ),
            );
            my $joinedSubtotalRow = join( "\t", @subtotalRow );
            print $outFile "$joinedSubtotalRow\n";
        }

        # Go through the uncrossed license list
        #
        my $licenseList = $track->MechanicalStatementLicenseList()->getList();
        foreach my $license (@$licenseList) {
            my $trackLicenseData = RPS::DB::Item::TrackLicense->Lookup( track_license_id => $license->TrackLicenseID() );
            my $issuerLicenseID = $trackLicenseData->issuer_license_id;

            my $incomeItemList       = $license->MechanicalStatementItemList()->getList();
            my $incomeItemListLength = scalar(@$incomeItemList);

            # If there are income items, print them out now.
            #
            if ( $incomeItemListLength != 0 ) {

                # We have to fetch the original track license data, to get the issuer license id.
                #

                foreach my $incomeItem (@$incomeItemList) {

                    my $product = RPS::DB::Item::Product->Lookup( product_id => $incomeItem->ProductID() );
                    my $albumTitle = _getAlbumTitle( $product, $track->AlbumName() );

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

                    my @itemRow = (
                        $statement->Publisher()->PublisherName(),                $issuerLicenseID,
                        $albumTitle,                                             $track->CatalogNumber(),
                        $incomeItem->UPC(),                                      $track->SongTitle(),
                        $track->ISRC(),                                          productCodeToName($productTypeID),
                        regionIDToName( $incomeItem->TrackLicense->RegionID() ), $incomeItem->RatePeriod(),
                        formatPercent( $incomeItem->TrackLicense->Share() ),     formatNumber( $incomeItem->NetRate() ),
                        formatNumber( $incomeItem->NetUnits() ),                 formatMoney( $incomeItem->AmountPaid() ),
                    );
                    my $joinedItemRow = join( "\t", @itemRow );
                    print $outFile "$joinedItemRow\n";
                }
            }

            # Now we'll print out the subtotal line for this license (need to account for crossed licenses, too, but that will come later)
            #
            my $xmlObj                  = $license->MechanicalStatementLicenseTransactionList();
            my $licenseTransactionList  = $xmlObj->getList();
            my $licenseTransactionCount = scalar(@$licenseTransactionList);

            # Add the transactions up into two buckets, adjustments and advances.
            #
            my $advanceTotal    = 0;
            my $adjustmentTotal = 0;
            if ( $licenseTransactionCount != 0 ) {
                foreach my $licenseTransaction (@$licenseTransactionList) {
                    my $transactionType;
                    if ( $licenseTransaction->TypeCode == 3 ) {
                        $advanceTotal += $licenseTransaction->Amount();
                    } else {
                        $adjustmentTotal += $licenseTransaction->Amount();
                    }
                }
            }

            my @subtotalRow = (
                $statement->Publisher()->PublisherName(), $issuerLicenseID,
                $track->AlbumName(),                      $track->CatalogNumber(),
                '',                                       $track->SongTitle(),
                $track->ISRC(),                           '',
                '',                                       '',
                '',                                       '',
                '',                                       '',
                formatMoney( $license->Subtotal() ),      formatMoney( $license->PreviousAdvanceBalance() ),
                formatMoney($advanceTotal),               formatMoney($adjustmentTotal),
                formatMoney( $license->AdjustedSubtotal() ),
            );
            my $joinedSubtotalRow = join( "\t", @subtotalRow );
            print $outFile "$joinedSubtotalRow\n";
        }
    }
}

# For now, we are not realy going to be doing any formatting of numbers.
#

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 _checkForRingtone {
    my ($incomeItem) = @_;

    my $productTypeID;

    if ( $incomeItem->TrackLicense->ProductTypeID() == RPS::DB::Item::Product::kProductTypeRingtone ) {
        $productTypeID = RPS::DB::Item::Product::kProductTypeRingtone;
    } else {
        $productTypeID = $incomeItem->ProductTypeID();
    }

    return $productTypeID;
}

my %parentTitles;

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

    my $title;

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

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

    return $title;
}

# These functions map ids to their text representations.
# Basically, the first time we ask for a particular id->string mapping, we
# will query the correct database, and build a hash.  Subsequent calls just
# hit the hash.
#
# Because these tables are so similar, I abstracted the guts into the _genericMapAccessor
# method (to save myself some typing).
#
my %idMaps;

sub _genericMapAccessor {
    my ( $collectionAccessor, $idName, $id ) = @_;

    if ( !defined $idMaps{$collectionAccessor} ) {
        $idMaps{$collectionAccessor} = {};

        # This is a very naughty thing to do, but it works great in this context.
        #
        #        no strict 'refs';
        #        my $c = &$collectionAccessor();
        #        use strict 'refs';
        my $c = $collectionAccessor->GetAll();

        while ( my $item = $c->next() ) {
            if ( $idName ne 'product_type_id' ) {
                $idMaps{$collectionAccessor}{ $item->$idName() } = $item->name;
            } else {
                $idMaps{$collectionAccessor}{ $item->$idName() } =
                  $item->description;
            }
        }
    }

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

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

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

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

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

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

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

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

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

#
# 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->{mechanicalStatementID} = $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 <mechanical_statement_id> [-f <output file>]\n";
    print STDERR "\n";
    print STDERR "Arguments:\n";
    print STDERR "\t-c <client_id>\t\t\tThe client_id of the client to process\n";
    print STDERR "\t-s <mechanical_statement_id>\tThe id of the mechanical statement to convert to text\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";
    }
}

