# -*- mode: perl; coding: utf-8 -*-
# keitairc/lib/plugins/00location_receiver
# 位置情報送信、のコールバック

# The line number (1 incremented) and filename below must be
# actual. see perlsyn.
# line 10 "keitairc/lib/plugins/00location_receiver"

use Net::HTTP;
use XML::Simple;
use Encode qw(is_utf8 _utf8_off decode);

# refits.cgk.affrc.go.jp を使ってgeocode逆変換
# ただし 2009 年から www.finds.jp に移転した
# see http://www.finds.jp/wsdocs/rgeocode/
# WGS84系を渡すこと
# と思っていたら「測地系は世界測地系(JGD2000)に基づいています」らしい
sub get_rgeocode_finds {
	my ($lat, $lon) = @_;
	my $s = Net::HTTP->new(Host => 'www.finds.jp') || return;
	$s->write_request(GET => "/ws/rgeocode.php?lat=$lat&lon=$lon");
	$s->read_response_headers();
	my $xml_code;
	for(;;){
		my $buf;
		my $n = $s->read_entity_body($buf, 1024);
		return unless defined $n;
		last unless $n;
		$xml_code .= $buf;
	}
	my $xml = XMLin($xml_code);
	if($xml->{status} ne '200'){
		return;
	}
	return $xml->{result}->{prefecture}->{pname} .
		$xml->{result}->{municipality}->{mname} .
		$xml->{result}->{local}->{section};
}

# maps.google.com を使ってgeocode逆変換
# WGS84系を渡すこと
sub get_rgeocode_google{
	my ($lat, $lon) = @_;
	my $s = Net::HTTP->new(Host => 'maps.google.com') || return;
	$s->write_request(
		'GET' => "/maps/geo?oe=utf-8&ll=$lat%2C$lon&output=xml&callback=gmap",
		'Accept-Language' => 'ja'
		);
	$s->read_response_headers();
	my $xml_code;
	for(;;){
		my $buf;
		my $n = $s->read_entity_body($buf, 1024);
		return unless defined $n;
		last unless $n;
		$xml_code .= $buf;
	}
	my $xml = XMLin($xml_code);
	if(defined $xml){
		if(defined $xml->{Response}){
			if(defined $xml->{Response}->{Placemark}){
				if(defined $xml->{Response}->{Placemark}->{p1}){
					return $xml->{Response}->{Placemark}->{p1}->{address};
				}
			}
		}
	}
	return;
}

sub get_rgeocode{
	my $s;
	if($::cf->rgeocode_server() eq 'refits' ||
	   $::cf->rgeocode_server() eq 'finds'){
		$s = get_rgeocode_finds(@_);
	}elsif($::cf->rgeocode_server() eq 'google'){
		$s = get_rgeocode_google(@_);
	}else{
		return;
	}
	if(is_utf8($s)){
		_utf8_off($s);
	}
	return $s;
}

sub dms_to_degree{
	my $dms = shift;
	my ($degree, $min, $sec, $secc) = ($dms =~ /^(\d+)\.(\d+)\.(\d+)\.(\d+)$/);
	$degree + $min/60 + $sec/3600 + $secc/360000;
}

sub wgs84_to_tokyo{
	my ($lat, $lon) = @_;
	return ($lat + 0.00010696*$lat - 0.000017467*$lat - 0.0046020,
		$lon + 0.000046047*$lon + 0.000083049*$lon - 0.010041);
}

sub tokyo_to_wgs84{
	my ($lat, $lon) = @_;
	return ($lat - 0.00010695*$lat + 0.000017464*$lat + 0.0046017,
		$lon - 0.000046038*$lon - 0.000083043*$lon + 0.010040);
}

# see http://labs.anoncom.net/others/GoogleMap/NoAjaxInterface.html
sub google_map_image{
	my ($wx, $wy, $format) = @_;
	my $gwx = sprintf('%0.6f', $wx);
	my $gwy = sprintf('%0.6f', $wy);
	$gwx =~ s/\.//;
	$gwy =~ s/\.//;
	my %format = (
		gif => 1,
		png => 2,
		jpeg => 3,
	);
	my $iconid = 15;
	my $zoom = 2000;
	my $width = 160;
	my $height = 240;
	sprintf('http://maps.google.com/mapdata?cc=JP&min_priority=1&w=%d&h=%d&latitude_e6=%d&longitude_e6=%d&zm=%d&Point=b&Point.latitude_e6=%d&Point.longitude_e6=%d&Point.iconid=%d&Point=e&image_format=%d',
		$width, $height,
		$gwx, $gwy, $zoom, $gwx, $gwy, $iconid, $format{$format});
}

