#!/usr/bin/perl
use strict;


use Data::Dumper;
use Getopt::Std;
use POSIX qw(ceil);
use POSIX ":sys_wait_h";
use Sys::Hostname;
use Carp;


use lib '/app/tools/common/lib';
use Common::DB::Item;
use Common::Assert;
use Common::TextProgressBar;
use Common::Timer;
use Common::DB::Item::Service;

#use lib '/app/tools/data_classes/lib';
#use File::Sale;
use lib '/app/tools/raptor/lib';
#use Raptor::Sale::Sale;
use Raptor::DB::Item::Sale;
use Raptor::DB::Item::File;

use lib '/app/tools/rps/lib';
use RPS::DB::Item::MechanicalCarryover;
use RPS::DB::Item::TrackLicense;
use RPS::DB::Item::Track;
use RPS::DB::Item::Master;
#use RPS::DB::Item::License;
use RPS::DB::Item::LicenseReserve;
use RPS::DB::Item::LicenseReserveLiquidation;
use RPS::DB::Item::StatRate;
use RPS::DB::Item::ProductTrack;
use RPS::DB::Item::Song;

use RPS::DB::Item::Publisher;
use RPS::DB::Item::Product;
use RPS::DB::Item::ControlledComposition;

use RPS::DB::Item::AlbumContract;
use RPS::DB::Item::MechanicalRun;
use RPS::DB::Item::MechanicalStatement;
use RPS::DB::Item::MechanicalStatementItem;
use RPS::DB::Item::MechanicalStatementItem;
use RPS::DB::Item::MechanicalStatementTrack;
use RPS::DB::Item::MechanicalStatementLicense;
use RPS::DB::Item::MechanicalStatementAdjustmentItem;
use RPS::DB::Item::SaleMechanicalStatementItemMap;

use RPS::License::TrackLicense;
use RPS::License::TrackLicenseList;

use RPS::Statement::MechanicalStatementDisplay;
use RPS::XMLCache;


my $gVerbosityLevel = 1;

# Parse the command-line options.
#
my %options;
_parseCommandLine(\%options);


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


# Read lines from STDIN.
#
while (my $line = <STDIN>)
{
    my ($upc, $firstReserve, $secondReserve, $returns) = _parseLine($line);

#    print "$upc $firstReserve $secondReserve\n";

    
	# Get the physical product associated with this upc
	#
    my $products = RPS::DB::Item::Product->GetByUPC($upc);
#    print "upc: $upc  numProducts: " . $products->size() . "\n";

	while (my $product = $products->next())
	{
		next if (RPS::DB::Item::Product::kProductTypeDigital == $product->product_type_id);
		next if (RPS::DB::Item::Product::kProductTypeDigitalTrack == $product->product_type_id);
#		print Dumper($product) . "\n";

        my $productID = $product->product_id;

		# Now, find the track_license_id(s) associated with this product.
        # Basically, we need to get all the non bonus track ids, then get 
        # the licenses.
		#
        my @trackLiceneIDs;
        my $albumTracks = RPS::DB::Item::Track->GetByAlbumID($product->album_id);
        while (my $albumTrack = $albumTracks->next())
        {
            next if (RPS::DB::Item::ProductTrack->TrackIsBonusTrack($albumTrack->track_id));

            my $track = RPS::DB::Item::Track->Lookup(track_id => $albumTrack->track_id);
            my $trackLicenses = RPS::DB::Item::TrackLicense->GetLicensesByTrackID($albumTrack->track_id);
#            print "  " . $track->title . "   # of licenses: " . $trackLicenses->size . "\n";
            while (my $trackLicense = $trackLicenses->next())
            {
                # Don't create reserves for licenses with no issue date.
                #
                if (! $trackLicense->date_issued || '0000-00-00' eq $trackLicense->date_issued)
                {
                    next;
                }
                if (RPS::DB::Item::TrackLicense::kPublicDomain == $trackLicense->type())
                {
#                    print "    <PUBLIC DOMAIN>\n";
                    next;
                }

                # Populate the carryover table.
                #
                if ($returns < 0)
                {
                    my $saleDate = '2005-12-31';
                    my $releaseDate = $product->release_date;
                    $releaseDate = $saleDate unless $releaseDate;
                    my $issueStatRateID  = _getStatRateID($releaseDate);
                    my $saleStatRateID = _getStatRateID($saleDate);
                    my $co = RPS::DB::Item::MechanicalCarryover->Create(
                     track_license_id       => $trackLicense->track_license_id,
                     sale_stat_rate_id      => $saleStatRateID,
                     issue_stat_rate_id     => $issueStatRateID,
                     product_id             => $productID,
                     units                  => $returns,
                    );
                    $co->save();

                }


                my $publisher = RPS::DB::Item::Publisher->Lookup(publisher_id => $trackLicense->publisher_id);
                if (! $publisher)
                {
                    print "    --- invalid track license: publisher_id " . $trackLicense->publisher_id . " not in system\n";
                }
                else
                {
                    if (! $trackLicense->reserve_percentage)
                    {
#                    print "    publisher: " . $publisher->publisher_name . "    reserve_percentage: " . $trackLicense->reserve_percentage . "\n";
                    }
                    else
                    {
                        # Finally, a valid, normal track license.
                        #
                        # So, now I need to determine the stat rate ids.
                        # Sale will be easy.
                        # Issue... get that from the product if we can.
                        # Otherwise just use the 'sale date'
                        #
                        my $releaseDate = $product->release_date;
                        if ($firstReserve)
                        {
                            _createReserve($firstReserve, $product, $trackLicense, '2005-06-30', 2);
                        }

                        if ($secondReserve)
                        {
                            _createReserve($secondReserve, $product, $trackLicense, '2005-12-31', 1);
                        }
                    }
                }
            }
        }
	}
}


