package cgeo.geocaching;

import cgeo.geocaching.sensors.DirectionProvider;
import cgeo.geocaching.sensors.GeoDataProvider;
import cgeo.geocaching.sensors.GpsStatusProvider;
import cgeo.geocaching.sensors.GpsStatusProvider.Status;
import cgeo.geocaching.sensors.IGeoData;
import cgeo.geocaching.utils.Log;
import cgeo.geocaching.utils.OOMDumpingUncaughtExceptionHandler;

import rx.Observable;
import rx.functions.Action1;
import rx.observables.ConnectableObservable;

import android.app.Application;
import android.view.ViewConfiguration;

import java.lang.reflect.Field;

public class CgeoApplication extends Application {

    private boolean forceRelog = false; // c:geo needs to log into cache providers
    public boolean showLoginToast = true; //login toast shown just once.
    private boolean liveMapHintShownInThisSession = false; // livemap hint has been shown
    private static CgeoApplication instance;
    private Observable<IGeoData> geoDataObservable;
    private Observable<Float> directionObservable;
    private Observable<Status> gpsStatusObservable;
    private volatile IGeoData currentGeo = null;
    private volatile float currentDirection = 0.0f;

    public static void dumpOnOutOfMemory(final boolean enable) {

        if (enable) {

            if (!OOMDumpingUncaughtExceptionHandler.activateHandler()) {
                Log.e("OOM dumping handler not activated (either a problem occured or it was already active)");
            }
        } else {
            if (!OOMDumpingUncaughtExceptionHandler.resetToDefault()) {
                Log.e("OOM dumping handler not resetted (either a problem occured or it was not active)");
            }
        }
    }

    public CgeoApplication() {
        setInstance(this);
    }

    private static void setInstance(final CgeoApplication application) {
        instance = application;
    }

    public static CgeoApplication getInstance() {
        return instance;
    }

    @Override
    public void onCreate() {
        try {
            final ViewConfiguration config = ViewConfiguration.get(this);
            final Field menuKeyField = ViewConfiguration.class.getDeclaredField("sHasPermanentMenuKey");
            menuKeyField.setAccessible(true);
            menuKeyField.setBoolean(config, false);
        } catch (IllegalArgumentException | IllegalAccessException | NoSuchFieldException ignore) {
        }
        // ensure initialization of lists
        DataStore.getLists();
    }

    @Override
    public void onLowMemory() {
        Log.i("Cleaning applications cache.");
        DataStore.removeAllFromCache();
    }

    public synchronized Observable<IGeoData> geoDataObservable() {
        if (geoDataObservable == null) {
            final ConnectableObservable<IGeoData> onDemand = GeoDataProvider.create(this).replay(1);
            onDemand.subscribe(new Action1<IGeoData>() {
                                  @Override
                                  public void call(final IGeoData geoData) {
                                      currentGeo = geoData;
                                  }
                              });
            geoDataObservable = onDemand.refCount();
        }
        return geoDataObservable;
    }

    public synchronized Observable<Float> directionObservable() {
        if (directionObservable == null) {
            final ConnectableObservable<Float> onDemand = DirectionProvider.create(this).replay(1);
            onDemand.subscribe(new Action1<Float>() {
                                  @Override
                                  public void call(final Float direction) {
                                      currentDirection = direction;
                                  }
                              });
            directionObservable = onDemand.refCount();
        }
        return directionObservable;
    }

    public synchronized Observable<Status> gpsStatusObservable() {
        if (gpsStatusObservable == null) {
            final ConnectableObservable<Status> onDemand = GpsStatusProvider.create(this).replay(1);
            gpsStatusObservable = onDemand.refCount();
        }
        return gpsStatusObservable;
    }

    public IGeoData currentGeo() {
        return currentGeo != null ? currentGeo : geoDataObservable().toBlocking().first();
    }

    public float currentDirection() {
        return currentDirection;
    }

    public boolean isLiveMapHintShownInThisSession() {
        return liveMapHintShownInThisSession;
    }

    public void setLiveMapHintShownInThisSession() {
        liveMapHintShownInThisSession = true;
    }

    /**
     * Check if cgeo must relog even if already logged in.
     *
     * @return <code>true</code> if it is necessary to relog
     */
    public boolean mustRelog() {
        final boolean mustLogin = forceRelog;
        forceRelog = false;
        return mustLogin;
    }

    /**
     * Force cgeo to relog when reaching the main activity.
     */
    public void forceRelog() {
        forceRelog = true;
    }

}

@@ -365,16 +365,133 @@
 ndler;%0A%0A
+import com.google.android.gms.common.ConnectionResult;%0Aimport com.google.android.gms.common.GooglePlayServicesUtil;%0A%0A
 import r
@@ -504,16 +504,16 @@
 rvable;%0A
-
 import r
@@ -1282,16 +1282,75 @@
  = 0.0f;
+%0A    private boolean isGooglePlayServicesAvailable = false;
 %0A%0A    pu
@@ -2629,24 +2629,346 @@
 getLists();%0A
+        // Check if Google Play services is available%0A        if (GooglePlayServicesUtil.isGooglePlayServicesAvailable(this) == ConnectionResult.SUCCESS) %7B%0A            isGooglePlayServicesAvailable = true;%0A        %7D%0A        Log.i(%22Google Play services are %22 + (isGooglePlayServicesAvailable ? %22%22 : %22not %22) + %22available%22);%0A
     %7D%0A%0A    @
@@ -5478,32 +5478,32 @@
  forceRelog() %7B%0A
-
         forceRel
@@ -5520,10 +5520,116 @@
 %0A    %7D%0A%0A
+    public boolean isGooglePlayServicesAvailable() %7B%0A        return isGooglePlayServicesAvailable;%0A    %7D%0A%0A
 %7D%0A

Initialize Google Play Services