#---------------------------------------------------------------
# ____                   _ _         ____  _
#|  _ \ ___  _   _  __ _| | |_ _   _/ ___|| |__   __ _ _ __ ___
#| |_) / _ \| | | |/ _` | | __| | | \___ \| '_ \ / _` | '__/ _ \
#|  _ < (_) | |_| | (_| | | |_| |_| |___) | | | | (_| | | |  __/
#|_| \_\___/ \__, |\__,_|_|\__|\__, |____/|_| |_|\__,_|_|  \___|
#            |___/             |___/
#
# 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 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'],
            value => 'By Date',
        },
        'StartDate' =>
        {
            order => 2,
            type => Report::Dynamic::Parameters::kTypeDate,
            display => 'Start',
        },
        'EndDate' =>
        {
            order => 3,
            type => Report::Dynamic::Parameters::kTypeDate,
            display => 'End',
        },
    );

    my %byRunTypeHash =  (
        'RunType' => 
        {
            type => Report::Dynamic::Parameters::kTypeMenu,
            display => 'Select Royalty Run',
            order => 4,
            validValues => [],
#            value => ['Choose...'],
        },
        'Payor' => 
        {
            type => Report::Dynamic::Parameters::kTypeMenu,
            display => 'Payor',
            order => 5,
            validValues => [],
        },
        'RunName' => 
        {
            type => Report::Dynamic::Parameters::kTypeMenu,
            display => 'Run Name',
            order => 6,
            validValues => [],
        },
    );

    my %consolidateHash =  (
        'Consolidate' => 
        {
            type => Report::Dynamic::Parameters::kTypeMenu,
            display => 'Consolidate By',
            order => 7,
            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 => 8,
            value => 'Enter Name',
        },
        'NameReportAddToSaved' => 
        {
            type => Report::Dynamic::Parameters::kTypeBoolean,
            display => 'Add to Saved Reports',
            order => 9,
            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, %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, %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();
}


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->GetAll(); 
    my %usMechPayors;
    while (my $mechRun = $allUSMechRuns->next())
    {
        if (RPS::RoyaltyRun::Status::kComplete != $mechRun->status()
         && RPS::RoyaltyRun::Status::kCommitted != $mechRun->status()
         && RPS::RoyaltyRun::Status::kClosed != $mechRun->status()
         && RPS::RoyaltyRun::Status::kInvalidated != $mechRun->status())
        {
            next;
        }

        my $payorID = $mechRun->payor_id();
        my $label = $mechRun->label();
        my $runID = $mechRun->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->GetAll(); 
    my %caMechPayors;
    while (my $caMechRun = $allCAMechRuns->next())
    {
        if (RPS::RoyaltyRun::Status::kComplete != $caMechRun->status()
         && RPS::RoyaltyRun::Status::kCommitted != $caMechRun->status()
         && RPS::RoyaltyRun::Status::kClosed != $caMechRun->status()
         && RPS::RoyaltyRun::Status::kInvalidated != $caMechRun->status())
        {
            next;
        }

        my $payorID = $caMechRun->payor_id();
        # label is generally not defined for CA Mechanicals, we will use the date
        # range instead
        my $label;
        if( $caMechRun->label() ) {
            $label = $caMechRun->label();
        } else {
            $label = $caMechRun->start_date() . " - " . $caMechRun->end_date();
        }
        my $runID = $caMechRun->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;
