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

use Spreadsheet::WriteExcel;
require Spreadsheet::WriteExcel::Big;

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

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

use lib '/app/tools/rps/lib';
use RPS::Statement::Mechanical::StatementFull;
use RPS::Statement::Mechanical::StatementLicenseTransactionList;
use RPS::Payor::Payor;
use RPS::Publisher::Publisher;
use RPS::Mechanical::MechanicalRun;
use RPS::DB::Item::Label;
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::ProductType;
use RPS::DB::Item::TrackLicense;
use RPS::DB::Item::MechanicalStatement;

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;


# We'll store formatting info here, once we have a worbook object to work with.
#
my $formats;
								

# Parse the command-line, then create the PDF!
#
my %options;
parseCommandLine(\%options);

createJumboStatementExcel($options{clientID}, $options{mechanicalRunID}, $options{outFilePath});


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


sub createJumboStatementExcel
{
    my ($clientID, $mechanicalRunID, $outFilePath) = @_;
    
    print STDERR "starting process to create excel file \n";
    print STDERR "clientID: ".$clientID." \n";
		print STDERR "mechanicalRunID: ".$mechanicalRunID." \n";
		print STDERR "outFilePath: ".$outFilePath." \n";
    
    # Instantiate the application singleton object.
    #
    my $appSingleton = Common::RSApp->new(clientID => $clientID);
    
    my $outFileName = "mechanical_run_$mechanicalRunID.xls";    

    # If an output file path was not specified, we'll just create the file right here.
    #
    if (! $outFilePath)
    {
        $outFilePath = $outFileName;
    }
    else 
    {
		    # Create the output path, if it isn't there already.
		    #
		    if ('/' ne substr($outFilePath, -1, 1))
		    {
		        $outFilePath .= '/';
		    }
		    if (! -d $outFilePath)
		    {
		        mkpath($outFilePath) or die "ERROR: Unable to create path $outFilePath: $!\n";
		    }
		    # Now that the directory is there, let's add the file name so that we have the complete path.
		    #
		    $outFilePath .=  $outFileName;	
    }
    
    print STDERR "creating excel file here: ".$outFilePath." \n";
    

    # Instantiate the Excel object.
    #
    my $workbook = Spreadsheet::WriteExcel::Big->new($outFilePath);


		print STDERR "workbook created \n";

		# Set up the special formats.   
		#		
		($workbook) = addFormats($workbook);
		
    
    # Add the first worksheet.
    #
    my $row;
    my $worksheet;
		($row, $workbook, $worksheet) = addWorksheet($row, $workbook, $worksheet);    


		# Grab all statements in run.
		#      
            		
    my $statementList = RPS::DB::Item::MechanicalStatement->Get
    (
        runID => $mechanicalRunID,
        sortByPublisherName => 1,
    );   
    
    print STDERR "fetched statements \n";
    

		# Iterate through all statements in run.
		#
    while ($statementList->hasNext())
    {
      my $statement = $statementList->next();
      my $statementID = $statement->mechanical_statement_id;	
      
      print STDERR "processing statement: ".$statementID." \n";	

			my $fullStatement = RPS::Statement::Mechanical::StatementFull->new(mechanicalStatementID => $statementID, focusPublisherID => 'all');
	
	    # Determine if this is an admin (or agent) or regular publisher. 
	    #
	    if ($fullStatement->Publisher()->IsAdmin() == 1
	     || $fullStatement->Publisher()->IsAgency() == 1)
	    {

	    	# Loop through all publishers for admins (and agents).
	    	#
	    	my $publisherList = $fullStatement->MechanicalStatementPublisherList()->getList();
				foreach my $publisher (@$publisherList)
				{
					($row, $workbook, $worksheet) = addJumboDetails($row, $publisher, $workbook, $worksheet, $formats);	
				}
	    } 
	    else 
	    {
	 			
	    	# Not an admin (or agent), so just do this once.
	    	($row, $workbook, $worksheet) = addJumboDetails($row, $fullStatement, $workbook, $worksheet, $formats);
	    }
		}

    # All done, save and clean up.
    #
    $workbook->close() or die "Error closing file: $!";
    
    print STDERR "done! \n";
    
}