$plugin = {
	name => 'location_receiver',

	action_imprementation => sub {
		my ($request, $name, $session_id, $param_string) = @_;
		my ($cid) = ($param_string =~ /^(\d+)/);
		my $uri = $request->uri();;
		$uri =~ s/.*\?//;
		my %h;
		for my $pair (split(/&/, $uri)){
			my ($k, $v) = split(/=/, $pair);
			$h{$k} = $v;
		}

		my $ci = new Keitairc::ClientInfo($request);
		my $view = new Keitairc::View($::cf, $ci);
		my $p;
		$p->{session_id} = $session_id;
		$p->{cid} = $cid;
		$p->{channel_compact} = $::ib->simple_escape($::ib->compact_channel_name($cid));

		# 測地系
		# 以下の2つの測地系の中から使用している測地系を示す。
		# wgs84 WGS84系: GPS測量で算出される座標系。数回の改定
		# により現在ではITRF座標系と実用上の差異はなくなってい
		# る。
		# tokyo 日本測地系: 測量法施行令第2条で定められた日本標
		# 準の測地系。
		# 備考: 引数内容としては"tokyo"という表示になるが、実際
		# の測地系はWGS84系のデータを使用。
		# from http://www.au.kddi.com/ezfactory/tec/spec/eznavi.html

		# NTT DoCoMo
		if($ci->is_docomo()){
			# iエリアはPOSTで来る
			# ACTN=OK&LAT=%2B35.44.27.996&LON=%2B139.35.37.932&GEO=wgs84&XACC=1&POSINFO=2
			my ($wx, $wy);
			my $posted = $request->content();
			my %posted;
			for my $pair (split(/&/, $posted)){
				my ($k, $v) = split(/=/, $pair);
				$posted{$k} = $v;
			}
			if(defined $posted{LAT}){
				$posted{LAT} =~ s/^%2b//i;
				$posted{LON} =~ s/^%2b//i;
				($wx, $wy) = (dms_to_degree($posted{LAT}), dms_to_degree($posted{LON}));
			}else{
				# iエリアじゃないからlcsかな
				# GET /loc.jsp?lat=%2B35.40.53.008&lon=%2B139.45.57.971&geo=WGS84&x-acc=3 
				# see http://www.utilz.jp/wiki/Gps
				unless(defined $h{lat}){
					$p->{error} = 1;
					$p->{error_lcs} = 1;
					return $view->render('location_receiver.html', $p);
				}
				$h{lat} =~ s/^%2b//i;
				$h{lon} =~ s/^%2b//i;
				($wx, $wy) = (dms_to_degree($h{lat}), dms_to_degree($h{lon}));
			}

			my ($tx, $ty) = wgs84_to_tokyo($wx, $wy);
			unless($p->{address} = get_rgeocode($wx, $wy)){
				$p->{error} = 1;
				$p->{rgeocode_invalid} = 1;
				return $view->render('location_receiver.html', $p);
			}
			$p->{wx} = $wx;
			$p->{wy} = $wy;
			$p->{tx} = $tx;
			$p->{ty} = $ty;
			$p->{map_image_url} = google_map_image($wx, $wy, 'gif');
			$p->{address} = decode('utf8', $p->{address});
			return $view->render('location_receiver.html', $p);
		}

		# Softbank
		# HTTPヘッダーのx-jphone-geocodeに位置情報が通知されます。
		# x-jphone-geocode: 354053%1A1394557%1A(住所がSJISでエ
		# ンコードされたもの) "%1A"(SUB)で区切られて lat, lon,
		# 住所(SJISでエンコード)の形式になります。住所はドキュ
		# メントを見る限りSJISでエスケープしたものとなっていま
		# すが、正しくデコードできない場合がありました。この情
		# 報は利用しない方が良いのかも知れません。
		# see http://www.utilz.jp/wiki/Gps
		if($ci->is_softbank()){
			my ($lat, $lon);
			if(defined $request->{_headers}->{'x-jphone-geocode'}){
				($lat, $lon) = split(/%1A/, $request->{_headers}->{'x-jphone-geocode'});
				# sigh
				$lat =~ s/^(\d\d)(\d\d)(\d\d)/$1.$2.$3.00/;
				$lon =~ s/^(\d\d\d)(\d\d)(\d\d)/$1.$2.$3.00/;
			}else{
				# GET /loc.jsp?pos=N35.40.53.00E139.45.57.97&geo=wgs84&x-acr=1 
				($lat, $lon) = ($h{pos} =~ /^N([\d.]+)E([\d.]+)$/);
			}
			my ($wx, $wy) = (dms_to_degree($lat), dms_to_degree($lon));
			my ($tx, $ty) = wgs84_to_tokyo($wx, $wy);
			unless($p->{address} = get_rgeocode($wx, $wy)){
				$p->{error} = 1;
				$p->{rgeocode_invalid} = 1;
				return $view->render('location_receiver.html', $p);
			}
			$p->{wx} = $wx;
			$p->{wy} = $wy;
			$p->{tx} = $tx;
			$p->{ty} = $ty;
			$p->{map_image_url} = google_map_image($wx, $wy, 'png');
			$p->{address} = decode('utf8', $p->{address});
			return $view->render('location_receiver.html', $p);
		}

		# see KDDI au: 技術情報 > 簡易位置情報
		# http://www.au.kddi.com/ezfactory/tec/spec/eznavi.html
		if($ci->is_ezweb()){
			# au W53S location の戻り例
			#   3/?datum=tokyo&unit=dms&lat=35.44.51.75&lon=139.35.15.0
			# au W53S gpsone の戻り例
			# /?ver=1&datum=0&unit=0&lat=%2b35.44.29.09&lon=%2b139.35.38.97&alt=71&time=20080114183222&smaj=116&smin=96&vert=46&majaa=24&fm=2
			$h{lat} =~ s/^%2b//i; # au GPSOneの場合
			$h{lon} =~ s/^%2b//i; # au GPSOneの場合
			my ($wx, $wy) = (dms_to_degree($h{lat}), dms_to_degree($h{lon}));
			my ($tx, $ty) = wgs84_to_tokyo($wx, $wy);
			unless($p->{address} = get_rgeocode($wx, $wy)){
				$p->{error} = 1;
				$p->{rgeocode_invalid} = 1;
				return $view->render('location_receiver.html', $p);
			}
			$p->{wx} = $wx;
			$p->{wy} = $wy;
			$p->{tx} = $tx;
			$p->{ty} = $ty;
			$p->{map_image_url} = google_map_image($wx, $wy, 'png');
			$p->{address} = decode('utf8', $p->{address});
			return $view->render('location_receiver.html', $p);
		}

		# http://developer.emnet.ne.jp/browser3-1.html
		# Example:
		# http://www.emobilemap.net/positioning.cgi?ver=MOPA-001-2001&pos=N35.44.33.156E135.22.33.124&geo=wgs84&x-acy=1
		if($ci->is_emobile()){
			unless(defined $h{pos}) {
				$p->{error} = 1;
				$p->{error_lcs} = 1;
				return $view->render('location_receiver.html', $p);
			}

			my ($wx, $wy);
			if($h{pos} =~ /N([0-9]+\.[0-9]+\.[0-9]+\.[0-9]+)E([0-9]+\.[0-9]+\.[0-9]+\.[0-9]+)/){
				($wx, $wy) = ($1, $2);
			}

			($wx, $wy) = (dms_to_degree($wx), dms_to_degree($wy));
			my ($tx, $ty) = wgs84_to_tokyo($wx, $wy);
			unless($p->{address} = get_rgeocode($wx, $wy)){
				$p->{error} = 1;
				$p->{rgeocode_invalid} = 1;
				return $view->render('location_receiver.html', $p);
			}
			$p->{wx} = $wx;
			$p->{wy} = $wy;
			$p->{tx} = $tx;
			$p->{ty} = $ty;
			$p->{map_image_url} = google_map_image($wx, $wy, 'gif');
			$p->{address} = decode('utf8', $p->{address});
			return $view->render('location_receiver.html', $p);
		}

		return $view->render('location_receiver.html', $p);
	}
};

1;
