package jp.agentec.abook.abv.cl.util;

import java.util.Date;
import java.util.Timer;
import java.util.TimerTask;

import jp.agentec.abook.abv.bl.common.log.Logger;
import jp.agentec.abook.abv.launcher.android.ABVApplication;
import jp.agentec.abook.abv.launcher.android.ABVUIDataCache;
import jp.agentec.abook.abv.launcher.android.R;
import jp.agentec.abook.abv.ui.common.dialog.ABookAlertDialog;
import jp.agentec.abook.abv.ui.common.util.AlertDialogUtil;
import android.content.ActivityNotFoundException;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.location.Location;
import android.location.LocationListener;
import android.location.LocationManager;
import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
import android.provider.Settings;

public class LocationManagerUtil {
	private final String TAG = "LocationManagerUtil";
	private long SIGNIFICANTLY_NEWER = 5 * 60 * 1000L;
	private final long SERVICE_TIMEOUT = 1000L * 10;
	private final long TIMER_INTERVAL = 1000L;
	private final long TIMER_DELAY = 0L;

	private LocationManager locationManager;
	private Context context;
	private LocationListener gpsLocationListener;
	private LocationListener networkLocationListener;
	private LocationManagerUtilListener listener;

	private Location currentLocation;
	private Timer locationTimer;
	private long time;

	public LocationManagerUtil(Context context, LocationManagerUtilListener listener) {
		this.context = context;
		this.listener = listener;
	}

	public interface LocationManagerUtilListener {
		void onGetLocation(Location location);
		void onGetLocationFailed();
	}

	/**
	 * GPSが有効かチェックする
	 * @return 有効の場合true
	 */
	public boolean isLocationGpsEnabled() {
		locationManager = (LocationManager) context.getSystemService(Context.LOCATION_SERVICE);
		if (locationManager == null) {
			return false;
		}
		boolean gps = locationManager.isProviderEnabled(LocationManager.GPS_PROVIDER);
		boolean secureLocationGpsEnabled;

		if (Build.VERSION.SDK_INT < 31) {
			// GPSの状態を取得(getSystemtServiceからのGPS ON/OFF取得が取れない場合があるため、secureで取得したgpsも判定するため)
			secureLocationGpsEnabled = android.provider.Settings.Secure.getString(context.getContentResolver(), Settings.Secure.LOCATION_PROVIDERS_ALLOWED).contains("gps");
		} else {
			secureLocationGpsEnabled = gps;
		}
		if (!gps || !secureLocationGpsEnabled) {
			return false;
		}
		return true;
	}

	/**
	 * NetWorkによる位置測定が有効かチェックする
	 * @return 有効の場合true
	 */
	public boolean isLocationNetWorkEnabled() {
		locationManager = (LocationManager) context.getSystemService(Context.LOCATION_SERVICE);
		if (locationManager == null) {
			return false;
		}
		boolean network = locationManager.isProviderEnabled(LocationManager.NETWORK_PROVIDER);
		boolean secureLocationNetWorkEnabled;

		if (Build.VERSION.SDK_INT < 31) {
			// GPSの状態を取得(getSystemtServiceからのGPS ON/OFF取得が取れない場合があるため、secureで取得したgpsも判定するため)
			secureLocationNetWorkEnabled = android.provider.Settings.Secure.getString(context.getContentResolver(), Settings.Secure.LOCATION_PROVIDERS_ALLOWED).contains("network");
		} else {
			secureLocationNetWorkEnabled = network;
		}
		if (!network || !secureLocationNetWorkEnabled) {
			return false;
		}
		return true;
	}

