#---------------------------------------------------------------
# ____                   _ _         ____  _
#|  _ \ ___  _   _  __ _| | |_ _   _/ ___|| |__   __ _ _ __ ___
#| |_) / _ \| | | |/ _` | | __| | | \___ \| '_ \ / _` | '__/ _ \
#|  _ < (_) | |_| | (_| | | |_| |_| |___) | | | | (_| | | |  __/
#|_| \_\___/ \__, |\__,_|_|\__|\__, |____/|_| |_|\__,_|_|  \___|
#            |___/             |___/
#
# Copyright (C) 2012 RoyaltyShare, Inc.   All Rights Reserved
#---------------------------------------------------------------

package RPS::Report::Dynamic::Sales::Parameters;
use strict;
use warnings;

use Data::Dumper;

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

use Common::RSApp;
use Common::WriteXML;
use Common::Client;
use Common::CurrencyFormat;

use Raptor::DB::Item::Period;
use RPS::CountryList;
use RPS::DB::Item::Payor;
use RPS::DB::Item::ArtistRoyaltyRun;
use RPS::DB::Item::LabelRoyaltyRun;
use RPS::DB::Item::MechanicalRun;
use RPS::DB::Item::UKMechanicalRun;
use RPS::DB::Item::CAMechanicalRun;
use RPS::RoyaltyRun::Status;
use RPS::RoyaltyRun::RoyaltyRunStatusList;
use RPS::DB::Item::SaleRunMap;
use RPS::Report::Dynamic::Sales::CatalogFieldHash;
use RPS::Report::Dynamic::Sales::SourceFieldHash;

use base 'RPS::Report::Dynamic::Parameters';

sub _CriteriaXSLT {
    '/app/tools/rps/templates/report/dynamic/criteria/sales.xsl';
}

# All the Sales reports will have some common Criteria, so we'll define that here.
#
sub _Criteria {
    my ( $self, $runTypes ) = @_;

    my %byCatalogHash = (
        'CatalogFilter' => {
            type => Report::Dynamic::Parameters::kTypeCustom,
        },
        'CatalogFieldList' => {
            type  => Report::Dynamic::Parameters::kTypeCustom,
            class => 'RPS::Report::Dynamic::Sales::CatalogFieldHash',
        },
    );

    my %bySourceHash = (
        'SourceFilter' => {
            type => Report::Dynamic::Parameters::kTypeCustom,
        },
        'SourceFieldList' => {
            type  => Report::Dynamic::Parameters::kTypeCustom,
            class => 'RPS::Report::Dynamic::Sales::SourceFieldHash',
        },
    );

    my %byDateHash = (
        'ShowByType' => {
            type        => Report::Dynamic::Parameters::kTypeMenu,
            display     => 'Show sales',
            order       => 1,
            validValues => [ 'By Date', 'By Royalty Run', 'By Sales Period' ],
            value       => 'By Date',
        },
        'StartDate' => {
            order   => 2,
            type    => Report::Dynamic::Parameters::kTypeDate,
            display => 'Start Date',
        },
        'EndDate' => {
            order   => 3,
            type    => Report::Dynamic::Parameters::kTypeDate,
            display => 'End Date',
        },
    );

    my %byRunTypeHash = (
        'RunType' => {
            type        => Report::Dynamic::Parameters::kTypeMenu,
            display     => 'Select Royalty Run',
            order       => 4,
            validValues => [],
        },
        'Payor' => {
            type        => Report::Dynamic::Parameters::kTypeMenu,
            display     => 'Payor',
            order       => 5,
            validValues => [],
        },
        'RunName' => {
            type        => Report::Dynamic::Parameters::kTypeMenu,
            display     => 'Run Name',
            order       => 6,
            validValues => [],
        },
        'PayeeDetail' => {
            type        => Report::Dynamic::Parameters::kTypeMenu,
            display     => 'Payee Detail',
            order       => 7,
            validValues => [ 'All Payees', 'Single Payee' ],
            value       => '',
        },
        'PayeeName' => {
            type    => Report::Dynamic::Parameters::kTypeScalar,
            display => 'Payee Name',
            order   => 8,
        },
        'PayeeID' => {
            type => Report::Dynamic::Parameters::kTypeCustom,
        },
    );

    my %bySalesPeriodHash = (
        'StartPeriod' => {
            order   => 9,
            type    => Report::Dynamic::Parameters::kTypeMenu,
            display => 'Start Period',
            validValues => [],
        },
        'EndPeriod' => {
            order   => 10,
            type    => Report::Dynamic::Parameters::kTypeMenu,
            display => 'End Period',
            validValues => [],
        },
    );

    my %consolidateHash = (
        'Consolidate' => {
            type        => Report::Dynamic::Parameters::kTypeMenu,
            display     => 'Consolidate By',
            order       => 11,
            validValues => [ 'None', 'Month', 'Quarter', 'Year' ],
            value       => 'None',
        },
    );

=for comment not implementing this quite yet, might not even go here

    my %nameReportHash =  (
        'NameReport' =>
        {
            type => Report::Dynamic::Parameters::kTypeScalar,
            display => 'Name Your Report',
            order => 10,
            value => 'Enter Name',
        },
        'NameReportAddToSaved' =>
        {
            type => Report::Dynamic::Parameters::kTypeBoolean,
            display => 'Add to Saved Reports',
            order => 11,
            validValues => ['Enter Name'],
        },
    );

=cut

    my $client = Common::Client::Current();
    my %bigHash;

    # if the client has any type of royalty runs...
    if (   $client->isClientType(Common::Client::kArtist)
        || $client->isClientType(Common::Client::kLabel)
        || $client->isClientType(Common::Client::kUSMechanicals)
        || $client->isClientType(Common::Client::kUKMechanicals)
        || $client->isClientType(Common::Client::kCAMechanicals) ) {
        my @runTypeArray;

        # load the run types for this client and add to the hash
        $self->_loadRunTypes( \@runTypeArray );
        $byRunTypeHash{'RunType'}{validValues} = \@runTypeArray;
        %bigHash = ( %byCatalogHash, %bySourceHash, %byDateHash, %byRunTypeHash, %bySalesPeriodHash, %consolidateHash );
    } else {

        # tweak the valid values to remove the "By Royalty Run" menu option
        # we may want to update the factory to default to a label for single
        # item menus
        $byDateHash{'ShowByType'}{validValues} = ['By Date'];
        %bigHash = ( %byCatalogHash, %bySourceHash, %byDateHash, %bySalesPeriodHash, %consolidateHash );
    }
    return \%bigHash;
}

