#------------------------------------------------------------
# Copyright (C) 2006 RoyaltyShare, Inc.   All Rights Reserved
#------------------------------------------------------------
package Distribution::Job::Parameters;

use strict;
use warnings;

# This class encapsulates the XML which represents the parameters
# we pass into the Delivery Framework.
# Yeah, the name is a bit clunky... <shrug>
#
# This class does _not_ read from any databases.
# It will either parse an XML string, or a hash table.
#
use XML::Simple;

use lib '/app/tools/common/lib';
use Common::XMLObject;
use Common::Assert;
use Common::UTF8;

use lib '/app/tools/distribution/lib';
use Distribution::Catalog::Album;

use File::Temp;

use base 'Common::XMLObject';

# This hash defines what tags/keys we expect.
#
use constant kAttribute    => 'a';
use constant kTag          => 't';
use constant kAlbumObjList => 'v';

my %gConfig = (

    # !!! I don't yet know what additional state we want in the XML
    # These below are copied from Delivery::Album, and just demonstrate
    # how to specify tags and attributes.
    # We _do_ know that there will be a bunch of <album> tags, so that is
    # set up at the bottom.
    #    'sell-bundled' => kAttribute,
    #    'is-explicit' => kAttribute,
    #    'upc' => kTag,
    #    'title' => kTag,
    #    'label' => kTag,
    #    'release-date' => kTag,
    #    'wholesale-price' => kTag,
    #    'cline' => kTag,
    #    'pline' => kTag,
    #    'genre' => kTag,
    #    'display-artist' => kTag,
    #    'territories' => kTag,
    'album' => kAlbumObjList,
);

sub _init {
    my $self = shift;
    my %args = @_;

    $self->SUPER::_init(%args);

    $self->{_serviceID}     = $args{service_id};
    $self->{_invertDenied}  = $args{invert_denied};
    $self->{_removeRegions} = $args{remove_regions};

    if ( $args{hash} ) {
        return $self->_initFromHash( $args{hash} );
    } elsif ( $args{XML} ) {
        return $self->_initFromXML( $args{XML} );
    } else {
        assert( 0, "ERROR - You need to provide either a hash ref or xml!" );
    }
}

sub _initFromHash {
    my ( $self, $hashRef ) = @_;

    assert( $hashRef, "ERROR - Missing 'hash' argument" );

    # It would be quicker, I suppose, to just hang onto the hash reference
    # and output that directly...  But, I'd like to give us the chance to
    # do some sort of validation.  And some of these keys need to become 'attributes'.
    # Plus, hanging onto references that get passed around is a good way to leak memory.
    #
    foreach my $expectedKey ( keys %gConfig ) {
        assert( exists $hashRef->{$expectedKey}, "ERROR - hash missing key $expectedKey" );
        if ( kAttribute eq $gConfig{$expectedKey} ) {
            $self->setXMLParam( $expectedKey, Common::UTF8::Encode( $hashRef->{$expectedKey} ) );
        } elsif ( kTag eq $gConfig{$expectedKey} ) {
            $self->{$expectedKey} = Common::UTF8::Encode( $hashRef->{$expectedKey} );
        } elsif ( kAlbumObjList eq $gConfig{$expectedKey} ) {
            $self->{album} = [];
            my $list = $hashRef->{$expectedKey};
            foreach my $albumHash (@$list) {

                # Instantiate an Album, save it here.
                #
                my $album = Distribution::Catalog::Album->new(
                    hash            => $albumHash,
                    service_id      => $self->{_serviceID},
                    countries_only  => $self->{_countriesOnly},
                    require_allowed => $self->{_requireAllowed}
                );
                push @{ $self->{album} }, $album;
            }
        }
    }

    return $self;
}

sub _initFromXML {
    my ( $self, $xml ) = @_;
    assert( $xml, "ERROR - Missing XML argument" );

    # Unfortunately, XML::Simple::XMLin seems to require that the XML be provided as a file
    # in order for UTF-8 to be handled correctly. Not sure why that is, as it's *supposed*
    # to be UTF-8 aware/friendly, but nonetheless...

    # my $hashRef = XMLin($xml, SuppressEmpty => undef, KeyAttr => [], ForceArray => ['album', 'volume', 'track']);

    my $xmlFile = File::Temp->new( UNLINK => 1, SUFFIX => ".xml" );
    $xmlFile->autoflush;
    binmode( $xmlFile, ":utf8" );

    my $xmlFilename = $xmlFile->filename;
    Common::Log::Debug("XMLin tempfile: $xmlFilename");

    print $xmlFile $xml;

    my $hashRef = XMLin( $xmlFilename, SuppressEmpty => undef, KeyAttr => [], ForceArray => [ 'album', 'volume', 'track' ] );

    #
    # This 'KeyAttr => []' business prevents XML::Simple from doing funny
    # things with the 'id' attribute present in the <volume> block.
    #
    # By default, XML::Simple would notice that attribute, and use it as
    # a hash key in the outgoing data structure...
    # So, this XML:
    # <volume id="1">
    #   <foo>blah</foo>
    #   <bar>blegh</bar>
    # </volume>
    # ... would result in data looking like this:
    #
    # { volume => { '1' => { foo => 'blah', bar => 'blegh' } } }
    #
    # That's not what we want.  We want all the <volume> data to end up in an array
    # reference, with 'id' just being another key => value pair in the each array element, like this:
    # { volume => [ { id => 1, foo => 'blah', bar => 'blegh'}, ... ] }
    #
    # Setting KeyAttr to an empty array lets that happen.
    #
    #
    # The 'ForceArray => ['album'] option forces XML::Simple to represent the <album> tags
    # with an array ref, even if there is only 1.  Usually, if there is only 1, it will simply
    # put that into a key => value pair, rather than key => [ $value ].  But our hash parsing
    # code always expects an array of albums.

    return $self->_initFromHash($hashRef);
}

sub albums {
    return shift->{album};
}

1;