	/**
	 * 位置情報の取得を開始します<br>
	 * 成功時にlistenerのonGetLocation、失敗時にlistenerのonGetLocationFailedが呼び出されます。
	 *
	 */
	public void startLocationService() {
		stopLocationService();

		currentLocation = null;
		locationManager = (LocationManager) context.getSystemService(Context.LOCATION_SERVICE);

		if (locationManager == null) {
			// 位置情報機能非搭載端末の場合、位置情報取得失敗とする
			setLocationFailed();
			return;
		}
		// 位置情報サービスが有効か？
		boolean isGpsEnabled = isLocationGpsEnabled();
		boolean isNetWorkEnabled = isLocationNetWorkEnabled();
		if (!(isGpsEnabled || isNetWorkEnabled)) {
			// この時点で位置情報サービスが有効でない場合は何もしない。
			setLocationFailed();
			return;
		}

		final Location lastKnownLocation = getLastKnownLocation();
		if (lastKnownLocation != null) {
			// 最後に取得できた位置情報があれば、とりあえず設定します。
			updateLocation(lastKnownLocation, true);

			// 最後に取得できた位置情報が5分以内のものであれば、これ以上処理を実行しません。
			if ((new Date().getTime() - lastKnownLocation.getTime()) < SIGNIFICANTLY_NEWER) {
				return;
			}
		}


		time = 0L;
		// 位置情報取得の生存時間を決定するタイマーを起動します。
		final Handler handler = new Handler();
		locationTimer = new Timer(true);
		locationTimer.scheduleAtFixedRate(new TimerTask() {
			@Override
			public void run() {
				handler.post(new Runnable() {
					@Override
					public void run() {
						if (time > SERVICE_TIMEOUT) {
							Logger.w(TAG, "time > SERVICE_TIMEOUT");
							setLocationFailed();
							return;
						}
						time = time + TIMER_INTERVAL;
					}
				});
			}
		}, TIMER_DELAY, TIMER_INTERVAL);

		// 位置情報の取得を開始します。
		if (isGpsEnabled) {
			gpsLocationListener = new LocationListener() {
				@Override
				public void onLocationChanged(final Location location) {
					if (isBetterLocation(currentLocation, location)) {
						updateLocation(location, false);
					}
				}

				@Override
				public void onProviderDisabled(final String provider) {
				}

				@Override
				public void onProviderEnabled(final String provider) {
				}

				@Override
				public void onStatusChanged(final String provider, final int status, final Bundle extras) {
				}
			};
			locationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, 0, 0, gpsLocationListener);
		}