sub _loadRunTypes {

    # based on client type, check for reportable runs for each run type

    my ( $self, $runTypes ) = @_;
    my $client     = Common::Client::Current();
    my $statusList = RPS::RoyaltyRun::RoyaltyRunStatusList->new();

    push( @$runTypes, 'Choose...' );

    if ( $client->isClientType(Common::Client::kArtist) ) {
        if (   $statusList->{ArtistRuns}->Complete()
            || $statusList->{ArtistRuns}->Committed()
            || $statusList->{ArtistRuns}->Closed()
            || $statusList->{ArtistRuns}->Invalidated() ) {

            #... yes, we have some Artist runs worth reporting on.
            push( @$runTypes, 'Artist/Producer' );
        }
    }
    if ( $client->isClientType(Common::Client::kLabel) ) {
        if (   $statusList->{LabelRuns}->Complete()
            || $statusList->{LabelRuns}->Committed()
            || $statusList->{LabelRuns}->Closed()
            || $statusList->{LabelRuns}->Invalidated() ) {

            #... yes, we have some Label runs worth reporting on.
            push( @$runTypes, 'Label' );
        }
    }
    if ( $client->isClientType(Common::Client::kUSMechanicals) ) {
        if (   $statusList->{USMechanicalRuns}->Complete()
            || $statusList->{USMechanicalRuns}->Committed()
            || $statusList->{USMechanicalRuns}->Closed()
            || $statusList->{USMechanicalRuns}->Invalidated() ) {

            #... yes, we have some US Mechanical runs worth reporting on.
            push( @$runTypes, 'US Mechanicals' );
        }
    }
    if ( $client->isClientType(Common::Client::kUKMechanicals) ) {
        if (   $statusList->{UKMechanicalRuns}->Complete()
            || $statusList->{UKMechanicalRuns}->Committed()
            || $statusList->{UKMechanicalRuns}->Closed()
            || $statusList->{UKMechanicalRuns}->Invalidated() ) {

            #... yes, we have some UK Mechanical runs worth reporting on.
            push( @$runTypes, 'UK Mechanicals' );
        }
    }
    if ( $client->isClientType(Common::Client::kCAMechanicals) ) {
        if (   $statusList->{CAMechanicalRuns}->Complete()
            || $statusList->{CAMechanicalRuns}->Committed()
            || $statusList->{CAMechanicalRuns}->Closed()
            || $statusList->{CAMechanicalRuns}->Invalidated() ) {

            #... yes, we have some CA Mechanical runs worth reporting on.
            push( @$runTypes, 'CA Mechanicals' );
        }
    }
}