sub _createReserve
{
    my ($units, $product, $trackLicense, $saleDate, $nextPeriod) = @_;

    my $releaseDate = $product->release_date;
    $releaseDate = $saleDate unless $releaseDate;
    my $issueStatRateID  = _getStatRateID($releaseDate);
    my $saleStatRateID = _getStatRateID($saleDate);

    my $track = RPS::DB::Item::Track->Lookup(track_id => $trackLicense->track_id);
    my $master = RPS::DB::Item::Master->Lookup(master_id => $track->master_id);
    my $duration = $master->duration;

    # Calculate the effective rate
    #
    my ($effectiveRate, $baseRate, $appliedStatRateID) = _calculateEffectiveRate($issueStatRateID, $saleStatRateID, 
     $trackLicense->rate_basis, $trackLicense->rate_type, $trackLicense->penny_rate, $duration, $trackLicense->share);
                    
    my $newReserve = RPS::DB::Item::LicenseReserve->Create
    (
        track_license_id => $trackLicense->track_license_id,
        original_statement_item_id => 0,
        product_id => $product->product_id,
        sale_stat_rate_id => $saleStatRateID,
        issue_stat_rate_id => $issueStatRateID,
        initial_units => $units,
        remaining_units => $units,
        effective_rate => $effectiveRate,
        next_period => $nextPeriod,
    );
    $newReserve->save();
}


sub _parseLine
{
    my ($line) = @_;

    chomp $line;
	print "\n$line\n";
    my @bits = split("\t", $line);

    # strip the '-' from upc.
    #
    my @upcBits = split('-', $bits[3]);
    $bits[3] = join('', @upcBits);
    return ($bits[3],$bits[4], $bits[5], $bits[6]);
}


