#------------------------------------------------------------
# Copyright (C) 2009 RoyaltyShare, Inc.   All Rights Reserved
#------------------------------------------------------------

package RPS::Mechanical::US::Process::CreateStatement;

use strict;

use lib '/app/tools/common/lib';
use lib '/app/tools/rps/lib';
use lib '/app/tools/raptor/lib';

use POSIX qw(ceil);

use Common::Assert;
use Common::DB::Item::Client;
use Common::Log;
use Common::RSMath;
use Common::Util;
use Data::Dumper;
use Raptor::DB::Item::File;
use Raptor::DB::Item::Sale;
use RPS::DB::Item::Album;
use RPS::DB::Item::AlbumContract;
use RPS::DB::Item::Artist;
use RPS::DB::Item::BonusTrack;
use RPS::DB::Item::ControlledComposition;
use RPS::DB::Item::FinanceAccount;
use RPS::DB::Item::LicenseReserve;
use RPS::DB::Item::LicenseReserveRun;
use RPS::DB::Item::Master;
use RPS::DB::Item::MechanicalCarryover;
use RPS::DB::Item::MechanicalRun;
use RPS::DB::Item::MechanicalRunCarryover;
use RPS::DB::Item::MechanicalStatement;
use RPS::DB::Item::MechanicalStatementAdjustmentItem;
use RPS::DB::Item::MechanicalStatementCrossedLicenseTransaction;
use RPS::DB::Item::MechanicalStatementItem;
use RPS::DB::Item::MechanicalStatementLicense;
use RPS::DB::Item::MechanicalStatementLicenseTransaction;
use RPS::DB::Item::MechanicalStatementPublisher;
use RPS::DB::Item::MechanicalStatementPublisherTransaction;
use RPS::DB::Item::MechanicalStatementTrack;
use RPS::DB::Item::MechanicalStatementTransaction;
use RPS::DB::Item::PendingTransaction;
use RPS::DB::Item::Product;
use RPS::DB::Item::ProductTrack;
use RPS::DB::Item::Publisher;
use RPS::DB::Item::PublisherAccount;
use RPS::DB::Item::PublisherIndirectBalanceAccount;
use RPS::DB::Item::PublisherTrackCrossedLicenseAccount;
use RPS::DB::Item::RegionCountryMap;
use RPS::DB::Item::ReserveLiquidation;
use RPS::DB::Item::SalePublisherMap;
use RPS::DB::Item::SaleRunMap;
use RPS::DB::Item::Song;
use RPS::DB::Item::StatRate;
use RPS::DB::Item::Track;
use RPS::DB::Item::TrackLicense;
use RPS::Mechanical::US::MechanicalRun;
use RPS::Mechanical::Process::CreateStatement;
use RPS::Mechanical::Process::SaleException;

use RPS::Mechanical::Process::CreateStatement::Complex;
use base 'RPS::Mechanical::Process::CreateStatement::Complex';

sub _defaultIssueStatRateID { 
    
    my ($self, $licenseType) = @_;
    
    if ($licenseType == RPS::DB::Item::TrackLicense::kRingtone)
    {
        return 28;
    }
    else
    {
        return 16; 
    }
}

sub _getAllPossibleLicenses
{
    my ($self) = @_;

    return RPS::DB::Item::TrackLicense->GetLicensesForPublisherStatement($self->_publisherID(), $self->_payorID());
}



sub _currencyCode
{
    my ($self) = @_;
    return 'USD';
}

sub _getStatRates
{
    my ($self) = @_;
    return RPS::DB::Item::StatRate->GetRatesUS();
}

sub _createLicenseReserve
{
    my ($self, $statementItem, $units, $periodsRemaining) = @_;

    my $newReserve = RPS::DB::Item::LicenseReserve->Create
    (
        track_license_id => $statementItem->track_license_id,
        original_statement_item_id => $statementItem->mechanical_statement_item_id,
        product_id => $statementItem->product_id,
        sale_stat_rate_id => $statementItem->sale_stat_rate_id,
        issue_stat_rate_id => $statementItem->issue_stat_rate_id,
        units => $units,
        effective_rate => $statementItem->net_rate,
        periods_remaining => $periodsRemaining,
    );
    return $newReserve;
}