sub _initProperties {
    my ( $self, %args ) = @_;

    $self->SUPER::_initProperties();
    $self->{RunData}            = $self->_initPayorAndRunXML();
    $self->{CountryList}        = RPS::CountryList->new();
    $self->{CurrencyFormatList} = Common::CurrencyFormatList->new();
    $self->{SalesPeriods}       = $self->_initSalesPeriods();
}

sub _initSalesPeriods {
    my $self = shift;

    my $hData = {};

    my $oPeriods = Raptor::DB::Item::Period->GetAllSortedByPeriodID();
    while ( my $oPeriod = $oPeriods->next() ) {
        push @{ $hData->{Period}},
        {
            PeriodID  => $oPeriod->period_id,
            Name      => $self->_getPeriodFullName($oPeriod),
            StartDate => $oPeriod->start_date,
            EndDate   => $oPeriod->end_date,
        }
    }

    return $hData;
}

sub _getPeriodFullName {
    my ($self, $oPeriod) = @_;

    my ($startYear, $startMonth, $startDay) = split('-', $oPeriod->start_date);
    my ($endYear,   $endMonth,   $endDay)   = split('-', $oPeriod->end_date);

    my @months = qw(Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec);

    my $startFormatted = "$months[$startMonth - 1] $startDay, $startYear";

    if ( $oPeriod->period_id == 0 ) {
        return $oPeriod->name eq ''
            ? "$startFormatted - Present (Current)"
            :  $oPeriod->name . " (Current)"
    } elsif ( $oPeriod->period_id != 0 && $oPeriod->name eq '' ) {
        my $endFormatted = "$months[$endMonth - 1] $endDay, $endYear";
        return "$startFormatted - $endFormatted";
    }

    return $oPeriod->name;
}

