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

package Common::Parser::Iterator::Text::XML;

#
# Common::Parser::Iterator::Text::TSV
#
# Tab delimited file iterator.
#

use strict;

use lib '/app/tools/common/lib';
use Common::Assert;
use Common::Parser::Item::XMLNode;

use base 'Common::Parser::Iterator::Text';

use XML::XPath::Node::Element;
use XML::XPath;
use Data::Dumper;

sub fileType          { 'xml' }
sub _itemClass        { 'Common::Parser::Item::XMLNode' }
sub _rootNode         { 'import' }
sub _rowNode          { 'album' }
sub _trackNode        { 'tracks/track' }
sub _versionAttribute { 'specVersion' }

sub CanParse {
    my $class = shift;
    my $self  = {};
    bless $self, $class;

    my %args     = @_;
    my $filename = $args{filename};
    my $fh       = $args{fh};
    my $xml;

    $self->open( $args{filename} );
    eval { my $node = $self->{_xpath}->find("/") };

    Common::Log::Debug("  - Failed XML parsing: $@") if ($@);

    return $@ ? undef : $self;
}

sub open {
    my $self = shift;
    my $filename = shift || $self->{_filename};

    my $format;

    $self->close();

    $self->{_fh}       = undef;
    $self->{_xpath}    = undef;
    $self->{_error}    = undef;
    $self->{_filename} = $filename;

    my $data = $self->_readFile($filename);

    Common::Log::Debug("Data: '$self->{_inputFileData}'");

    assert( $filename, "File required" );

    Common::Log::Debug("Opening XML File: $filename");

    my $xp = XML::XPath->new( xml => $data );

    if ($xp) {
        $self->{_xpath} = $xp;
        return $xp;
    } else {
        Common::Log::Print("Failed to open text file: $filename");
        $self->{_error} = "Failed to open parse file";
        return;
    }
}

sub close {
    my $self = shift;

    $self->{_xpath}      = undef;
    $self->{_nodes}      = undef;
    $self->{_error}      = undef;
    $self->{_filename}   = undef;
    $self->{_currentRow} = undef;
}

# rewind
#
# Move the iterator back to the begining of the file
#
sub rewind {
    my $self = shift;

    $self->{_currentRow} = 0;
    $self->{_nodes}      = undef;
}

# hasNext
#
# Are there more lines to read from the file?
#
sub hasNext {
    my $self  = shift;
    my $count = scalar @{ $self->_getRowNodes() };

    Common::Log::Debug(" - Record count: $count, Current node: $self->{_currentRow}");

    return $count >= ( $self->{_currentRow} + 1 );
}

# next
#
# read a line from the datafile, split it up on the delimiter and return a
# Common::Parser::Item::Row object.
#
sub next {
    my $self = shift;

    return unless ( $self->hasNext() );

    $self->{_currentRow} += 1;

    my $record = $self->_fetchRow( $self->{_currentRow} );

    my $dataClass = $self->_itemClass();

    return $dataClass->new( node => $record );
}

# version
#
# Read the version attribute value from the root node and return it.
#
sub version {
    my $self          = shift;
    my $xpVersionPath = "/" . $self->_rootNode();

    my ($root) = $self->{_xpath}->findnodes($xpVersionPath);

    if ($root) {
        my $version = $root->getAttribute( $self->_versionAttribute );
        return "$version";
    } else {
        return;
    }
}

# _fetchRow
#
# Read a line from the input file and split it. Call from the 'next' method.
sub _fetchRow {
    my $self     = shift;
    my $position = shift || return;
    my $nodes    = $self->_getRowNodes();
    return $nodes ? $nodes->[ $position - 1 ] : undef;
}

sub _getRowNodes {
    my $self = shift;

    if ( $self->{_xpath} && !$self->{_nodes} ) {
        $self->{_nodes} = $self->_getAlbumNodes();
    }

    return $self->{_nodes};
}

sub _getAlbumNodes {
    my $self      = shift;
    my $xpRowNode = "/" . $self->_rootNode() . "/" . $self->_rowNode();

    Common::Log::Debug( Dumper( $self->{_xpath} ) );
    Common::Log::Debug("Lookup XPATH: $xpRowNode");
    my @albums = $self->{_xpath}->findnodes($xpRowNode);

    my $tracks;
    my @results;

    foreach my $album (@albums) {
        $tracks = $self->_getTrackNodes($album);

        if ($tracks) {
            foreach my $track (@$tracks) {
                push( @results, $self->_newAlbumNode( $album, $track ) );
            }
        } else {
            push( @results, $album );
        }
    }

    return \@results;
}

sub _getTrackNodes {
    my $self      = shift;
    my $albumNode = shift;
    my $trackPath = "/" . $self->_rowNode() . "/" . $self->_trackNode();

    my $xp = XML::XPath->new( xml => $albumNode->toString() );

    my @tracks = $xp->findnodes($trackPath);

    return @tracks ? \@tracks : undef;
}

sub _newAlbumNode {
    my $self       = shift;
    my $album      = shift;
    my $track      = shift;
    my $newNode    = new XML::XPath::Node::Element("album");
    my $trackNode  = new XML::XPath::Node::Element("tracks");
    my @childNodes = $album->getChildNodes();

    my $id = $self->_trackNode;
    $id =~ s/\/.*//g;

    foreach my $node (@childNodes) {
        unless ( $node->getName && $node->getName eq $id ) {
            $newNode->appendChild($node);
        }
    }

    $trackNode->appendChild($track);
    $newNode->appendChild($trackNode);

    return $newNode;
}

###
1;    # Play nicely.
###
