package jp.unsf.ics.soft.jig;

import static java.lang.Math.abs;
import static java.lang.Math.acos;
import static java.lang.Math.cos;
import static java.lang.Math.sin;

import java.util.Calendar;
import java.util.Locale;

import android.app.Activity;
import android.app.Notification;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.app.Service;
import android.content.Intent;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.location.Location;
import android.location.LocationListener;
import android.location.LocationManager;
import android.location.LocationProvider;
import android.os.Bundle;
import android.os.Handler;
import android.support.v4.app.NotificationCompat;
import android.util.Log;
import android.view.View;

public class LocationTestActivity extends Activity implements LocationListener {

	int calcInterval = 1;
	int measureInterval = 1;
	volatile Location nowLoc = null;
	int ave[] = { 30, 45, 60, 90, 120 };
	SizedConcurrentLinkedQueue<Location> listLoc = new SizedConcurrentLinkedQueue<Location>(ave[ave.length - 1] / measureInterval + 5);
	private LocationManager gpsLocationManager;
	private LocationManager netLocationManager;
	//static SimpleDateFormat sdf = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss", Locale.JAPAN);

	MainApplication app;

	@Override
	public void onCreate(Bundle savedInstanceState) {
		Log.d("LocationTestActivity.onCreate", "create1");
		
		super.onCreate(savedInstanceState);

		// 念のため常に初期化する
		app = (MainApplication) this.getApplication();
		setContentView(R.layout.main);

		// 2重起動防止
		if (!app.getFirstTicket(this)) {
			finish();
			return;
		}
		
		Log.d("LocationTestActivity.onCreate", "create2");
		
		// GPSとNETWORKの位置検索プロバイダの作成と定期的取得依頼
		gpsLocationManager = (LocationManager) getSystemService(LOCATION_SERVICE);
		netLocationManager = (LocationManager) getSystemService(LOCATION_SERVICE);
		if (gpsLocationManager != null) {
			gpsLocationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, measureInterval * 1000 * 0, 1, this);
		}else{
			Log.w("LocationTestActivity.onCreate", "gpsLocationManager is NULL.");
		}
		if (netLocationManager != null) {
			netLocationManager.requestLocationUpdates(LocationManager.NETWORK_PROVIDER, measureInterval * 1000 * 0, 1, this);
		}else{
			Log.w("LocationTestActivity.onCreate", "netLocationManager is NULL.");
		}

