use 5.010;
use strict;
use warnings;

package Geo::Openstreetmap::Parser;

# ABSTRACT: Openstreetmap XML dump parser

use autodie;

use XML::Parser;


=method new

Creates a parser object

    my $parser = Geo::Openstreetmap::Parser->new( node => \&process_node, ... );

    sub process_node {
        my ($obj) = @_;
        ...
    }

Callbacks are possible for any tag, but useful for osm primitives:
    node
    way
    relation

Callback function receives hash with params:
    attr    - hash with xml attributes
    tag     - hash with osm tags
    nd      - array of node_ids (for ways)
    member  - array of hashes like { type => 'way', role => 'from', ref => '-1' } (for relations)

=cut

sub new
{
    my ($class, %callback) = @_;
    
    my $self = bless { callback => \%callback }, $class;
    $self->_init_parser();
    return $self;
}


=method parse

Parses XML input, executing defined callback functions for OSM objects

    $parser->parse( *STDIN );

=cut

sub parse
{
    my ($self, $fh) = @_;
    $self->{parser}->parse($fh);
    return;
}




sub _init_parser
{
    my ($self) = @_;

    my @path;

    $self->{parser} = XML::Parser->new( Handlers => {
            Start => sub {
                    my ($expat, $el, %attr) = @_;
                    push @path, { attr => \%attr };
                },
            End => sub {
                    my ($expat, $el) = @_;
                    my $obj = pop @path;

                    for ( $el ) {
                        my $list = $path[-1]->{$el};
                        when ('tag')    { $list->{$obj->{attr}->{k}} = $obj->{attr}->{v} }
                        when ('nd')     { push @$list, $obj->{attr}->{ref} }
                        when ('member') { push @$list, $obj->{attr} }

                        when ($self->{callback}) { $self->{callback}->{$el}->($obj) }
                    }
                },
        });
    return;
}


sub _process_object
{
    my ($self, $el, $obj) = @_;

    $self->{$el}->($obj)  if $self->{$el};
    return;
}

1;
