# Station.pm - WWW::Velib::Station
#
# Copyright (c) 2007 David Landgren
# All rights reserved

package WWW::Velib::Station;
use strict;

use LWP::Simple;
use Math::Trig qw(deg2rad great_circle_distance);
use XML::Twig;

use vars '$VERSION';
$VERSION = '0.01';

use constant DETAILS => 'http://www.velib.paris.fr/service/stationdetails/';

sub new {
    my $class = shift;
    my $self  = bless {number => shift}, $class;
    $self->refresh;
    return $self;
}

sub make {
    my $class = shift;
    my $self  = {};
    @{$self}{qw(number name address fullAddress lat lng open)} = @_;
    @{$self}{qw(theta phi)} = _theta_phi(@{$self}{qw(lat lng)});
    return bless $self, $class;
}

sub _theta_phi { deg2rad($_[0]), deg2rad(90 - $_[1]) }

sub load_v1 {
    my $class = shift;
    my $self  = {};
    @{$self}{qw(number open lat lng theta phi name address fullAddress)} = @_;
    return bless $self, $class;
}

sub coords {
    my $self = shift;
    return @{$self}{qw(theta phi)};
}

sub distance_from {
    my $self  = shift;
    my $there = shift;
    my $scale = shift || 5;
    $self->{dist} = $scale * sprintf( '%0.0f',
            great_circle_distance( $there->coords, $self->coords, 6378249)
        / $scale);
    return $self->{dist};
}

sub refresh {
    my $self = shift;
    if (my $content = get(DETAILS . $self->number)) {
        $self->{_html} = $content;
        my $twig = XML::Twig->new(
            twig_handlers => {
                available => sub {$self->{available} = $_[1]->text},
                free      => sub {$self->{free     } = $_[1]->text},
                total     => sub {$self->{total    } = $_[1]->text},
                ticket    => sub {$self->{ticket   } = $_[1]->text},
            },
        );
        $twig->parse($content);
    }
    else {
        @{$self}{qw(available free total ticket)} = (-1) x 4;
    };
    return $self;
}

sub number       {return $_[0]->{number     }}
sub open         {return $_[0]->{open       }}
sub name         {return $_[0]->{name       }}
sub latitude     {return $_[0]->{lat        }}
sub longitude    {return $_[0]->{lng        }}
sub address      {return $_[0]->{address    }}
sub full_address {return $_[0]->{fullAddress}}
sub available    {return $_[0]->{available  }}
sub free         {return $_[0]->{free       }}
sub total        {return $_[0]->{total      }}
sub disabled  {
    return $_[0]->total == -1
        ? -1
        : $_[0]->total - ($_[0]->free + $_[0]->available)
    ;
}

'The Lusty Decadent Delights of Imperial Pompeii';
__END__

=head1 NAME

WWW::Velib::Station - Details of Velib' station bicycle and parking availability

=head1 VERSION

This document describes version 0.01 of WWW::Velib::Station, released
2007-11-13.

=head1 SYNOPSIS

  use WWW::Velib::Station;

  my $s = WWW::Velib::Station->new(2007);
  print $s->available, $/; # hopefully a positive number

=head1 DESCRIPTION

=head1 METHODS

=over 4

=item new

Create a WWW::Velib::Station object. A single input parameter
is given, representing the station number.

=item make

Create a WWW::Velib::Station object, based on the geographic
information provided by WWW::Velib::Map. Not expected to be called
from client code.

=item load_v1

Create a WWW::Velib::Station object, based on locally-cached contents
of the geographic information provided by WWW::Velib::Map. Not
expected to be called from client code.

=item coords

Returns a two-element list containing the theta and phi coordinates
of the station.

=item distance_from

Returns the distance in metres of a station from the current station.
The result is rounded by default to the nearest 5 metres.

    my $depart; # two WWW::Velib::Station objects
    my $arrive;
    my $distance = $depart->distance_from($arrive);

To round off to another interval, specify the rounding as a second
parameter:

    my $dist_km = $depart->distance_from($arrive, 1000);

=item number

Returns the number (indentifier) of the station.

=item open

Indicates whether the station is open for business or not.

=item name

Returns the name of the station.

=item latitude

Returns the station's latitude, in degrees.

=item longitude

Returns the station's longitude, in degrees.

=item address

Returns the short address of the station.

=item full_address

Returns the full address of the station.

=item refresh

If a station has been built by loading a map, the following details
will not be loaded (it takes time to fetch a couple of thousand web
pages). This method will fetch the current status of the station
(bikes available, slots available). On a long-running process, may
be called repeatedly (at a suitable interval) to update the status.

=item available

Returns the number of bicycles available at the specified station.

=item total

Returns the total number of bicycle posts installed at the station.

=item free

Returns the number of bicycle posts that are able to receive a
bicycle.

=item disabled

Returns the number of bicycle posts that are locked or have a
locked bicycle attached.

=back

=head1 AUTHOR

David Landgren, copyright (C) 2007. All rights reserved.

http://www.landgren.net/perl/

If you (find a) use this module, I'd love to hear about it. If you
want to be informed of updates, send me a note. You know my first
name, you know my domain. Can you guess my e-mail address?

=head1 LICENSE

This library is free software; you can redistribute it and/or modify
it under the same terms as Perl itself.