sub _getLicensesWithPendingTransactions
{
    my ($self) = @_;
    return RPS::DB::Item::TrackLicense->GetLicensesWithPendingTransactionsForMechanicalStatement($self->_publisherID(), $self->_payorID());
}

sub _reserveType
{
    my ($self) = @_;
    return RPS::DB::Item::ReserveLiquidation::kTypePublishingLicense;
}

sub _ringtoneStatRateType
{
    my ($self) = @_;
    return RPS::DB::Item::StatRate::kUSRingStatRateType;
}

sub _lookupTrackLicense
{
    my ($self, $id) = @_;
    return RPS::DB::Item::TrackLicense->Lookup(track_license_id => $id);
}

sub _createStatementItem
{
    my ($self, $statementID, $trackLicenseID, $productID, $upc, $isrc, $saleStatRateID, $issueStatRateID) = @_;
    my $item = RPS::DB::Item::MechanicalStatementItem->Create
    (
        mechanical_statement_id => $self->{_statementID},
        track_license_id => $trackLicenseID,
        product_id => $productID,
        upc => $upc,
        isrc => $isrc,
        sale_stat_rate_id => $saleStatRateID,
        issue_stat_rate_id => $issueStatRateID,
    );
    return $item;
}

sub _createStatementLicense
{
    my ($self, $trackLicense) = @_;

    my $item = RPS::DB::Item::MechanicalStatementLicense->Create
    (
        mechanical_statement_id => $self->_statementID(),
        track_license_id => $trackLicense->track_license_id(),
        region_id => $trackLicense->region_id(),
    );
    return $item;
}

sub _getStatementLicenseItemsByStatementID
{
    my ($self, $statementID) = @_;
    return RPS::DB::Item::MechanicalStatementLicense->GetByMechanicalStatementID($statementID);
}

sub _getStatementTrackItemsByStatementID
{
    my ($self, $statementID) = @_;
    return RPS::DB::Item::MechanicalStatementTrack->GetByMechanicalStatementID($statementID);
}

sub _getFileLabel
{
    my ($self, $run) = @_;
    return RPS::Mechanical::US::MechanicalRun::GenerateFileLabel($run->label);
}

sub _lookupPublisher
{
    my ($self, $publisherID) = @_;
    return RPS::DB::Item::Publisher->Lookup(publisher_id => $publisherID);
}

sub _lookupPublisherAccount
{
    my ($self, $publisherID, $payorID) = @_;
    return RPS::DB::Item::PublisherAccount->Lookup(publisher_id => $publisherID, payor_id => $payorID);
}

sub _createPublisherAccount
{
    my ($self, $publisherID, $payorID) = @_;
    return RPS::DB::Item::PublisherAccount->Create
    (
        publisher_id => $publisherID,
        payor_id => $payorID,
    );
}

sub _lookupStatementPublisherAccount
{
    my ($self, $publisherID, $payorID, $indirectPublisherID) = @_;
    return RPS::DB::Item::PublisherIndirectBalanceAccount->Lookup(publisher_id => $publisherID, payor_id => $payorID, indirect_publisher_id => $indirectPublisherID);
}

sub _createStatementPublisherAccount
{
    my ($self, $publisherID, $payorID, $indirectPublisherID) = @_;
    return RPS::DB::Item::PublisherIndirectBalanceAccount->Create
    (
        publisher_id => $publisherID,
        payor_id => $payorID,
        indirect_publisher_id => $indirectPublisherID,
    );
}

sub _getStatementTransactionItemsByStatementID
{
    my ($self, $statementID) = @_;
    return RPS::DB::Item::MechanicalStatementTransaction->GetByMechanicalStatementID($statementID);
}

sub _getStatementPublisherTransactionItems
{
    my ($self, $statementID, $publisherID) = @_;
    
    return RPS::DB::Item::MechanicalStatementPublisherTransaction->GetByMechanicalStatementAndPublisherID($statementID, $publisherID);
}

sub _publisherIDFromStatementTrackItem
{
    my ($self, $trackItem) = @_;
    return $trackItem->publisher_id;
}

sub _publisherIDFromTrackLicense
{
    my ($self, $trackLicense) = @_;
    return $trackLicense->publisher_id;
}

sub _publisherIDFromStatementItem
{
    my ($self, $statement) = @_;
    return $statement->publisher_id();
}

