#
# Copyright (c) 2004-2007 - Consultas, PKG.fr
#
# This file is part of A2P.
#
# A2P is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# A2P is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with A2P; if not, write to the Free Software
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
#
# $Id: SimpleXML.pm 3 2007-10-18 16:20:19Z guillaume $
#

package A2P::EService::SimpleXML ;

# API to handle XML format

use strict ;
use XML::LibXML ;
use A2P::EService::Tools qw( abort ) ;

BEGIN {
    our $VERSION = sprintf "%s", q$Rev: 836 $ =~ /(\d[0-9.]+)\s+/ ;
}
our $VERSION ;

sub new {
    my $class = shift ;

    my $parser = new XML::LibXML ;
    abort "Unable to get an XML parser" unless (defined($parser));

    my $self = {
        PARSER => $parser,
        NODES  => {}
    };

    bless $self, $class ;

    # Still a file if called with an argument
    $self->loadfile($_[0]) if @_ ;

    return $self ;
}

sub loadfile {
    my $self = shift ;
    my $file = shift or abort "No XML file given to load" ;

    abort "Bad XMLfile found" unless -s $file ;

    my $xml = $self->{PARSER}->parse_file( $file );
    abort "Can't parse '$file' as XML" unless (defined($xml));

    # Keep trace of encoding
    $self->{ENCODING} = lc( $xml->encoding() || '' );

    my $root = $xml->lastChild ;
    abort "No XML root found in file" unless (defined($root));

    return $self->{ROOT} = $root ;
}

sub encoding {
    my $self = shift ;
    return defined($self->{ENCODING}) ? $self->{ENCODING} : '' ;
}

sub root {
    my $self = shift ;
    return defined($self->{ROOT}) ? $self->{ROOT} : '' ;
}

sub name {
    my $root = $_[0]->root ;
    return $root ? $root->nodeName : '' ;
}

sub attribut {
    my $root = $_[0]->root ;
    my $name = $_[1] or return '' ;
    return $root ? $root->getAttribute($name) : '' ;
}

sub content {
    my $self = shift ;
    my $root = $self->root ;
    my $content = $root ? $root->textContent : '' ;

    # Strip first empty lines due to XML format
    ( $content ) = $content =~ /^\n*(.*)$/ms if $content ;

    return $content ;
}

sub nodes {
    my $self = shift ;
    my $name = shift or return () ;
    my $filter = shift || {} ;

    my %attrib = () ;
    while (@_) {
        my $attribut = shift ;
        $attrib{$attribut} = "" ;
    }

    # Control attribut filter is a hash list
    $filter = {} unless (ref($filter) =~ /^HASH/i);

    # Use cached nodes if still passed here
    my @nodes ;
    if ( exists($self->{NODES}->{$name})
    and ref($self->{NODES}->{$name}) =~ /^ARRAY/i ) {
        @nodes = @{$self->{NODES}->{$name}} ;

    } else {
        my $root ;
        # Return no node value if root is not available
        return () unless ( $root = $self->root );

        # Get nodes
        @nodes = $root->getChildrenByTagName($name);

        # Unbind nodes to skip retreiving their content in the body
        map { $_->unbindNode } @nodes ;

        # Keep the list in cache
        $self->{NODES}->{$name} = \@nodes ;
    }

    my @values = () ;
    foreach my $node ( @nodes ) {
        # Check if this node is to return
        my $filtered = 0 ;
        foreach my $key (keys(%{$filter})) {
            $filtered ++ ;
            my $match = $filter->{$key} ;
            my $value = $node->getAttribute($key) || "" ;
            next unless ( $value =~ /^$match$/ );
            $filtered = 0 ;
            last ;
        }
        next if $filtered ;

        my $found = 0 ;
        foreach my $attribut (keys(%attrib)) {
            my $value = $node->getAttribute($attribut) || "" ;
            next unless ($value);
            $found ++ ;
            $attrib{$attribut} = $value ;
        }

        my $content = $node->textContent || "" ;
        push @values, $found ? [ $content, \%attrib ] : $content ;
    }

    return @values ;
}

1;