sub addJumboHeaders 
{
		my ($row, $worksheet) = @_;
		
		$worksheet->write($row, 0, 'Publisher');
		$worksheet->write($row, 1, 'Admin');
		$worksheet->write($row, 2, 'Agent');
		$worksheet->write($row, 3, 'Label');
		$worksheet->write($row, 4, 'Album');
		$worksheet->write($row, 5, 'Track');
		$worksheet->write($row, 6, 'Catalog #');
		$worksheet->write($row, 7, 'UPC');
		$worksheet->write($row, 8, 'ISRC');
		$worksheet->write($row, 9, 'Config');
		$worksheet->write($row, 10, 'Region');
		$worksheet->write($row, 11, 'Rate Period');
		$worksheet->write($row, 12, 'Base Rate');
		$worksheet->write($row, 13, '% of Rate');
		$worksheet->write($row, 14, 'Share');
		$worksheet->write($row, 15, 'Net Rate');
		$worksheet->write($row, 16, '% of Sales');
		$worksheet->write($row, 17, 'Packaging');
		$worksheet->write($row, 18, 'Free Goods');
		$worksheet->write($row, 19, 'Misc.');
		$worksheet->write($row, 20, 'Sales');
		$worksheet->write($row, 21, 'Reserves');
		$worksheet->write($row, 22, 'Liquidations');
		$worksheet->write($row, 23, 'Returns');
		$worksheet->write($row, 24, 'Carryover');
		$worksheet->write($row, 25, 'Gross Units');
		$worksheet->write($row, 26, 'Total');
		$worksheet->write($row, 27, 'Accrual');
		$worksheet->write($row, 28, 'Net Units');
		$worksheet->write($row, 29, 'Amount Due');
		$worksheet->write($row, 30, 'License ID');
		$worksheet->write($row, 31, 'Previous Balance');
		$worksheet->write($row, 32, 'Adjustments');
		$worksheet->write($row, 33, 'Advances');
		$worksheet->write($row, 34, 'License Balance');	
		
		return ($row, $worksheet);
}	


