package com.mapswithme.maps.location;

import android.content.Context;
import android.location.Location;
import android.location.LocationListener;
import android.location.LocationManager;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;

import com.mapswithme.maps.MwmApplication;
import com.mapswithme.util.LocationUtils;

import java.util.ArrayList;
import java.util.List;
import java.util.ListIterator;

class AndroidNativeProvider extends BaseLocationProvider
{
  private final static String TAG = AndroidNativeProvider.class.getSimpleName();
  private final static String[] TRUSTED_PROVIDERS = { LocationManager.NETWORK_PROVIDER,
                                                      LocationManager.GPS_PROVIDER };

  @NonNull
  private final LocationManager mLocationManager;
  @NonNull
  private final List<LocationListener> mListeners = new ArrayList<>();

  AndroidNativeProvider(@NonNull LocationFixChecker locationFixChecker)
  {
    super(locationFixChecker);
    mLocationManager = (LocationManager) MwmApplication.get().getSystemService(Context.LOCATION_SERVICE);
  }

  @SuppressWarnings("MissingPermission")
  // A permission is checked externally
  @Override
  protected void start()
  {
    LOGGER.d(TAG, "Android native provider is started");
    if (isActive())
      return;

    List<String> providers = getAvailableProviders(mLocationManager);
    if (providers.isEmpty())
    {
      setActive(false);
      return;
    }

    setActive(true);
    for (String provider : providers)
    {
      LocationListener listener = new BaseLocationListener(getLocationFixChecker());
      long interval = LocationHelper.INSTANCE.getInterval();
      LOGGER.d(TAG, "Request Android native provider '" + provider
                    + "' to get locations at this interval = " + interval + " ms");
      mLocationManager.requestLocationUpdates(provider, interval, 0, listener);
      mListeners.add(listener);
    }

    LocationHelper.INSTANCE.startSensors();

    Location location = findBestNotExpiredLocation(mLocationManager, providers,
                                                   LocationUtils.LOCATION_EXPIRATION_TIME_MILLIS_SHORT);
    if (location != null && !getLocationFixChecker().isLocationBetterThanLast(location))
    {
      location = LocationHelper.INSTANCE.getSavedLocation();
      if (location == null || LocationUtils.isExpired(location, LocationHelper.INSTANCE.getSavedLocationTime(),
                                                      LocationUtils.LOCATION_EXPIRATION_TIME_MILLIS_SHORT))
      {
        return;
      }
    }

    if (location != null)
      onLocationChanged(location);
  }

  private void onLocationChanged(@NonNull Location location)
  {
    ListIterator<LocationListener> iterator = mListeners.listIterator();
    // All listeners have to be notified only through safe list iterator interface,
    // otherwise ConcurrentModificationException will be obtained, because each listener can
    // cause 'stop' method calling and modifying the collection during this iteration.
    // noinspection WhileLoopReplaceableByForEach
    while (iterator.hasNext())
      iterator.next().onLocationChanged(location);
  }

  @Override
  protected void stop()
  {
    LOGGER.d(TAG, "Android native provider is stopped");
    ListIterator<LocationListener> iterator = mListeners.listIterator();
    // noinspection WhileLoopReplaceableByForEach
    while (iterator.hasNext())
      mLocationManager.removeUpdates(iterator.next());

    mListeners.clear();
    setActive(false);
  }

  @Nullable
  static Location findBestNotExpiredLocation(long expirationMillis)
  {
    final LocationManager manager = (LocationManager) MwmApplication.get().getSystemService(Context.LOCATION_SERVICE);
    return findBestNotExpiredLocation(manager,
                                      getAvailableProviders(manager),
                                      expirationMillis);
  }

  @SuppressWarnings("MissingPermission")
  // A permission is checked externally
  @Nullable
  private static Location findBestNotExpiredLocation(LocationManager manager, List<String> providers, long expirationMillis)
  {
    Location res = null;
    for (final String pr : providers)
    {
      final Location last = manager.getLastKnownLocation(pr);
      if (last == null || LocationUtils.isExpired(last, last.getTime(), expirationMillis))
        continue;

      if (res == null || res.getAccuracy() > last.getAccuracy())
        res = last;
    }
    return res;
  }

  @NonNull
  private static List<String> getAvailableProviders(@NonNull LocationManager locationManager)
  {
    final List<String> res = new ArrayList<>();
    for (String provider : TRUSTED_PROVIDERS)
    {
      if (locationManager.isProviderEnabled(provider))
        res.add(provider);
    }
    return res;
  }
}

@@ -3954,89 +3954,8 @@
  %7D%0A%0A
-  @SuppressWarnings(%22MissingPermission%22)%0A  // A permission is checked externally%0A
   @N
@@ -4116,16 +4116,32 @@
 = null;%0A
+    try%0A    %7B%0A  
     for 
@@ -4174,24 +4174,26 @@
 rs)%0A    
+  
 %7B%0A      
 final Lo
@@ -4180,24 +4180,26 @@
     %7B%0A      
+ 
+ 
 final Locati
@@ -4238,24 +4238,26 @@
 cation(pr);%0A
+  
       if (la
@@ -4343,16 +4343,18 @@
         
+  
 continue
@@ -4358,24 +4358,26 @@
 nue;%0A%0A      
+ 
+ 
 if (res == n
@@ -4431,16 +4431,18 @@
         
+  
 res = la
@@ -4445,16 +4445,188 @@
 = last;%0A
+      %7D%0A    %7D%0A    catch (SecurityException e)%0A    %7B%0A      LOGGER.e(TAG, %22Dynamic permission ACCESS_COARSE_LOCATION/ACCESS_FINE_LOCATION is not granted%22,%0A               e);%0A
     %7D%0A  

fix SecurityException