sub _publisherIDFromStatementPublisher
{
    my ($self, $statementPublisher) = @_;
    return $statementPublisher->publisher_id();
}

sub _idFromTrackLicense
{
    my ($self, $trackLicense) = @_;
    return $trackLicense->track_license_id();
}

sub _idFromStatementItem
{
    my ($self, $statementItem) = @_;
    return $statementItem->mechanical_statement_item_id();
}

sub _idFromStatement
{
    my ($self, $statement) = @_;
    return $statement->mechanical_statement_id();
}

sub _publisherIDFromStatement
{
    my ($self, $statement) = @_;
    return $statement->publisher_id();
}

sub _idFromStatementLicense
{
    my ($self, $statementLicense) = @_;
    return $statementLicense->mechanical_statement_license_id();
}

sub _lookupPublisherTrackCrossedLicenseAccount
{
    my ($self, $publisherID, $trackID, $payorID) = @_;

    return RPS::DB::Item::PublisherTrackCrossedLicenseAccount->Lookup
    (
        publisher_id => $publisherID,
        track_id => $trackID,
        payor_id => $payorID,
    );
}

sub _createPublisherTrackCrossedLicenseAccount
{
    my ($self, $publisherID, $trackID, $payorID, $accountID) = @_;

    return RPS::DB::Item::PublisherTrackCrossedLicenseAccount->Create
    (
        publisher_id => $publisherID, 
        track_id => $trackID,
        payor_id => $payorID,
        finance_account_id => $accountID,
    );
}

sub _getStatementCrossedLicenseTransactions
{
    my ($self, $statementID, $publisherID, $trackID) = @_;

    return RPS::DB::Item::MechanicalStatementCrossedLicenseTransaction->GetByMechanicalStatementIDPublisherIDTrackID($statementID, $publisherID, $trackID);
}

sub _idFromStatementLicenseItem
{   
    my ($self, $licenseItem) = @_;
    return $licenseItem->mechanical_statement_license_id();
}

sub _trackLicenseIDFromStatementLicenseItem
{
    my ($self, $licenseItem) = @_;
    return $licenseItem->track_license_id();
}

sub _getStatementLicenseTransactionsByStatementLicenseID
{
    my ($self, $statementLicenseID) = @_;
    return RPS::DB::Item::MechanicalStatementLicenseTransaction->GetByMechanicalStatementLicenseID($statementLicenseID);
}

sub _createStatementLicenseTransaction
{
    my ($self, $licenseItemID) = @_;
    return RPS::DB::Item::MechanicalStatementLicenseTransaction->Create
    (
	    'mechanical_statement_license_id'   => $licenseItemID,
    );
}

sub _getAllCommittedLicenseReservesByTrackLicenseID
{
    my ($self, $trackLicenseID) = @_;
    return RPS::DB::Item::LicenseReserve->GetAllCommittedByTrackLicenseID($trackLicenseID);
}

sub _createLicenseReserveRunItem
{
    my ($self, $reserve) = @_;
    return RPS::DB::Item::LicenseReserveRun->Create
    (
        license_reserve_id => $reserve->license_reserve_id,
        mechanical_run_id => $self->_runID(),
    );
}

sub _getAllMechanicalCarryoverByTrackLicenseID
{
    my ($self, $trackLicenseID) = @_;
    return RPS::DB::Item::MechanicalCarryover->GetAllByTrackLicenseID($trackLicenseID);
}

sub _createMechanicalRunCarryover
{
    my ($self, $carryover) = @_;

    my $mechanicalRunCarryoverItem = RPS::DB::Item::MechanicalRunCarryover->Create
    (
        mechanical_carryover_id => $carryover->mechanical_carryover_id,
        mechanical_run_id => $self->_runID(),
    );
}

sub _getLicensesForPublisherStatementWithTrackAndRegion
{
    my ($self, $trackID, $regionID, $startDate, $endDate, $publisherID) = @_;
    return RPS::DB::Item::TrackLicense->GetLicensesForPublisherStatementWithTrackAndRegion($trackID, $regionID, $startDate, $endDate, $publisherID, $self->_payorID());
}

sub _getPaidSaleRunMapForLicense
{
    my ($self, $license) = @_;

    return RPS::DB::Item::SaleRunMap->GetPaidUSMechanicalsWithTrackLicense($license->track_license_id);
}