		if (isNetWorkEnabled) {
			networkLocationListener = new LocationListener() {
				@Override
				public void onLocationChanged(final Location location) {
					if (isBetterLocation(currentLocation, location)) {
						updateLocation(location, false);
					}
				}

				@Override
				public void onProviderDisabled(final String provider) {
				}

				@Override
				public void onProviderEnabled(final String provider) {
				}

				@Override
				public void onStatusChanged(final String provider, final int status, final Bundle extras) {
				}
			};
			locationManager.requestLocationUpdates(LocationManager.NETWORK_PROVIDER, 0, 0, networkLocationListener);
		}

	}

	private Location getLastKnownLocation() {
		return getLastKnownLocation(new String[] { LocationManager.GPS_PROVIDER, LocationManager.NETWORK_PROVIDER });
	}

	private Location getLastKnownLocation(final String[] providers) throws IllegalArgumentException {
		if (locationManager == null || providers == null) {
			return null;
		}

		Location result = null;
		for (final String provider : providers) {
			if (provider != null && locationManager.isProviderEnabled(provider)) {
				final Location lastKnownLocation = locationManager.getLastKnownLocation(provider);
				if (isBetterLocation(result, lastKnownLocation)) {
					result = lastKnownLocation;
				}
			}
		}
		return result;
	}

	/**
	 * 第一引数の位置情報に比べ、第二引数の位置情報がより有効であるか判定します。
	 *
	 * @param currentLocation
	 * @param newLocation
	 * @return
	 */
	public boolean isBetterLocation(final Location currentLocation, final Location newLocation) {
		if (newLocation == null) {
			// 新しい位置情報が null の場合は常に無効と判断します。
			return false;
		}
		if (currentLocation == null) {
			// 現在の位置情報が null の場合は常に有効と判断します。
			return true;
		}

		// Check whether the new location fix is newer or older
		final long timeDelta = newLocation.getTime() - currentLocation.getTime();
		if (timeDelta > SIGNIFICANTLY_NEWER) {
			return true;
		} else if (timeDelta < SIGNIFICANTLY_NEWER) {
			return false;
		}

		final int accuracyDelta = (int) (newLocation.getAccuracy() - currentLocation.getAccuracy());
		if (accuracyDelta < 0) {
			return true;
		} else if (timeDelta > 0 && accuracyDelta <= 0) {
			return true;
		} else if (timeDelta > 0 && accuracyDelta <= 200 && isSameProvider(newLocation.getProvider(), currentLocation.getProvider())) {
			return true;
		}
		return false;
	}

	public static boolean isSameProvider(final String provider1, final String provider2) {
		if (provider1 == null) {
			return provider2 == null;
		}
		return provider1.equals(provider2);
	}

	void updateLocation(final Location location, final boolean lastKnownLocation) {
		currentLocation = location;
		setLocation(location);
	}

	/**
	 * 端末の位置情報サービスが無効の場合アラートを表示します
	 */
	public void showLoactionServiceAlert() {
		locationManager = (LocationManager) context.getSystemService(Context.LOCATION_SERVICE);
		if (locationManager != null) {
			// 位置情報サービスが有効か？
			boolean isGpsEnabled = isLocationGpsEnabled();
			boolean isNetWorkEnabled = isLocationNetWorkEnabled();

			if (!(isGpsEnabled || isNetWorkEnabled)) {
				ABVUIDataCache appDataCache = ABVApplication.getABVUIDataCache(context);
				if (appDataCache.checkLocationServiceFlg) {
					// 位置情報が有効になっていない場合は、Google Maps アプリライクなダイアログを起動します。
					ABookAlertDialog dialog = AlertDialogUtil.createAlertDialog(context, R.string.location_services_disabled);
					dialog.setMessage(R.string.location_services_disabled_message);
					dialog.setPositiveButton(R.string.setting, new DialogInterface.OnClickListener() {
						@Override
						public void onClick(final DialogInterface dialog, final int which) {
							try {
								// 端末の位置情報設定画面へ遷移
								context.startActivity(new Intent("android.settings.LOCATION_SOURCE_SETTINGS"));
							} catch (final ActivityNotFoundException e) {
								// 位置情報設定画面がない場合、位置情報取得失敗とする
								Logger.d(TAG, "android.settings.LOCATION_SOURCE_SETTINGS failed.", e);
								setLocationFailed();
							}
						}
					});
					dialog.setNegativeButton(R.string.cancel, null);
					dialog.show();
					appDataCache.checkLocationServiceFlg = false;
					stopLocationService();
				}
			}
		}
	}

	public void stopLocationService() {
		if (locationManager != null) {
			if (locationTimer != null) {
				locationTimer.cancel();
				locationTimer.purge();
				locationTimer = null;
			}
			if (networkLocationListener != null) {
				locationManager.removeUpdates(networkLocationListener);
				networkLocationListener = null;
			}
			if (gpsLocationListener != null) {
				locationManager.removeUpdates(gpsLocationListener);
				gpsLocationListener = null;
			}
		}
	}

	private void setLocation(final Location location) {
		stopLocationService();
		Logger.d(TAG, "Location Latitude:" + location.getLatitude() + ", Longitude:" + location.getLongitude());
		if (listener != null) {
			listener.onGetLocation(location);
		}
	}

	private void setLocationFailed() {
		stopLocationService();
		if (listener != null) {
			listener.onGetLocationFailed();
		}
	}

	/**
	 * GPSが有効でかどうかをチェックします。
	 * @param context アプリのcontext
	 * @return 有効の場合はtrue
	 */
	public static boolean isLocationGpsEnabled(Context context) {
		LocationManager locationManager = (LocationManager) context.getSystemService(Context.LOCATION_SERVICE);
		if (locationManager == null) {
			return false;
		}
		boolean gpsEnabled = locationManager.isProviderEnabled(LocationManager.GPS_PROVIDER);
		boolean secureLocationGpsEnabled;

		if (Build.VERSION.SDK_INT < 31) {
			// GPSの状態を取得(getSystemtServiceからのGPS ON/OFF取得が取れない場合があるため、secureで取得したgpsも判定するため)
			secureLocationGpsEnabled = android.provider.Settings.Secure.getString(context.getContentResolver(), Settings.Secure.LOCATION_PROVIDERS_ALLOWED).contains("gps");
		} else {
			secureLocationGpsEnabled = gpsEnabled;
		}
		if (!(gpsEnabled || secureLocationGpsEnabled)) {
			return false;
		}
		return true;
	}
}
