# FixMyStreet:Map
# Adding the ability to have different maps on FixMyStreet.
#
# Copyright (c) 2010 UK Citizens Online Democracy. All rights reserved.
# Email: matthew@mysociety.org; WWW: http://www.mysociety.org/

package FixMyStreet::Map;

use strict;

use Module::Pluggable
  sub_name    => 'maps',
  search_path => __PACKAGE__,
  require     => 1;

# Get the list of maps we want and load map classes at compile time
my @ALL_MAP_CLASSES = allowed_maps();

use FixMyStreet::Gaze;
use mySociety::Locale;
use Utils;

=head2 allowed_maps

Returns an array of all the map classes that were found and that
are permitted by the config.

=cut

sub allowed_maps {
    my $cfg = FixMyStreet->config('MAP_TYPE') || [];
    $cfg = [ split /,/, $cfg ] unless ref $cfg eq 'ARRAY';
    my @allowed = @$cfg;
    push @allowed, 'OSM'; # OSM is always allowed
    @allowed = map { __PACKAGE__.'::'.$_ } @allowed;
    my %avail = map { $_ => 1 } __PACKAGE__->maps;
    return grep { $avail{$_} } @allowed;
}

=head2 reload_allowed_maps

Allows tests to override MAP_TYPE at run time.

=cut

sub reload_allowed_maps {
    @ALL_MAP_CLASSES = allowed_maps();
}

=head2 map_class

Sets the appropriate class given a query parameter string.
Returns the old map class, if any.

=cut

our $map_class;
our $map_cobrand;
sub set_map_class {
    my $arg = shift;
    my $str;
    if (ref $arg) {
        # A cobrand object
        $map_cobrand = $arg;
        $str = $arg->map_type;
    } else {
        $map_cobrand = undef;
        $str = $arg;
    }
    $str = __PACKAGE__.'::'.$str if $str;
    my %avail = map { $_ => 1 } @ALL_MAP_CLASSES;
    $str = $ALL_MAP_CLASSES[0] unless $str && $avail{$str};
    my $old_map_class = $map_class;
    $map_class = $str;
    return $old_map_class;
}

sub display_map {
    return $map_class->display_map(@_);
}

sub map_javascript {
    $map_class->map_javascript;
}

sub map_features {
    my ( $c, %p ) = @_;

    if ($p{bbox}) {
        @p{"min_lon", "min_lat", "max_lon", "max_lat"} = split /,/, $p{bbox};
    }

    if (defined $p{latitude} && defined $p{longitude}) {
        # TODO - be smarter about calculating the surrounding square
        # use deltas that are roughly 500m in the UK - so we get a 1 sq km search box
        my $lat_delta = 0.00438;
        my $lon_delta = 0.00736;
        $p{min_lon} = Utils::truncate_coordinate($p{longitude} - $lon_delta);
        $p{min_lat} = Utils::truncate_coordinate($p{latitude} - $lat_delta);
        $p{max_lon} = Utils::truncate_coordinate($p{longitude} + $lon_delta);
        $p{max_lat} = Utils::truncate_coordinate($p{latitude} + $lat_delta);
    } else {
        $p{longitude} = Utils::truncate_coordinate(($p{max_lon} + $p{min_lon} ) / 2);
        $p{latitude} = Utils::truncate_coordinate(($p{max_lat} + $p{min_lat} ) / 2);
    }

    my $report_age = $c->stash->{show_old_reports} ? undef : $c->cobrand->report_age;
    $p{report_age} = $report_age;

    $p{page} = $c->get_param('p') || 1;
    my $on_map = $c->cobrand->problems_on_map->around_map( $c, %p );
    my $pager = $c->stash->{pager} = $on_map->pager;
    $on_map = [ $on_map->all ];

    if ( $c->{stash}->{show_old_reports} ) {
        # if show_old_reports is on then there must be old reports
        $c->stash->{num_old_reports} = 1;
    } else {
        my $older = $c->cobrand->problems_on_map->around_map( $c, %p, report_age => undef, page => 1 );
        $c->stash->{num_old_reports} = $older->pager->total_entries - $pager->total_entries;
    }

    # if there are fewer entries than our paging limit on the map then
    # also return nearby entries for display
    my $nearby;
    if (@$on_map < $pager->entries_per_page && $pager->current_page == 1) {
        $p{limit} = 20;
        $p{ids} = [ map { $_->id } @$on_map ];
        $p{distance} = FixMyStreet::Gaze::get_radius_containing_population( $p{latitude}, $p{longitude} );
        $nearby = $c->model('DB::Nearby')->nearby($c, %p);
    }

    return ( $on_map, $nearby, $p{distance} );
}

sub click_to_wgs84 {
    return $map_class->click_to_wgs84(@_);
}

=head2 tile_xy_to_wgs84

Takes the tile x,y and converts to lat, lon. Legacy to deal with old URLs,
hence hard-coded things.

=cut

sub tile_xy_to_wgs84 {
    my ( $x, $y ) = @_;
    my $easting  = int( $x * (5000/31) + 0.5 );
    my $northing = int( $y * (5000/31) + 0.5 );
    my ( $lat, $lon ) = Utils::convert_en_to_latlon( $easting, $northing );
    return ( $lat, $lon );
}

1;
