#!/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;
}

