/* 
 * Requires Google Play Services 
 * Doesn't run on emulator 
*/

package course.examples.location.getlocationservices;

import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Locale;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;

import android.app.Activity;
import android.graphics.Color;
import android.location.Location;
import android.os.Bundle;
import android.util.Log;
import android.widget.TextView;

import com.google.android.gms.common.ConnectionResult;
import com.google.android.gms.common.GooglePlayServicesClient;
import com.google.android.gms.common.GooglePlayServicesUtil;
import com.google.android.gms.location.LocationClient;
import com.google.android.gms.location.LocationListener;
import com.google.android.gms.location.LocationRequest;

public class LocationGetLocationActivity extends Activity implements
		GooglePlayServicesClient.ConnectionCallbacks,
		GooglePlayServicesClient.OnConnectionFailedListener, LocationListener {

	private static final long ONE_MIN = 1000 * 60;
	private static final long TWO_MIN = ONE_MIN * 2;
	private static final long FIVE_MIN = ONE_MIN * 5;
	private static final long MEASURE_TIME = 1000 * 30;
	private static final long POLLING_FREQ = 1000 * 10;
	private static final long FASTES_UPDATE_FREQ = 1000 * 2;
	private static final float MIN_ACCURACY = 25.0f;
	private static final float MIN_LAST_READ_ACCURACY = 500.0f;

	// Define an object that holds accuracy and frequency parameters
	LocationRequest mLocationRequest;

	// Views for display location information
	private TextView mAccuracyView;
	private TextView mTimeView;
	private TextView mLatView;
	private TextView mLngView;

	private int mTextViewColor = Color.GRAY;

	// Current best location estimate
	private Location mBestReading;

	private final String TAG = "LocationGetLocationActivity";

	private boolean mFirstUpdate = true;
	private LocationClient mLocationClient;
	private Location mCurrentLocation;

	@Override
	public void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);

		if (!servicesAvailable())
			finish();

		setContentView(R.layout.main);

		mAccuracyView = (TextView) findViewById(R.id.accuracy_view);
		mTimeView = (TextView) findViewById(R.id.time_view);
		mLatView = (TextView) findViewById(R.id.lat_view);
		mLngView = (TextView) findViewById(R.id.lng_view);

		// Create new Location Client. This class will handle callbacks
		mLocationClient = new LocationClient(this, this, this);

		// Create and define the LocationRequest
		mLocationRequest = LocationRequest.create();

		// Use high accuracy
		mLocationRequest.setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY);

		// Update every 10 seconds
		mLocationRequest.setInterval(POLLING_FREQ);

		// Recieve updates no more often than every 2 seconds
		mLocationRequest.setFastestInterval(FASTES_UPDATE_FREQ);

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

		// Connect to LocationServices
		mLocationClient.connect();
	}

	@Override
	protected void onStop() {

		// Stop updates
		mLocationClient.removeLocationUpdates(this);

		// Disconnect from LocationServices
		mLocationClient.disconnect();

		super.onStop();
	}


	// Called back when location changes

	@Override
	public void onLocationChanged(Location location) {

		ensureColor();

		// Determine whether new location is better than current best
		// estimate

		if (null == mBestReading
				|| location.getAccuracy() < mBestReading.getAccuracy()) {

			// Update best estimate
			mBestReading = location;

			// Update display
			updateDisplay(location);

			if (mBestReading.getAccuracy() < MIN_ACCURACY)
				mLocationClient.removeLocationUpdates(this);

		}
	}

	@Override
	public void onConnected(Bundle dataBundle) {

		// Get first reading. Get additional location updates if necessary

		if (servicesAvailable()) {
			// Get best last location measurement meeting criteria
			mBestReading = bestLastKnownLocation(MIN_LAST_READ_ACCURACY,
					FIVE_MIN);

			// Display last reading information
			if (null != mBestReading) {

				updateDisplay(mBestReading);

			} else {

				mAccuracyView.setText("No Initial Reading Available");

			}

			if (null == mBestReading
					|| mBestReading.getAccuracy() > MIN_LAST_READ_ACCURACY
					|| mBestReading.getTime() < System.currentTimeMillis()
							- TWO_MIN) {

				mLocationClient.requestLocationUpdates(mLocationRequest, this);
				
				// Schedule a runnable to unregister location listeners
				Executors.newScheduledThreadPool(1).schedule(new Runnable() {

					@Override
					public void run() {

						mLocationClient.removeLocationUpdates(LocationGetLocationActivity.this);

					}
				}, MEASURE_TIME, TimeUnit.MILLISECONDS);
			}

			}
		}

	// Get the last known location from all providers
	// return best reading is as accurate as minAccuracy and
	// was taken no longer then maxAge milliseconds ago

	private Location bestLastKnownLocation(float minAccuracy, long maxAge) {

		Location bestResult = null;
		float bestAccuracy = Float.MAX_VALUE;
		long bestTime = Long.MIN_VALUE;

		// Get the best most recent location currently available
		mCurrentLocation = mLocationClient.getLastLocation();

		if (mCurrentLocation != null) {

			float accuracy = mCurrentLocation.getAccuracy();
			long time = mCurrentLocation.getTime();

			if (accuracy < bestAccuracy) {

				bestResult = mCurrentLocation;
				bestAccuracy = accuracy;
				bestTime = time;

			}
		}

		// Return best reading or null
		if (bestAccuracy > minAccuracy || (System.currentTimeMillis() - bestTime) > maxAge) {
			return null;
		} else {
			return bestResult;
		}
	}


	@Override
	public void onDisconnected() {

		Log.i(TAG, "Disconnected. Try again later.");

	}

	@Override
	public void onConnectionFailed(ConnectionResult connectionResult) {
		Log.i(TAG, "Connection Failed. Try again later.");
	}

	private boolean servicesAvailable() {

		// Check that Google Play Services are available
		int resultCode = GooglePlayServicesUtil
				.isGooglePlayServicesAvailable(this);

		// If Google Play services is available

		return (ConnectionResult.SUCCESS == resultCode);

	}

	// Update display
	private void updateDisplay(Location location) {

		mAccuracyView.setText("Accuracy:" + location.getAccuracy());

		mTimeView.setText("Time:"
				+ new SimpleDateFormat("MM/dd/yyyy HH:mm:ss", Locale
						.getDefault()).format(new Date(location.getTime())));

		mLatView.setText("Longitude:" + location.getLongitude());

		mLngView.setText("Latitude:" + location.getLatitude());

	}

	private void ensureColor() {
		if (mFirstUpdate) {
			setTextViewColor(mTextViewColor);
			mFirstUpdate = false;
		}
	}

	private void setTextViewColor(int color) {

		mAccuracyView.setTextColor(color);
		mTimeView.setTextColor(color);
		mLatView.setTextColor(color);
		mLngView.setTextColor(color);

	}

}