sub addJumboDetails
{
    my ($row, $statement, $workbook, $worksheet, $formats) = @_;

    # Add the track details.
    #
    my $trackList = $statement->MechanicalStatementTrackList()->getList();
    foreach my $track (@$trackList)
    {
    	# Look up the Label name.
    	#
    	my $trackID = $track->TrackID();
    	my $labelName = RPS::DB::Item::Label->GetLabelNameFromTrackID($trackID);

	    # Go through the entire license list.
	    #
	    my $licenseList = $track->MechanicalStatementLicenseList()->getList();
	    foreach my $license (@$licenseList)
	    {

				#print STDERR "processing license: ".$license->TrackLicenseID()." \n";
			
    		# Figure out what we're going to use for License Balance.
    		#
		    my $licenseBalance;
		    if ($license->AdjustedSubtotal != 0) 
		    {
		    	$licenseBalance = $license->AdjustedSubtotal;
		    }
		    else 
		    {
		    	$licenseBalance = $license->AdvanceBalance;
		    }		    	

				# Add up the transactions now for use later.
				#
	      my $xmlObj = $license->MechanicalStatementLicenseTransactionList();
	    	my $licenseTransactionList = $xmlObj->getList();
	    	my $licenseTransactionCount = scalar(@$licenseTransactionList);						
				my $adjustments = 0;
				my $advances = 0;
				if ($licenseTransactionCount != 0)
				{
		    	foreach my $licenseTransaction (@$licenseTransactionList)
					{
		
		  			if ($licenseTransaction->TypeCode == 3) 
		  			{
		  				# Advances
		  				$advances += $licenseTransaction->Amount();
		  			} 
		  			else
		  			{
		  				# Adjustments
		  				$adjustments += $licenseTransaction->Amount();
		  			}  		    	  
					
					}
						
				}		    	
	    	

				# One day we'll capture everything we need during the royalty run.
				# But until that day, we have to hit the live license for some things.
				#
	    	my $trackLicense = RPS::DB::Item::TrackLicense->Lookup(track_license_id => $license->TrackLicenseID());


	    	my $incomeItemList = $license->MechanicalStatementItemList()->getList();
	    	my $incomeItemListLength = scalar(@$incomeItemList);	    	
	    	
	    	if ($incomeItemListLength != 0)
	    	{
	    	
		    	foreach my $incomeItem (@$incomeItemList)
		    	{    	  		
	
						($row, $workbook, $worksheet) = incrementRow($row, $workbook, $worksheet);	
						
						$worksheet->write_string($row, 0, $statement->Publisher()->PublisherName());
						if ($statement->Publisher()->AdminID())
						{
							$worksheet->write_string($row, 1, $statement->Publisher()->AdminName());
						}
						if ($statement->Publisher()->AgentID())
						{
							$worksheet->write_string($row, 2, $statement->Publisher()->AgentName());
						}
						$worksheet->write_string($row, 3, $labelName);
						$worksheet->write_string($row, 4, $track->AlbumName());				
						$worksheet->write_string($row, 5, $track->SongTitle());
						$worksheet->write_string($row, 6, $track->CatalogNumber() , $formats->{left});										
						$worksheet->write_string($row, 7, $incomeItem->UPC());
						$worksheet->write_string($row, 8, $track->ISRC());										
						$worksheet->write_string($row, 9, productCodeToName($incomeItem->ProductTypeID()));
						$worksheet->write_string($row, 10, regionIDToName($license->RegionID()));
						$worksheet->write_string($row, 11, $incomeItem->RatePeriod());
						$worksheet->write($row, 12, Common::Client::Current()->Locale()->currencyFormat()->symbol().formatNumber($incomeItem->BaseRate()) , $formats->{right});
						$worksheet->write($row, 13, formatPercent($incomeItem->TrackLicense->RatePercentage()) , $formats->{right});				
						$worksheet->write($row, 14, formatPercent($incomeItem->TrackLicense->Share()) , $formats->{right});						
						$worksheet->write($row, 15, Common::Client::Current()->Locale()->currencyFormat()->symbol().formatNumber($incomeItem->NetRate()) , $formats->{right});
						
						# Pulling these values from the live license.
						#
						$worksheet->write($row, 16, formatPercent($trackLicense->percentage_of_sales) , $formats->{right});
						$worksheet->write($row, 17, formatPercent($trackLicense->packaging_deduction) , $formats->{right});
						$worksheet->write($row, 18, formatPercent($trackLicense->free_goods) ,$formats->{right});
						$worksheet->write($row, 19, formatPercent($trackLicense->misc_deduction) , $formats->{right});
						
						$worksheet->write($row, 20, formatNumber($incomeItem->Sales()) , $formats->{right});
						$worksheet->write($row, 21, formatNumber($incomeItem->Reserved()) , $formats->{right});
						$worksheet->write($row, 22, formatNumber($incomeItem->Liquidated()) , $formats->{right});
						$worksheet->write($row, 23, formatNumber($incomeItem->Returns()) , $formats->{right});
						$worksheet->write($row, 24, formatNumber($incomeItem->PreviousCarryover()) , $formats->{right});
						$worksheet->write($row, 25, formatNumber($incomeItem->GrossUnits()) , $formats->{right});
						$worksheet->write($row, 26, formatMoney($incomeItem->Total()) , $formats->{right});					
						$worksheet->write($row, 27, formatNumber($incomeItem->Carryover()) , $formats->{right});					
						$worksheet->write($row, 28, formatNumber($incomeItem->NetUnits()) , $formats->{right});
						$worksheet->write($row, 29, formatMoney($incomeItem->AmountPaid()), $formats->{right});
						$worksheet->write_string($row, 30, $license->TrackLicenseID() , $formats->{left});
						$worksheet->write($row, 31, formatMoney($license->PreviousAdvanceBalance()) , $formats->{right});
						$worksheet->write($row, 32, formatMoney($adjustments) , $formats->{right});
						$worksheet->write($row, 33, formatMoney($advances) , $formats->{right});
						$worksheet->write($row, 34, formatMoney($licenseBalance) , $formats->{right});
						
		      }
		      
		    }
	
		    else 
		    {
		    	
	    		# Even if there are no income items, we still want to display the track info.
	    		#
	    		
	    		($row, $workbook, $worksheet) = incrementRow($row, $workbook, $worksheet);
	    		
					$worksheet->write_string($row, 0, $statement->Publisher()->PublisherName());
					if ($statement->Publisher()->AdminID())
					{
						$worksheet->write_string($row, 1, $statement->Publisher()->AdminName());
					}
					if ($statement->Publisher()->AgentID())
					{
						$worksheet->write_string($row, 2, $statement->Publisher()->AgentName());
					}
					$worksheet->write_string($row, 3, $labelName);
					$worksheet->write_string($row, 4, $track->AlbumName());				
					$worksheet->write_string($row, 5, $track->SongTitle());
					$worksheet->write_string($row, 6, $track->CatalogNumber() , $formats->{left});										
					$worksheet->write_string($row, 8, $track->ISRC());										
					$worksheet->write_string($row, 10, regionIDToName($license->RegionID()));
					$worksheet->write_string($row, 30, $license->TrackLicenseID() , $formats->{left});
					$worksheet->write($row, 31, formatMoney($license->PreviousAdvanceBalance()) , $formats->{right});
					$worksheet->write($row, 32, formatMoney($adjustments) , $formats->{right});
					$worksheet->write($row, 33, formatMoney($advances) , $formats->{right});
					$worksheet->write($row, 34, formatMoney($licenseBalance) , $formats->{right});				
		    } 
								       
	    }

    }

    return ($row, $workbook, $worksheet);
}





