package Geo::EZLocate;

use 5.006;
use strict;
use warnings;

use Geo::EZLocate::Interfaces::Authentication::Authentication;
use Geo::EZLocate::Interfaces::Geocoding::Geocoding;

=head1 NAME

Geo::EZLocate - TomTom EZLocate geocoding service interface

=head1 VERSION

Version 0.01

=cut

our $VERSION = '0.01';

our $timeout = 10; # in minutes

=head1 SYNOPSIS

    use Geo::EZLocate;

    my $geo = Geo::EZLocate->new();
    my $result = $geo->login('myusername', 'mypassword');
    $
    ...

=head1 METHODS

=head2 login USERNAME PASSWORD

Authenticates and caches a login token.  Returns the result code, which 
is zero on success and nonzero on failure.

=cut

# shorthand class names

my $Interfaces = 'Geo::EZLocate::Interfaces';
my $Auth = $Interfaces.'::Authentication::Authentication';
my $EZClient = $Interfaces.'::EZClient::EZClient';
my $Geocoding = $Interfaces.'::Geocoding::Geocoding';

sub new {
  my $class = shift;
  # mimic the official API
  my $username = shift;
  my $password = shift;
  return bless { username => $username, password => $password, @_ },
         'Geo::EZLocate';
}

sub login {
  my $self = shift;

  if ( $self->{identity} and (time - $self->{authtime}) < $timeout ) {
    return; # no need to reauth
  }

  my ($username, $password) = @_;
  if ( $username ) {
    $self->{username} = $username;
    $self->{password} = $password;
  }

  # preprocess key
  my $key = 0;
  for (split(//, $self->{password})) {
    $key = ($key << 4) + ord($_);
    my $bits = $key & 0xf0000000;
    $bits >>= 24;
    $key = ($key ^ $bits);
  }
  $key = $key & 0x0fffffff;

  my $client = $self->{'auth'} ||= $Auth->new;
  my $r_requestChallenge = $client->requestChallenge({
      userName => $self->{username},
      minutesValid => $timeout,
  });
  my $eid = $r_requestChallenge->get_encryptedID;

  # XOR $eid with key, multiply it by the magic number, XOR it back
  my $ans = $eid ^ $key;
  $ans = ($ans * 39371) % 0x3fffffff;
  $ans = $ans ^ $key;

  my $r_answerChallenge = $client->answerChallenge({
      originalChallenge => $eid,
      encryptedResponse => $ans,
  });

  my $id = $r_answerChallenge->get_credential;
  if ( $id ) {
    $self->{identity} = $id;
    $self->{authtime} = time;
  }
  return $r_answerChallenge->get_resultCode;
}

=head2 findAddress ADDRESS, CITY, STATE, ZIP, [OPTION => VALUE] ...

Validate the specified address.  The only OPTION that matters currently
is 'country', which can be set to "CA" to search for addresses in Canada.

=cut

sub findAddress {
  my $self = shift;
  my %params;
  # again, imitate the positional parameters in the official SDK
  $params{Addr} = shift;
  $params{City} = shift;
  $params{State} = shift;
  $params{ZIP} = shift;
  my %opt = @_;

  my $client = $self->{geocoding} ||= $Geocoding->new;
  $self->login();
  die "authentication failed" unless $self->{identity};

  my $service = 'USA_Geo_004';
  if ( lc($opt{'country'} || '') eq 'ca' ) {
    $service = 'CAN_Geo_001';
  }
  my $input = {
    nv => [ map { +{ name => $_, value => $params{$_} } } keys %params ]
  };
  my $r_findAddress = $client->findAddress({
    'service'   => $service,
    'identity'  => $self->{identity},
    'input'     => $input,
  });

  my $nvs = $r_findAddress->get_result->get_mAttributes->get_nv;

  my $match = {};
  foreach my $nv (@$nvs) {
    $match->{$nv->get_name} = '' . $nv->get_value; #force stringification
  }

  $match;
}

=head1 AUTHOR

Mark Wells, C<< <mark at freeside.biz> >>

=head1 BUGS

Much of the API is unsupported.  The Geo::EZLocate::Interfaces::*
packages have autogenerated wrappers for all of the API methods, and
they work, but I haven't documented or tested most of them.

This module shouldn't even be necessary, but the EZLocate SDK has a 
restrictive license which makes it impractical for open-source 
development.  This is a shame, because their geocoding service is 
very good.

=head1 SUPPORT

You can find documentation for this module with the perldoc command.

    perldoc Geo::EZLocate

This library is not supported by TomTom.  Commercial support is available
from Freeside Internet Services, L<http://www.freeside.biz>.

=head1 LICENSE AND COPYRIGHT

Copyright 2012 Mark Wells.

This program is free software; you can redistribute it and/or modify it
under the terms of either: the GNU General Public License as published
by the Free Software Foundation; or the Artistic License.

See http://dev.perl.org/licenses/ for more information.

=cut

1; # End of Geo::EZLocate