sub _initPayorAndRunXML {

    # First, we can declare an empty hash.  This method will return this hash by reference.
    #
    my %returnHash;

    # Rather than build joins, I'm going to read the whole payor table in at once
    # and cache it in a hash. I find this easier to grok, personally, and the payor
    # table is not very large...
    #
    my %payorCache;
    my $allPayors = RPS::DB::Item::Payor->GetAll();
    while ( my $payor = $allPayors->next() ) {

        # We'll use the payor_id as the key, and the name as the hash value.
        #
        $payorCache{ $payor->payor_id() } = $payor->name();
    }

    # Declaring these way out here... We will use these two variables a few times
    # to traverse hashes.
    #
    my ( $key, $value );

    # Since we're going to be returning _every_ completed (or commited or closed)
    # run, I would just iterate over a single collection and build the nested hashes
    # as we go.
    #
    # So, here's how I would do this for artist runs.
    #
    my $allArtistRuns = RPS::DB::Item::ArtistRoyaltyRun->GetAll();
    #
    # Get all the runs. Now, if we had a 'GetAllCompletedRuns' method, I'd use
    # that instead, so I could skip the test of 'status' in the following loop.
    # But this certainly works...

    # There are a few ways to go here... I think we want a 'list' of <Payor> blocks.
    # The easiest way to do that will be to build up a temporary hash
    # (keyed by payor_id) containing Arrays of runs. Then at the end of this loop
    # we'll iterate over that and shove things into the 'return hash'.
    #
    my %artistPayors;

    while ( my $artistRun = $allArtistRuns->next() ) {
        if (   RPS::RoyaltyRun::Status::kComplete != $artistRun->status()
            && RPS::RoyaltyRun::Status::kCommitted != $artistRun->status()
            && RPS::RoyaltyRun::Status::kClosed != $artistRun->status()
            && RPS::RoyaltyRun::Status::kInvalidated != $artistRun->status() ) {

            # Skip over runs that are not complete, committed, or closed.
            #
            next;
        }

        # Let's de-reference some fields to make this easier to read.
        #
        my $payorID = $artistRun->payor_id();
        my $label   = $artistRun->label();
        my $runID   = $artistRun->artist_royalty_run_id();

        # So note how we treat the final link in this hash chain as an array.
        # Note also that we don't have to explicitly allocate an array. Perl just
        # deals with it.
        #
        push @{ $artistPayors{$payorID} },
          {
            'RunID' => $runID,
            'Label' => $label,
          };
    }

    # Now iterate over that temporary hash... Basically we want to re-build that as
    # an array that lives inside the hash we want to return.
    #
    my @artistPayorArray;
    while ( ( $key, $value ) = each(%artistPayors) ) {

        # '$value' here is an array reference.
        # To tell the XML Output code that each of these should be in a tag called <Run>, we
        # add this 'Run' key
        #
        push @artistPayorArray, {
            'PayorID' => $key,
            'Name'    => $payorCache{$key},
            'Run'     => $value,              # remember, 'Run' is an array reference
        };
    }

    # Now we add this new 'payor array' to the outgoing hash.
    # Again, we want each item in this array to start with a <Payor> tag, so
    # we use a hash key of 'Payor'.
    $returnHash{'ArtistRoyalty'}{'Payor'} = \@artistPayorArray;

    my $allLabelRuns = RPS::DB::Item::LabelRoyaltyRun->GetAll();

    my %labelPayors;

    my $size = $allLabelRuns->size();

    while ( my $labelRun = $allLabelRuns->next() ) {
        if (   RPS::RoyaltyRun::Status::kComplete != $labelRun->status()
            && RPS::RoyaltyRun::Status::kCommitted != $labelRun->status()
            && RPS::RoyaltyRun::Status::kClosed != $labelRun->status()
            && RPS::RoyaltyRun::Status::kInvalidated != $labelRun->status() ) {
            next;
        }

        my $payorID = $labelRun->payor_id();
        my $label   = $labelRun->label();
        my $runID   = $labelRun->label_royalty_run_id();

        push @{ $labelPayors{$payorID} },
          {
            'RunID' => $runID,
            'Label' => $label,
          };
    }

    my @labelPayorArray;
    while ( ( $key, $value ) = each(%labelPayors) ) {
        push @labelPayorArray, {
            'PayorID' => $key,
            'Name'    => $payorCache{$key},
            'Run'     => $value,              # remember, 'Run' is an array reference
        };
    }

    $returnHash{'LabelRoyalty'}{'Payor'} = \@labelPayorArray;

    # Again, this time with US Mechanicals.
    #
    my $allUSMechRuns = RPS::DB::Item::MechanicalRun->GetAllExtended();
    my %usMechPayors;
    foreach my $hMechRun ( @$allUSMechRuns ) {
        if (   RPS::RoyaltyRun::Status::kComplete != $hMechRun->{status}
            && RPS::RoyaltyRun::Status::kCommitted != $hMechRun->{status}
            && RPS::RoyaltyRun::Status::kClosed != $hMechRun->{status}
            && RPS::RoyaltyRun::Status::kInvalidated != $hMechRun->{status} ) {
            next;
        }

        my $payorID = $hMechRun->{payor_id};
        my $label   = $hMechRun->{label};
        my $runID   = $hMechRun->{mechanical_run_id};

        push @{ $usMechPayors{$payorID} },
          {
            'RunID' => $runID,
            'Label' => $label,
          };
    }

    my @usMechPayorArray;
    while ( ( $key, $value ) = each(%usMechPayors) ) {
        push @usMechPayorArray,
          {
            'PayorID' => $key,
            'Name'    => $payorCache{$key},
            'Run'     => $value,
          };
    }
    $returnHash{'USMechanicals'}{'Payor'} = \@usMechPayorArray;

    # !!! I'll leave the others as an exercise for the reader...

    # Again, this time with UK Mechanicals.
    #
    my $allUKMechRuns = RPS::DB::Item::UKMechanicalRun->GetAll();
    my %ukMechPayors;
    while ( my $ukMechRun = $allUKMechRuns->next() ) {
        if (   RPS::RoyaltyRun::Status::kComplete != $ukMechRun->status()
            && RPS::RoyaltyRun::Status::kCommitted != $ukMechRun->status()
            && RPS::RoyaltyRun::Status::kClosed != $ukMechRun->status()
            && RPS::RoyaltyRun::Status::kInvalidated != $ukMechRun->status() ) {
            next;
        }

        my $payorID = $ukMechRun->payor_id();
        my $label   = "Q" . $ukMechRun->quarter() . " " . $ukMechRun->year();
        my $runID   = $ukMechRun->uk_mechanical_run_id();

        push @{ $ukMechPayors{$payorID} },
          {
            'RunID' => $runID,
            'Label' => $label,
          };
    }

    my @ukMechPayorArray;
    while ( ( $key, $value ) = each(%ukMechPayors) ) {
        push @ukMechPayorArray,
          {
            'PayorID' => $key,
            'Name'    => $payorCache{$key},
            'Run'     => $value,
          };
    }
    $returnHash{'UKMechanicals'}{'Payor'} = \@ukMechPayorArray;

    # Again, this time with CA Mechanicals.
    #
    my $allCAMechRuns = RPS::DB::Item::CAMechanicalRun->GetAllExtended();
    my %caMechPayors;
    foreach my $hCaMechRun ( @$allCAMechRuns ) {
        if (   RPS::RoyaltyRun::Status::kComplete != $hCaMechRun->{status}
            && RPS::RoyaltyRun::Status::kCommitted != $hCaMechRun->{status}
            && RPS::RoyaltyRun::Status::kClosed != $hCaMechRun->{status}
            && RPS::RoyaltyRun::Status::kInvalidated != $hCaMechRun->{status} ) {
            next;
        }

        my $payorID = $hCaMechRun->{payor_id};

        # label is generally not defined for CA Mechanicals, we will use the date
        # range instead
        my $label;
        if ( $hCaMechRun->{label} ) {
            $label = $hCaMechRun->{label};
        } else {
            $label = $hCaMechRun->{start_date} . " - " . $hCaMechRun->{end_date};
        }
        my $runID = $hCaMechRun->{ca_mechanical_run_id};

        push @{ $caMechPayors{$payorID} },
          {
            'RunID' => $runID,
            'Label' => $label,
          };
    }

    my @caMechPayorArray;
    while ( ( $key, $value ) = each(%caMechPayors) ) {
        push @caMechPayorArray,
          {
            'PayorID' => $key,
            'Name'    => $payorCache{$key},
            'Run'     => $value,
          };
    }
    $returnHash{'CAMechanicals'}{'Payor'} = \@caMechPayorArray;

    # The preceeding backslash means 'return by reference'.
    # It's ok to return by value, too, but this is generally more efficient.
    #
    return \%returnHash;
}