sub _lookupMechanicalStatementItem
{
    my ($self, $statementItemID) = @_;
    return RPS::DB::Item::MechanicalStatementItem->Lookup(mechanical_statement_item_id => $statementItemID);
}

sub _getLastStatementAdjustmentItem
{
    my ($self, $statementItemID) = @_;
    return RPS::DB::Item::MechanicalStatementAdjustmentItem->GetLastStatementAdjustment($statementItemID);
}

sub _idFromStatementAdjustmentItem
{
    my ($self, $lastAdjustment) = @_;
    return $lastAdjustment->mechanical_statement_adjustment_item_id;
}

sub _idFromRunItem
{
    my ($self, $run) = @_;
    return $run->mechanical_run_id;
}

sub _createStatementAdjustmentItem
{
    my ($self, $license, $originalStatementItemID, $lastAdjustmentID, $adjustmentAmount, $total) = @_;

    return RPS::DB::Item::MechanicalStatementAdjustmentItem->Create
    (
        mechanical_statement_id => $self->_statementID(),
        track_license_id => $license->track_license_id(),
        original_mechanical_statement_item_id => $originalStatementItemID,
        previous_mechanical_statement_adjustment_item_id => $lastAdjustmentID,
        amount => $adjustmentAmount,
        new_total => $total,
        adjustment_type_code => RPS::DB::Item::MechanicalStatementAdjustmentItem::kAdjustmentTypeCCompNewLicense, #!!! is this really accurate?
    );
}

sub _createStatementPublisher
{
    my ($self, $publisherID) = @_;
    
    return RPS::DB::Item::MechanicalStatementPublisher->Create
    (
        mechanical_statement_id => $self->_statementID(),
        publisher_id => $publisherID,
    );
}

sub _getStatementPublisherFromAccount
{
    my ($self, $account) = @_;
    my $publisherID = $account->publisher_id;
    my $publisher;
    
    my $statementPublishers =  RPS::DB::Item::MechanicalStatementPublisher->GetByMechanicalStatementAndPublisherID($self->_statementID(),$publisherID);
    while (my $statementPublisher = $statementPublishers->next())
    {
        $publisher = $statementPublisher;
    }   
    
    if (! $publisher)
    {
        $publisher =  $self->_createStatementPublisher($publisherID);  
    }
    
    return $publisher;
}

sub _getSubPublisherIDs
{
   my ($self, $statementID) = @_;
   
#   my @ids = RPS::DB::Item::MechanicalStatementPublisherTransaction->GetSubPublisherIDs($statementID);
    my @ids;
    my $allPublishers = RPS::DB::Item::MechanicalStatementPublisher->GetByMechanicalStatementID($statementID);
    while (my $p = $allPublishers->next())
    {
        push @ids, $p->publisher_id;
    }
    
   return @ids; 
}

sub _getPublisherStatement
{
    my ($self, $statementID, $publisherID) = @_;
    
    my $publisherStatement;
    
    $publisherStatement = RPS::DB::Item::MechanicalStatementPublisher->Lookup(mechanical_statement_id => $statementID, publisher_id => $publisherID); 
    
    return $publisherStatement;
}

sub _getIndirectAccountByPayorIndirectPublisherID
{
    my ($self, $payorID, $publisherID) = @_;
    my $indirectAccounts = RPS::DB::Item::PublisherIndirectBalanceAccount->GetByPayorIndirectPublisherID($payorID, $publisherID);
    
    return $indirectAccounts;
}
    
sub _createStatementTrack
{
    my ($self, $publisherID, $trackID, $albumID, $songTitle) = @_;
    return RPS::DB::Item::MechanicalStatementTrack->Create
    (
        mechanical_statement_id => $self->_statementID(),
        publisher_id => $publisherID,
        track_id => $trackID,
        album_id => $albumID,
        song_title => $songTitle,
    );
}

