#!/usr/bin/perl
# CellPicGPS v1.1
# Copyright (c) 2009 Fotis Loukos <fotisl@gmail.com>
# Retrieve coordinates for pictures with EXIF cellid information.
# This program is released under the terms of the MIT license.

use Getopt::Long;
use LWP;
use JSON;
use Image::ExifTool qw(:Public);
use Data::Dumper;

my $VERSION = "1.1";

sub usage {
    print "Usage: $0 [OPTION] <input>\n";
    print "Where: input is the input image.\n\n";
    print "Valid options are:\n";
    print "\t-service srv   Select service to use for retrieving coordinates\n";
    print "\t-listsrv       List all valid services and exit\n";
    print "\t-gmaps         Dump a google maps url\n";
    print "\t-help          This help\n";
    print "\t-version       Output version and exit\n";
    exit;
}

# Hidden google maps API
sub getloc_google {
    my ($cid, $lac, $mnc, $mcc) = @_;

    my @start = (
        0x00, 0x0E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        0x00, 0x00, 0x00, 0x00, 0x1B, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00 );
    my @end = (
        0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00 );

    my $ua = LWP::UserAgent->new;
    $ua->agent("Mozilla/4.0 (compatible; MSIE 5.5; Windows NT)");

    my $response = $ua->post("http://www.google.com/glm/mmap", Content =>
            pack("C*", @start, unpack("c*", pack("N", $cid)),
            unpack("c*", pack("N", $lac)), unpack("c*", pack("N", $mnc)),
            unpack("c*", pack("N", $mcc)), @end));

    my @res = unpack("C*", $response->content);

    my $lat = $res[7] << 24 | $res[8] << 16 | $res[9] << 8 | $res[10];
    my $lon = $res[11] << 24 | $res[12] << 16 | $res[13] << 8 | $res[14];
    my $acc = $res[15] << 24 | $res[16] << 16 | $res[17] << 8 | $res[18];

    return ($lat / 1000000, $lon / 1000000, $acc);
}

sub getloc_googlejson {
    my ($cid, $lac, $mnc, $mcc) = @_;

    my %request;
    $request{"version"} = "1.1.0";
    $request{"host"} = "maps.google.com";
    $request{"home_mobile_country_code "} = $mcc;
    $request{"home_mobile_network_code"} = $mnc;
    $request{"radio_type"} = "gsm";

    my @cells;
    my %cell;
    $cell{"cell_id"} = $cid;
    $cell{"location_area_code"} = $lac;
    $cell{"mobile_country_code"} = $mcc;
    $cell{"mobile_network_code"} = $mnc;

    push @cells, \%cell;

    $request{"cell_towers"} = \@cells;

    my $ua = LWP::UserAgent->new;
    $ua->agent("Mozilla/4.0 (compatible; MSIE 5.5; Windows NT)");
    my $response = $ua->post("http://www.google.com/loc/json", Content =>
            encode_json \%request);
    my $loc = decode_json $response->content;
    return ($loc->{location}->{latitude}, $loc->{location}->{longitude},
            $loc->{location}->{accuracy});
}

sub getloc_opencellid {
    my ($cid, $lac, $mnc, $mcc) = @_;

    my $ua = LWP::UserAgent->new;
    $ua->agent("Mozilla/4.0 (compatible; MSIE 5.5; Windows NT)");

    my $url = "http://www.opencellid.org/cell/get?mcc=$mcc&mnc=$mnc" .
        "&cellid=$cid&lac=$lac";

    my $response = $ua->get($url);

    my ($lat) = $response->content =~ /lat="([.0-9]+)"/s;
    my ($lon) = $response->content =~ /lon="([.0-9]+)"/s;
    my ($acc) = $response->content =~ /range="([.0-9]+)"/s;

    return ($lat, $lon, $acc);
}

my %services = (
    "google"      => \&getloc_google,
    "googlejson"  => \&getloc_googlejson,
    "opencellid"  => \&getloc_opencellid
);



my $service = "google";
my $gmaps = 0, $listsrv = 0, $showusage = 0, $version = 0;
my $res = GetOptions("service=s" => \$service,
                     "listsrv" => \$listsrv,
                     "gmaps" => \$gmaps,
                     "help" => \$showusage,
                     "version" => \$version);

usage() if($showusage);

if($version) {
    print "CellPicGPS v$VERSION\n";
    print "Copyright (c) 2009 Fotis Loukos <fotisl\@gmail.com>\n";
    print "Retrieve coordinates for pictures with EXIF cellid information.\n";
    print "This program is released under the terms of the MIT license.\n";
    exit;
}

if($listsrv) {
    print "Valid services:\n";
    for $srv (keys %services) {
        print "\t$srv\n";
    }
    exit;
}

if(!exists $services{$service}) {
    print "Invalid service! Please use -listsrv to see all valid services.\n";
    exit;
}

my $img = shift;
usage() if(!$img);

if(! -r $img) {
    print "Cannot open file $img for reading.\n";
    exit;
}

my $exifTool = new Image::ExifTool;
$exifTool->ExtractInfo($img);

my %cellinfo;

foreach my $tag ("Cellid", "Lac", "Mnc", "Mcc") {
    my $val = $exifTool->GetValue($tag);
    if(ref $val eq "SCALAR") {
        print "Cannot get $tag from image.\n";
        exit;
    } else {
        $cellinfo{$tag} = $val;
    }
}

foreach my $tag ("Cellid", "Lac", "Mnc", "Mcc") {
    print "$tag:\t" . $cellinfo{$tag} . "\n";
}

print "Using service: $service\n";

my ($lat, $lon, $acc) = &{$services{$service}}($cellinfo{"Cellid"}, $cellinfo{"Lac"},
        $cellinfo{"Mnc"}, $cellinfo{"Mcc"});

print "Latitude:  $lat\n";
print "Longitude: $lon\n";
print "Accuracy: $acc\n";

if($gmaps) {
    print "Google Maps URL: http://maps.google.com/maps?q=$lat+$lon&z=10\n";
}