sub addWorksheet 
{
		my ($row, $workbook, $worksheet) = @_;
		
    $worksheet = $workbook->add_worksheet();
    
    print STDERR "worksheet added \n";
    
    # Set up the row variable.
    #
    $row = 0;
    
    # Add the column headers 
		#
		($row, $worksheet) = addJumboHeaders($row, $worksheet);
    
    # Not needed at the moment...
    # Specify widths for some columns.
    #
    
    # Amount column for summary table
		#$worksheet->set_column(3, 3, 12);
		
		# UPC column for detail table
		#$worksheet->set_column(8, 8, 12);
		
		# Region column for detail table
		#$worksheet->set_column(10, 10, 12);		
		
		# Amount Due column for detail table
		#$worksheet->set_column(15, 15, 12);				
    
    return ($row, $workbook, $worksheet);	
}

sub addFormats
{
		my ($workbook) = @_;
			
		my $formatB = $workbook->add_format();
		$formatB->set_bold();   
		
		my $formatRB = $workbook->add_format();
		$formatRB->set_bold();
		$formatRB->set_align('right');		 

		my $formatR = $workbook->add_format();
		$formatR->set_align('right');	
		
		my $formatL = $workbook->add_format();
		$formatL->set_align('left');			
		
		my $formatUB = $workbook->add_format();
		$formatUB->set_bold();
		$formatUB->set_bottom(1);		
		
		my $formatUR = $workbook->add_format();
		$formatUR->set_align('right');
		$formatUR->set_bottom(1);
		
		my $formatU = $workbook->add_format();
		$formatU->set_bottom(1);	
		
		$formats = 
    {
        bold 							=> $formatB,
        right_bold 				=> $formatRB,
        right 						=> $formatR,
        left 							=> $formatL,
        underline_bold 		=> $formatUB,
        right_underline 	=> $formatUR,
        underline 				=> $formatU,
    };	
    
    return ($workbook);   	
}

sub incrementRow 
{
    my ($row, $workbook, $worksheet) = @_;	
 		
 		   
		# Have we reached the max row for excel?
		#
		if($row >= 65534)
		{
			# Add a new worksheet
			#
			($row, $workbook, $worksheet) = addWorksheet($row, $workbook, $worksheet);
		}	
		
		$row++;
		
		return ($row, $workbook, $worksheet);
}


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

# 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('r:p:c:V:', \%opt);

    if (! $opt{r} || ! $opt{c})
    {
        usage();
        exit(1);
    }
    $settings->{mechanicalRunID} = $opt{r};
    $settings->{clientID} = $opt{c};
    $settings->{outFilePath} = $opt{p};

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


sub usage
{
    print STDERR "\nusage: $0 -c <client_id> -r <mechanical_run_id> [-p <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 <mechanical_run_id>\tThe id of the mechanical run to convert to excel\n";       
    print STDERR "\t-p <output file>\tThe output file path. 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";
    }
}