# Pass dates in 'yyyy-mm-dd' format.
#
sub _parseCommandLine
{
    my ($settings) = @_;

    my %opt;
    getopts('s:e:c:V:n', \%opt);

    if (! $opt{c})
    {
        _usage();
        exit(1);
    }
    $settings->{clientID} = $opt{c};

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


sub _usage
{
    print "\nusage: $0 -c client_id [-V]\n";
    print "\n";
    print "Arguments:\n";
    print "\t-c <client_id>\t\tThe client_id of the client to process\n";
    print "\t-V <N>\t\t\tVerbosity level - 0 means no output\n";
}


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

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


# It's dumb to hit the database for these stat rate tables.
# Better to read the whole table into memory first, then 
# read from ram.

my $gStatRateTable;
my $gStatRateByDate;
sub _getStatRateTable
{
    if (! $gStatRateTable)
    {
        $gStatRateTable = {};

        my $statRates = RPS::DB::Item::StatRate->GetAll();
        while (my $statRate = $statRates->next())
        {
            $gStatRateTable->{$statRate->stat_rate_id} = $statRate;
        }
    }

    return $gStatRateTable;
}


# Generally we are going to be asking for the same stat rate for the same
# date over and over and over and over... so it makes sense to cache it.
#
sub _getStatRateID
{
    my $endDate = shift;

    if (! $gStatRateByDate->{$endDate})
    {
        my $table = _getStatRateTable();

        # get the stat rates, sorted by date
        #
        my @sortedRateIDs = sort { $table->{$b}->date_effective <=> $table->{$a}->date_effective} keys %$table;
        foreach my $testRateID (@sortedRateIDs)
        {
            if ($table->{$testRateID}->date_effective <= $endDate)
            {
                $gStatRateByDate->{$endDate} = $table->{$testRateID}->stat_rate_id;
                last;
            }
        }
    }
    return $gStatRateByDate->{$endDate};
}


sub _calculateEffectiveRate
{
    my ($issueStatRateID, $saleStatRateID, $rateBasis, $rateType, $pennyRate, $duration, $share) = @_;


    # Calculate the effective rate per unit to pay.
    # rate basis - either sale or release
    # rate term - full, partial, or penny
    #
    my $appliedStatRateID;
    my ($baseStatRate, $minuteRate);
    my $effectiveRate;
    if (RPS::DB::Item::TrackLicense::kRateBasisSale == $rateBasis)
    {
        ($baseStatRate, $minuteRate)= _getStatRate($saleStatRateID);
        $appliedStatRateID = $saleStatRateID;
    }
    elsif (RPS::DB::Item::TrackLicense::kRateBasisIssue == $rateBasis)
    {
        ($baseStatRate, $minuteRate)= _getStatRate($issueStatRateID);
        $appliedStatRateID = $issueStatRateID;
    }
    else
    {
        croak "!!! Unknown rate basis: " . $rateBasis;
    }
                    
    if (RPS::DB::Item::TrackLicense::kRateTypeFull == $rateType)
    {
        # Use the base stat rate for songs of 5 minutes or less.
        # Use the minute rate for longer songs.
        #
        $effectiveRate = $baseStatRate;
        if ($duration > 300)
        {
            my $minutes = ceil($duration / 60);
            my $minBasedRate = ($minutes * $minuteRate);

            $effectiveRate = ($minBasedRate > $effectiveRate ? $minBasedRate : $effectiveRate);
        }
    }
    elsif (RPS::DB::Item::TrackLicense::kRateTypeMinimum == $rateType)
    {
        $effectiveRate = $baseStatRate;
    }
    elsif (RPS::DB::Item::TrackLicense::kRateTypePenny == $rateType)
    {
        $effectiveRate = $pennyRate;
    }
    else
    {
        croak "!!! Unknown rate type: " . $rateType;
    }


    # Now that we have the effective rate, cut it according to the share percentage.
    #
    my $baseRate = $effectiveRate;
    $effectiveRate = $effectiveRate * ($share / 100);

    return ($effectiveRate, $baseRate, $appliedStatRateID);
}


sub _getStatRate
{
    my $statRateID = shift;
    
    my $table = _getStatRateTable();
    my $statRate = $table->{$statRateID};
    assert($statRate);

    return ($statRate->rate, $statRate->minute_rate);
}

sub _getControlledCompIDFromAlbumID
{
    my ($albumID) = @_;

    my $albumContract = RPS::DB::Item::AlbumContract::GetCComp($albumID);
    my $cc = RPS::DB::Item::ControlledComposition->GetByContractID($albumContract->artist_contract_id);
    return $cc->controlled_composition_id;
}

