#!/usr/bin/perl
use strict;
use Data::Dumper;
use lib '/app/tools/common/lib';
use lib '/app/tools/rps/lib';
use Common::RSApp;
use Common::WriteXML;
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::RoyaltyRun::Status;

# To make this sample code run, we need an Application singleton.
# We'll use C_RSDEMO as the client id.
#
my $app = Common::RSApp->new( clientID => 202 );

my $xml = getRunTypeStuff();

print "Here's the data structure:" . Dumper($xml) . "\n";

print "... And here is the corresponding XML:\n";
print Common::WriteXML::GetXMLString($xml) . "\n";

sub getRunTypeStuff {

    # 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() ) {

            # 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();
    print "label Run collection size: $size\n";

    while ( my $labelRun = $allLabelRuns->next() ) {
        if (   RPS::RoyaltyRun::Status::kComplete != $labelRun->status()
            && RPS::RoyaltyRun::Status::kCommitted != $labelRun->status()
            && RPS::RoyaltyRun::Status::kClosed != $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() ) {
            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() ) {
            next;
        }

        my $payorID = $ukMechRun->payor_id();
        my $label   = $ukMechRun->label();
        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;

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