#!/usr/local/bin/perl -w
###########################################
# street-cleaning - Find street cleaning
#    times between cross streets.
# Mike Schilli, 2011 (m@perlmeister.com)
###########################################
use strict;
use Geo::Parse::OSM;
use Graph::Directed;
use LWP::UserAgent;

my @bbox = qw( -122.4374  37.74754
               -122.42096 37.75894 );
my $url = "http://api.openstreetmap.org/" .
  "api/0.6/map?bbox=" . join ',', @bbox;

my $mapfile = "map.osm.gz";

my( $street_on, $street_cross1, 
    $street_cross2, $side ) = @ARGV;
die "usage: $0 street cross1 cross2 side" 
  if !defined $side;

my $ua = LWP::UserAgent->new();
$ua->default_header("Accept-Encoding", 
                    "gzip");
if( ! -f $mapfile or -M $mapfile > 7 ) {
  my $rsp = $ua->mirror( $url, $mapfile );
  $rsp->is_success or die $rsp->message();
}

my $osm = Geo::Parse::OSM->new( $mapfile );

my %on_nodes = ();

street_nodes( $osm, $street_on, sub { 
  $on_nodes{ $_[0] } = 1;
} );

my $cross1_node = cross_find( $osm, 
      \%on_nodes, $street_cross1 );
my $cross2_node = cross_find( $osm, 
      \%on_nodes, $street_cross2 );

my( $nodes, $flip_order) = 
  find_path_on_way( $osm, $street_on, 
         $cross1_node, $cross2_node );

$side = flipside( $side, $flip_order );

my $parking = parking($osm, $nodes, $side);

print "Street Cleaning: ", 
      street_cleaning( $parking ), "\n";

###########################################
sub street_nodes {
###########################################
  my( $osm, $name, $cb ) = @_;

  $osm->seek_to( 0 );
  $osm->parse( sub {
    my($n) = @_;
    if( exists $n->{tag}->{name} and
      $n->{tag}->{name} eq $name ) {
      for my $n ( @{ $n->{chain} } ) {
        $cb->( $n ) or last;
      }
    }
  }, only => "way");
}

###########################################
sub cross_find {
###########################################
  my($osm, $on_nodes, $cross_street) = @_;

  my $found;
  street_nodes( $osm, $cross_street, sub {
    my($n) = @_;
    if( exists $on_nodes->{ $n } ) {
      $found = $n;
      return 0; # stop iteration
    }
    return 1; # continue iteration
  });

  return $found;
}

###########################################
sub find_path_on_way {
###########################################
  my( $osm, $way_name, @nodes ) = @_;

  my $g = Graph::Directed->new();

  $osm->seek_to(0);
  $osm->parse(sub {
    my($n) = @_;
    if( exists $n->{tag}->{name} and
        $n->{tag}->{name} eq $way_name ) {
      $g->add_path( @{ $n->{chain} } );
    }
  }, only => "way" );

  my $flip_order = 0;

  my @path = $g->SP_Dijkstra( @nodes );

  if( !@path ) {
      @nodes = reverse @nodes;
      @path = $g->SP_Dijkstra( @nodes );
      $flip_order = 1;
  }

  return( \@path, $flip_order );
}

###########################################
sub parking {
###########################################
  my( $osm, $nodes, $side ) = @_;

  my %to_match = map { $_ => 1 } @$nodes;
  my %results  = ();

  $osm->seek_to( 0 );
  $osm->parse( sub {
    my($w) = @_;

    my @matches = 
      grep { exists $to_match{$_} }
           @{ $w->{chain} };

    return if @matches < 2;

    for my $tag ( keys %{ $w->{tag} } ) {
      if( $tag =~ 
          /parking:condition:$side:.*/ ) {
        $results{$tag} = $w->{tag}->{$tag};
      }
    }
  }, only => "way");

  return \%results;
}

###########################################
sub street_cleaning {
###########################################
  my( $parking ) = @_;

  for my $key ( keys %$parking ) {
    if( $key =~ /(.*)\:reason/ ) {
      if( $parking->{ $key } eq 
          "street_cleaning" ) {
        return $parking->{ $1 . 
            ":time_interval" };
      }
    }
  }

  return undef;
}

###########################################
sub flipside {
###########################################
  my($side, $flip_order) = @_;

  if( $flip_order ) {
    if( $side eq "left" ) {
      $side = "right";
    } else {
      $side = "left";
    }
  }

  return $side;
}