# Overriding the base class so that we can look up the run name based on the run id and run type
#
sub specifiedCriteria {
    my ($self) = @_;

    my %specifiedCriteria;

    if ( $self->{Criteria} ) {
        my $runID;
        my $payorID;
        my $runType;
        my $runName;

        foreach my $key ( keys %{ $self->{Criteria} } ) {
            if ( $key eq 'RunName' ) {
                $runID = $self->{Criteria}{$key}->access();
            }
            if ( $key eq 'Payor' ) {
                $payorID = $self->{Criteria}{$key}->access();
            } elsif ( $key eq 'RunType' ) {
                $runType = $self->{Criteria}{$key}->access();

                # The run types name are slightly different in the data structure
                if ( $runType eq 'Artist/Producer' ) {
                    $runType = 'ArtistRoyalty';
                } elsif ( $runType eq 'Label' ) {
                    $runType = 'LabelRoyalty';
                } elsif ( $runType eq 'CA Mechanicals' ) {
                    $runType = 'CAMechanicals';
                } elsif ( $runType eq 'US Mechanicals' ) {
                    $runType = 'USMechanicals';
                } elsif ( $runType eq 'UK Mechanicals' ) {
                    $runType = 'UKMechanicals';
                }
            }
        }

        if ( $runID && $runType && $payorID ) {
            my $runTypeGroup = $self->{RunData}{$runType};
            foreach my $key ( keys %{$runTypeGroup} ) {
                my $payorRef = $runTypeGroup->{$key};
                my @payor    = @$payorRef;

                foreach my $payorHash (@payor) {
                    if ( $payorHash->{PayorID} == $payorID ) {
                        my $runListRef = $payorHash->{Run};
                        my @runList    = @$runListRef;

                        foreach my $runHash (@runList) {
                            if ( $runHash->{RunID} == $runID ) {
                                $runName = $runHash->{Label};
                            }
                        }
                    }
                }
            }

            return $runName;
        }
    }

    return undef;

}

1;