sub _createStatementCrossedLicenseTransaction
{
    my ($self, $statementLicense, $publisherID, $trackID, $pendingTrans) = @_;

    return RPS::DB::Item::MechanicalStatementCrossedLicenseTransaction->Create
    (
        #mechanical_statement_license_id => $statementLicense->mechanical_statement_license_id,
        mechanical_statement_id => $self->_statementID(),
        publisher_id => $publisherID,
        track_id => $trackID,
        amount => $pendingTrans->amount,
        memo => $pendingTrans->memo,
        check_number => $pendingTrans->check_number,
        pending_transaction_id => $pendingTrans->pending_transaction_id,
        transaction_date => $pendingTrans->transaction_date,
        type_code => $pendingTrans->type_code,
    );
}

sub _createStatementTransactionFromPendingTransaction
{
    my ($self, $pendingTrans) = @_;
    return RPS::DB::Item::MechanicalStatementTransaction->Create
    (
        mechanical_statement_id => $self->_statementID(),
        amount                  => $pendingTrans->amount(),
        memo                    => $pendingTrans->memo(),
        check_number            => $pendingTrans->check_number(),
        pending_transaction_id  => $pendingTrans->pending_transaction_id(),
        transaction_date        => $pendingTrans->transaction_date(),
        type_code               => $pendingTrans->type_code(),
    );
}

sub _createPublisherStatementTransactionFromPendingTransaction
{
    my ($self, $pendingTrans, $account) = @_;
    return RPS::DB::Item::MechanicalStatementPublisherTransaction->Create
    (
        mechanical_statement_id => $self->_statementID(),
        publisher_id            => $account->publisher_id(),
        amount                  => $pendingTrans->amount(),
        memo                    => $pendingTrans->memo(),
        check_number            => $pendingTrans->check_number(),
        pending_transaction_id  => $pendingTrans->pending_transaction_id(),
        transaction_date        => $pendingTrans->transaction_date(),
        type_code               => $pendingTrans->type_code(),
    );
}

sub _getLicensesByTrackIDAndDate
{
    my ($self, $trackID, $startDate, $endDate) = @_;
    return RPS::DB::Item::TrackLicense->GetLicensesByTrackIDAndDate($trackID, $startDate, $endDate);
}

sub _getLicensesByTrackID
{
    my ($self, $trackID) = @_;
    return RPS::DB::Item::TrackLicense->GetLicensesByTrackID($trackID);
}

sub _testAlreadyPaidSaleWithLicense
{   
    my ($self, $sale, $trackLicense) = @_;

    return RPS::DB::Item::SaleRunMap->HavePaidUSMechanicalsForSaleWithTrackLicense($sale->sale_id, $trackLicense->track_license_id);
}

sub _getStatementDBItem
{
    my ($self) = @_;

    my $statement = RPS::DB::Item::MechanicalStatement->Lookup(mechanical_statement_id => $self->{_statementID});
    return $statement;
}

sub _runType
{
    my ($self) = @_;
    return RPS::DB::Item::SaleRunMap::kRunTypeMechanical;
}

sub _getRunDBItem
{
    my ($self) = @_;

    my $statement = $self->_getStatementDBItem();
    my $runID = $statement->mechanical_run_id;
    my $run = RPS::DB::Item::MechanicalRun->Lookup(mechanical_run_id => $runID);
    return $run;
}

sub _getSaleIDs
{
    my ($self) = @_;

    # Read the sales to process from the SalePublisherMap table
    #
    my $statement = $self->_getStatementDBItem();

    return RPS::DB::Item::SalePublisherMap->GetPublisherMappedSaleIDs($statement->publisher_id, $statement->mechanical_run_id);
}


# We don't want to create log entries for DIGITAL sales outside of CA and US.
#
sub _createRetentionLogEntry
{
    my ($self, $sale, $trackID, $trackLicenseID, $share) = @_;

    my $product = $self->_getProduct($sale->product_id);

    if ($self->_productIsDigital($product))
    {
        if ('US' ne $sale->country_code
         && 'CA' ne $sale->country_code)
        {
            return;
        }
    }

    return $self->SUPER::_createRetentionLogEntry($sale, $trackID, $trackLicenseID, $share);
}

sub _getControlledCompContractByAlbumID
{
	my ($self, $albumID) = @_;
	return RPS::DB::Item::AlbumContract->GetCComp($albumID);
}

sub _trackIsExemptFromMechanicals
{
	my ($self, $trackID) = @_;
    assert($trackID);

	my $track = $self->_getTrack($trackID);
    assert($track);

	return $track->mechanical_exempt;
}

1;