		// 別スレッド実行処理は、綺麗に止められない事があるからワンショットオンリーで実行する
		if (app.getOneShotTicket(this.getClass().getName() + "@THREAD")) {
			new Handler().postDelayed(calcFunc, 1);
			new Handler().postDelayed(measureFunc, 1);
		} else {
			Log.e("LocationTestActivity.onCreate", "Cannot run thread. Need rerun.");
		}
	}

	@Override
	public void onDestroy() {
		// 位置検索プロバイダに定期的情報取得休止のお知らせを送る
		if (gpsLocationManager != null) {
			gpsLocationManager.removeUpdates(this);
		}
		if (netLocationManager != null) {
			netLocationManager.removeUpdates(this);
		}
		Log.d("LocationTestActivity.onDestroy","destroy");
		
		// 2重起動防止解除
		app.disposeTicket(this);
		
		super.onDestroy();
	}

	@Override
	protected void onResume() {
		super.onResume();
	}

	@Override
	protected void onPause() {
		// 携帯がスリープしようが、内部では位置情報取得を継続して電力消費する。
		super.onPause();
	}


	@Override
	public void onLocationChanged(Location location) {
		//if(nowLoc!=null) Log.d("LocationTestActivity.onLocationChanged:nowLoc", nowLoc.toString());
		//Log.d("LocationTestActivity.onLocationChanged:location", location.toString());
		synchronized (app.sync_) { // もしlocation.getTime()が古い情報でも、onchangeした時の情報が最新と捉える
			// たぶんここが、GPSとNETWORKどちらをどう信頼するかの肝。
			if (nowLoc == null
					|| (location.getAccuracy() < getDistance(location, nowLoc) 
							|| ((-0.5 < (location.getTime() - nowLoc.getTime())) && (location.getAccuracy() <= nowLoc.getAccuracy())) 
							|| ((0.0 <= (location.getTime() - nowLoc.getTime())) && location.getProvider().equals(nowLoc.getProvider())))) {
				nowLoc = location;
				Log.d("LocationTestActivity.onLocationChanged", "kakikae!");
			}
		}
		//Log.d("LocationTestActivity.onLocationChanged:nowLoc", nowLoc.toString());
	}

	@Override
	public void onProviderDisabled(String provider) {
		//superなし
	}

	@Override
	public void onProviderEnabled(String provider) {
		//superなし
	}

	@Override
	public void onStatusChanged(String provider, int status, Bundle extras) { 
		switch (status) {
			case LocationProvider.AVAILABLE:
				break;
			case LocationProvider.OUT_OF_SERVICE:
				break;
			case LocationProvider.TEMPORARILY_UNAVAILABLE:
				break;
		}
	}


	// 定期的に計算し、通知領域に表示するスレッド。一端実行すると止まらない。
	private final Runnable calcFunc = new Runnable() {
		@Override
		public void run() {
			//Log.d("calcFunc", "run");
			synchronized(app.sync_){
				String text = "";
				String text2 = "";
				boolean existAvedata = false;
				for (int i : ave) { // 平均速度取得間隔で、過去の測量結果を得る
					text += "/" + i;
					Location loc = listLoc.get(i / measureInterval);
					if (loc != null) {
						existAvedata = true;
						text2 += "/" + String.format(Locale.getDefault(), "%03.1f", getKmph(nowLoc, loc, i));
					} else {
						text2 += "/" + "-.-";
					}
				}
				if(existAvedata){
					text = text.substring(1) + "sec:" + text2.substring(1) + " km/h";
				} else if (nowLoc != null) { // まだ測量結果が十分にたまっていない、かつ現在の測量結果はある
					text = "Speed: " + String.format(Locale.getDefault(), "%03.1f km/h", nowLoc.getSpeed());
				} else {
					text = "計測中…"; // 1回もonChange発生しない場合。
				}
				String nowProvider = nowLoc == null ? "---" : nowLoc.getProvider();
				setIntent("平均速度", "Average speed (" + nowProvider.toUpperCase(Locale.US) + ")", text, false);// 通知領域表示
			}
			new Handler().postDelayed(calcFunc, calcInterval * 1000); // 自分を再度登録
		}
	};

	// 定期的に位置情報を格納するスレッド。一端実行すると止まらない。
	// calcFuncと並列実行されるため排他処理は慎重に。
	private final Runnable measureFunc = new Runnable() {
		@Override
		public void run() {
			//Log.d("measureFunc", "run");
			synchronized(app.sync_){
				if (nowLoc != null) { // measureInterval内の最新にこだわらず、最新データを利用する
					listLoc.add(nowLoc);
				}
			}
			new Handler().postDelayed(measureFunc, measureInterval * 1000); // 自分を再度登録
		}
	};

	
	Intent intent = null;
	PendingIntent contentIntent = null;
	Bitmap largeIcon = null;
	NotificationCompat.Builder builder = null;
	NotificationManager manager = null;
	
	protected void setIntent(String ticker, String title, String text, boolean notification) {
		synchronized(this){
			if(intent == null){ // 1回だけ作成
				intent = new Intent(LocationTestActivity.this, LocationTestActivity.class);
				//contentIntent = PendingIntent.getBroadcast(LocationTestActivity.this, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT);
				contentIntent = PendingIntent.getActivity(LocationTestActivity.this, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT);
				largeIcon = BitmapFactory.decodeResource(getResources(), R.drawable.ic_launcher); // アンドロイド山田電機君でいいのだろうか！
				 builder = new NotificationCompat.Builder(getApplicationContext());
				 manager = (NotificationManager) getSystemService(Service.NOTIFICATION_SERVICE);
			}
		}
		builder.setContentIntent(contentIntent);
		builder.setTicker(ticker);
		builder.setSmallIcon(R.drawable.ic_launcher);
		builder.setContentTitle(title);
		builder.setContentText(text);
		builder.setLargeIcon(largeIcon);
		builder.setWhen(0); // 付けると更新時明滅, 0で表示時刻表示無し
		builder.setOngoing(true);
		if (notification) { // ブザーうるせえ
			builder.setDefaults(Notification.DEFAULT_SOUND | Notification.DEFAULT_VIBRATE | Notification.DEFAULT_LIGHTS);
		}
		builder.setAutoCancel(false);// タップしても消さない
		manager.notify(0, builder.build());
	}
	
	// ２点間の速度（時速）を計算する。計算の時間間隔はLocationが持つ時刻を利用する。
	protected double getKmph(Location l1, Location l2) {
		return  getKmph(l1, l2, abs(l1.getTime() - l2.getTime()) / 1000.0);
	}

	// ２点間の速度（時速）を計算する。計算の時間間隔は秒で指定する。
	protected double getKmph(Location l1, Location l2, double interval) {
		if (interval == 0.0) {
			return 0.0;
		} else {
			return getDistance(l1, l2) * 60.0 * 60.0 / interval / 1000.0;
		}
	}
	
	// ２点間の距離を地殻に沿って計算する。地球は完全球体。単位はm
	private static double getDistance(Location l1, Location l2) {
		double theta = l1.getLongitude() - l2.getLongitude();
		double dist = rad2deg(acos(sin(deg2rad(l1.getLatitude())) * sin(deg2rad(l2.getLatitude())) + cos(deg2rad(l1.getLatitude())) * cos(deg2rad(l2.getLatitude())) * cos(deg2rad(theta)))) * 60 * 1.1515 * 1.609344;
		return dist * 1000.0;
	}

	// ラジアンを度に。
	private static double rad2deg(double radian) {
		return radian * (180f / Math.PI);
	}

	// 度をラジアンに。
	private static double deg2rad(double degrees) {
		return degrees * (Math.PI / 180f);
	}
	
	/*
	// デバッグ用
	private String getString(Location loc){
		StringBuilder sb = new StringBuilder();
		sb.append("種:");
		sb.append(loc.getProvider());
		sb.append(",");
		sb.append("緯:");
		sb.append(String.format(Locale.US, "%03.8f", loc.getLatitude()));
		sb.append(",");
		sb.append("経:");
		sb.append(String.format(Locale.US, "%03.8f", loc.getLongitude()));
		sb.append(",");
		sb.append("精:");
		sb.append(String.format(Locale.US, "%03.2f", loc.getAccuracy()));
		sb.append(",");
		sb.append("時:");
		sb.append(sdf.format(loc.getTime()));
		return sb.toString();
	}
	*/

	public void onClick(View v) {
		switch(v.getId()){
			case R.id.imageView1:
				break;
			case R.id.textView1: // Easter egg
				View imageVeew1 = this.getWindow().getDecorView().findViewById(R.id.imageView1);
				if(imageVeew1.getVisibility() == View.VISIBLE) imageVeew1.setVisibility(View.INVISIBLE); 
				else imageVeew1.setVisibility(View.VISIBLE);
				break;
			default:
				break;
		}  
	}
}