/* Android IMSI-Catcher Detector | (c) AIMSICD Privacy Project
 * -----------------------------------------------------------
 * LICENSE:  http://git.io/vki47 | TERMS:  http://git.io/vki4o
 * -----------------------------------------------------------
 */
package zz.aimsicd.lite.ui.activities;

import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.ServiceConnection;
import android.content.SharedPreferences;
import android.content.SharedPreferences.OnSharedPreferenceChangeListener;
import android.database.Cursor;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.os.AsyncTask;
import android.os.Bundle;
import android.os.IBinder;

import android.support.v4.content.res.ResourcesCompat;
import android.telephony.CellInfo;
import android.telephony.PhoneStateListener;
import android.telephony.ServiceState;
import android.telephony.TelephonyManager;
import android.util.Log;
import android.view.Menu;
import android.view.MenuItem;

import android.support.v4.content.LocalBroadcastManager;

import org.osmdroid.tileprovider.tilesource.TileSourceFactory;
import org.osmdroid.util.GeoPoint;
import org.osmdroid.views.MapView;
import org.osmdroid.views.overlay.ScaleBarOverlay;
import org.osmdroid.views.overlay.compass.CompassOverlay;
import org.osmdroid.views.overlay.compass.InternalCompassOrientationProvider;
import org.osmdroid.views.overlay.mylocation.GpsMyLocationProvider;
import org.osmdroid.views.overlay.mylocation.MyLocationNewOverlay;

import java.util.LinkedList;
import java.util.List;

import io.freefair.android.injection.annotation.InjectView;
import io.freefair.android.injection.annotation.XmlLayout;
import io.freefair.android.injection.annotation.XmlMenu;

import zz.aimsicd.lite.AppAIMSICD;

import zz.aimsicd.lite.R;
import zz.aimsicd.lite.adapters.AIMSICDDbAdapter;
import zz.aimsicd.lite.constants.DBTableColumnIds;
import zz.aimsicd.lite.constants.TinyDbKeys;
import zz.aimsicd.lite.map.CellTowerGridMarkerClusterer;
import zz.aimsicd.lite.map.CellTowerMarker;
import zz.aimsicd.lite.map.MarkerData;
import zz.aimsicd.lite.service.AimsicdService;
import zz.aimsicd.lite.utils.Cell;
import zz.aimsicd.lite.utils.GeoLocation;
import zz.aimsicd.lite.utils.Helpers;
import zz.aimsicd.lite.utils.RequestTask;
import zz.aimsicd.lite.utils.TinyDB;


/**
 * Description:    TODO: add details
 *
 * Variables:      TODO: add a list of variables that can be tuned (Max/MinZoom factors etc)
 *
 * Current Issues:
 *
 * [x] Map is not immediately updated with the BTS info. It take a "long" time ( >10 seconds)
 * before map is updated. Any way to shorten this?
 * [ ] See: #272 #250 #228
 * [ ] Some pins remain clustered even on the greatest zoom, this is probably
 * due to over sized icons, or too low zoom level.
 * [x] pin icons are too big. We need to reduce pin dot diameter by ~50%
 * [ ] Need a manual way to add GPS coordinates of current location (see code comments below)
 * [ ]
 *
 * Notes:
 * a) Latest OSM version can use MaxZoomLevel of 21, please see:
     * https://github.com/osmdroid/osmdroid/issues/49
     * https://github.com/osmdroid/osmdroid/issues/81
     * https://code.google.com/p/osmbonuspack/issues/detail?id=102
 */
@XmlLayout(zz.aimsicd.lite.R.layout.activity_map_viewer)
@XmlMenu(R.menu.activity_map_viewer)
public final class MapViewerOsmDroid extends BaseActivity implements OnSharedPreferenceChangeListener {

    public static final String TAG = "AICDL";
    public static final String mTAG = "MapViewerOsmDroid: ";

    public static final String updateOpenCellIDMarkers = "update_open_cell_markers";

    @InjectView(R.id.mapview)
    private MapView mMap;
    private AIMSICDDbAdapter mDbHelper;
    private SharedPreferences prefs;
    private AimsicdService mAimsicdService;
    private boolean mBound;

    private GeoPoint loc = null;

    private MyLocationNewOverlay mMyLocationOverlay;
    private CompassOverlay mCompassOverlay;
    private CellTowerGridMarkerClusterer mCellTowerGridMarkerClusterer;
    private Menu mOptionsMenu;
    TelephonyManager tm;

    private PhoneStateListener mPhoneStateListener = new PhoneStateListener() {
        @Override
        public void onServiceStateChanged(ServiceState serviceState) {
            loadEntries();
        }

        @Override
        public void onCellInfoChanged(List<CellInfo> cellInfo) {
            loadEntries();
        }
    };

    /**
     * Called when the activity is first created.
     */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        Log.i(TAG, mTAG + "Starting MapViewer");

        setUpMapIfNeeded();

        mDbHelper = new AIMSICDDbAdapter(this);
        tm = (TelephonyManager) getSystemService(TELEPHONY_SERVICE);

        // Bind to LocalService
        Intent intent = new Intent(this, AimsicdService.class);
        bindService(intent, mConnection, BIND_AUTO_CREATE);

        TelephonyManager tm = (TelephonyManager) getSystemService(TELEPHONY_SERVICE);
        tm.listen(mPhoneStateListener,  PhoneStateListener.LISTEN_CELL_LOCATION | PhoneStateListener.LISTEN_DATA_CONNECTION_STATE);
    }

    @Override
    public void onResume() {
        super.onResume();
        setUpMapIfNeeded();

        prefs = this.getSharedPreferences(AimsicdService.SHARED_PREFERENCES_BASENAME, 0);
        prefs.registerOnSharedPreferenceChangeListener(this);

        LocalBroadcastManager.getInstance(this).registerReceiver(mMessageReceiver, new IntentFilter(updateOpenCellIDMarkers));

        if (!mBound) {
            // Bind to LocalService
            Intent intent = new Intent(this, AimsicdService.class);
            bindService(intent, mConnection, BIND_AUTO_CREATE);
        }

        loadPreferences();
        loadEntries();

        if (mCompassOverlay != null) {
            mCompassOverlay.enableCompass();
        }

        if (mMyLocationOverlay != null) {
            mMyLocationOverlay.enableMyLocation();
        }
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        prefs.unregisterOnSharedPreferenceChangeListener(this);
        // Unbind from the service
        if (mBound) {
            unbindService(mConnection);
            mBound = false;
        }

        TelephonyManager tm = (TelephonyManager) getSystemService(TELEPHONY_SERVICE);
        tm.listen(mPhoneStateListener, PhoneStateListener.LISTEN_NONE);
        LocalBroadcastManager.getInstance(this).unregisterReceiver(mMessageReceiver);
    }

    @Override
    protected void onPause() {
        super.onPause();
        LocalBroadcastManager.getInstance(this).unregisterReceiver(mMessageReceiver);

        if (mCompassOverlay != null) {
            mCompassOverlay.disableCompass();
        }

        if (mMyLocationOverlay != null) {
            mMyLocationOverlay.disableMyLocation();
        }
    }

    private final BroadcastReceiver mMessageReceiver = new BroadcastReceiver() {
        @Override
        public void onReceive(Context context, Intent intent) {
            loadEntries();
            if (mCellTowerGridMarkerClusterer != null && mCellTowerGridMarkerClusterer.getItems() != null) {
                Log.i(TAG, mTAG + "mMessageReceiver CellTowerMarkers.invalidate() markers.size():" + mCellTowerGridMarkerClusterer.getItems().size());
            }

        }
    };

    /**
     * Service Connection to bind the activity to the service
     *
     * This seem to setup the connection and animates the map window movement to the
     * last known location.
     */
    private final ServiceConnection mConnection = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            // We've bound to LocalService, cast the IBinder and get LocalService instance
            mAimsicdService = ((AimsicdService.AimscidBinder) service).getService();
            mBound = true;

            // setup map
            GeoLocation lastKnown = mAimsicdService.lastKnownLocation();
            if (lastKnown != null) {
                mMap.getController().setZoom(16);               // Initial Zoom level
                mMap.getController().animateTo(new GeoPoint(
                        lastKnown.getLatitudeInDegrees(),
                        lastKnown.getLongitudeInDegrees()));
            }
        }

        @Override
        public void onServiceDisconnected(ComponentName arg0) {
           Log.e(TAG, mTAG + "Service Disconnected");
            mBound = false;
        }
    };

    // Load the default map type from preferences
    private void loadPreferences() {
        String mapTypePref = getResources().getString(zz.aimsicd.lite.R.string.pref_map_type_key);
        prefs = getSharedPreferences(
                AimsicdService.SHARED_PREFERENCES_BASENAME, 0);
        if (prefs.contains(mapTypePref)) {
            int mapType = Integer.parseInt(prefs.getString(mapTypePref, "0"));
            setupMapType(mapType);
        }
    }

    private void setupMapType(int mapType) {
        mMap.setTileSource(TileSourceFactory.DEFAULT_TILE_SOURCE);
        // There are two other map types (hybrid and satellite), but we don't use them
        // as they are redundant (hybrid) and broken (satellite).
        switch (mapType) {
            case 0:
                mMap.setTileSource(TileSourceFactory.DEFAULT_TILE_SOURCE);
                break;
            case 1:
                mMap.setTileSource(TileSourceFactory.CYCLEMAP);
                break;
            default:
                mMap.setTileSource(TileSourceFactory.DEFAULT_TILE_SOURCE);
                break;
        }
    }

    /**
     * Description:     Initialises the Map and sets initial options such as:
     * Zoom levels and controls
     * Compass
     * ScaleBar
     * Cluster Pin colors
     * Location update settings
     */
    private void setUpMapIfNeeded() {

        // Check if we were successful in obtaining the map.
        mMap.setBuiltInZoomControls(true);
        mMap.setMultiTouchControls(true);
        mMap.setMinZoomLevel(3);
        mMap.setMaxZoomLevel(19); // Latest OSM can go to 21!
        mMap.getTileProvider().createTileCache();
        mCompassOverlay = new CompassOverlay(this, new InternalCompassOrientationProvider(this), mMap);

        ScaleBarOverlay mScaleBarOverlay = new ScaleBarOverlay(this);
        mScaleBarOverlay.setScaleBarOffset(getResources().getDisplayMetrics().widthPixels / 2, 10);
        mScaleBarOverlay.setCentred(true);

        // Sets cluster pin color
        mCellTowerGridMarkerClusterer = new CellTowerGridMarkerClusterer(MapViewerOsmDroid.this);
        //BitmapDrawable mapPinDrawable = (BitmapDrawable) getResources().getDrawable(R.drawable.ic_map_pin_orange);
        BitmapDrawable mapPinDrawable = (BitmapDrawable) ResourcesCompat.getDrawable(getResources(),R.drawable.ic_map_pin_orange, null);

        mCellTowerGridMarkerClusterer.setIcon(mapPinDrawable == null ? null : mapPinDrawable.getBitmap());

        GpsMyLocationProvider gpsMyLocationProvider = new GpsMyLocationProvider(MapViewerOsmDroid.this.getBaseContext());
        gpsMyLocationProvider.setLocationUpdateMinDistance(100); // [m]  // Set the minimum distance for location updates
        gpsMyLocationProvider.setLocationUpdateMinTime(10000);   // [ms] // Set the minimum time interval for location updates
        mMyLocationOverlay = new MyLocationNewOverlay(MapViewerOsmDroid.this.getBaseContext(), gpsMyLocationProvider, mMap);
        mMyLocationOverlay.setDrawAccuracyEnabled(true);

        mMap.getOverlays().add(mCellTowerGridMarkerClusterer);
        mMap.getOverlays().add(mMyLocationOverlay);
        mMap.getOverlays().add(mCompassOverlay);
        mMap.getOverlays().add(mScaleBarOverlay);
    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        this.mOptionsMenu = menu;
        return super.onCreateOptionsMenu(menu);
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        // Handle item selection
        switch (item.getItemId()) {
            case R.id.map_preferences:
                Intent intent = new Intent(this, MapPrefActivity.class);
                startActivity(intent);
                return true;
            case R.id.get_opencellid: {
                if (mBound) {
                    GeoLocation lastKnown = mAimsicdService.lastKnownLocation();
                    if (lastKnown != null) {
                        Helpers.msgLong(this,
                                getString(zz.aimsicd.lite.R.string.contacting_opencellid_for_data));
                        Cell cell;
                        cell = mAimsicdService.getCell();
                        cell.setLon(lastKnown.getLongitudeInDegrees());
                        cell.setLat(lastKnown.getLatitudeInDegrees());
                        setRefreshActionButtonState(true);
                        TinyDB.getInstance().putBoolean(TinyDbKeys.FINISHED_LOAD_IN_MAP, false);
                        Helpers.getOpenCellData(this, cell, RequestTask.DBE_DOWNLOAD_REQUEST_FROM_MAP, mAimsicdService);
                        return true;
                    }
                }

                if (loc != null) {
                    Helpers.msgLong(this,
                            getString(R.string.contacting_opencellid_for_data));
                    Cell cell = new Cell();
                    cell.setLat(loc.getLatitude());
                    cell.setLon(loc.getLongitude());
                    setRefreshActionButtonState(true);
                    TinyDB.getInstance().putBoolean(TinyDbKeys.FINISHED_LOAD_IN_MAP, false);
                    Helpers.getOpenCellData(this, cell, RequestTask.DBE_DOWNLOAD_REQUEST_FROM_MAP, mAimsicdService);
                } else {
                    Helpers.msgLong(this,
                            getString(R.string.unable_to_determine_last_location));
                }
                return true;
            }
            default:
                return super.onOptionsItemSelected(item);
        }
    }

    /**
     * Description:    Loads Signal Strength Database details to plot on the map,
     * only entries which have a location (lon, lat) are used.
     */
    private void loadEntries() {

        new AsyncTask<Void, Void, GeoPoint>() {
            @Override
            protected GeoPoint doInBackground(Void... voids) {
                //int signal;

                mCellTowerGridMarkerClusterer.getItems().clear();

                //New function only gets bts from DBe_import by sim network
                loadOcidMarkersByNetwork();

                List<CellTowerMarker> items = new LinkedList<>();

                Cursor c = null;
                try {
                    // Grab cell data from CELL_TABLE (cellinfo) --> DBi_bts
                    c = mDbHelper.getCellData();
                } catch (IllegalStateException ix) {
                   Log.e(TAG, mTAG + "Problem getting data from CELL_TABLE", ix);
                }

                /*
                    This function is getting cells we logged from DBi_bts
                 */
                if (c != null && c.moveToFirst()) {
                    do {
                        if (isCancelled()) {
                            return null;
                        }
                        // The indexing here is that of DB table
                        final int cellID = c.getInt(c.getColumnIndex(DBTableColumnIds.DBI_BTS_CID));     // CID
                        final int lac = c.getInt(c.getColumnIndex(DBTableColumnIds.DBI_BTS_LAC));        // LAC
                        final int mcc = c.getInt(c.getColumnIndex(DBTableColumnIds.DBI_BTS_MCC));        // MCC
                        final int mnc = c.getInt(c.getColumnIndex(DBTableColumnIds.DBI_BTS_MNC));        // MNC
                        final int psc = c.getInt(c.getColumnIndex(DBTableColumnIds.DBI_BTS_PSC));        // PSC
                        // TODO: 2016-02-27 Is there a reason why #DBI_BTS_RAT doesn't exist?
                        // final String rat = c.getString(c.getColumnIndex(DBTableColumnIds.DBI_BTS_RAT));  // RAT
                        final double dLat = c.getDouble(c.getColumnIndex(DBTableColumnIds.DBI_BTS_LAT)); // Lat
                        final double dLng = c.getDouble(c.getColumnIndex(DBTableColumnIds.DBI_BTS_LON)); // Lon

                        if (Double.doubleToRawLongBits(dLat) == 0
                                && Double.doubleToRawLongBits(dLng) == 0) {
                            continue;
                        }
                        // TODO this (signal) is not in DBi_bts
                        // signal = 1;
                        //c.getInt(c.getColumnIndex(DBTableColumnIds.DBE_IMPORT_AVG_SIGNAL));  // signal
                        // In case of missing or negative signal, set a default fake signal,
                        // so that we can still draw signal circles.  ?
                        //if (signal <= 0) {
                        //    signal = 20;
                        //}

                        if (Double.doubleToRawLongBits(dLat) != 0
                                || Double.doubleToRawLongBits(dLng) != 0) {
                            loc = new GeoPoint(dLat, dLng);

                            CellTowerMarker ovm = new CellTowerMarker(MapViewerOsmDroid.this, mMap,
                                    "Cell ID: " + cellID,
                                    "", loc,
                                    new MarkerData(
                                            getApplicationContext(),
                                            String.valueOf(cellID),
                                            String.valueOf(loc.getLatitude()),
                                            String.valueOf(loc.getLongitude()),
                                            String.valueOf(lac),
                                            String.valueOf(mcc),
                                            String.valueOf(mnc),
                                            String.valueOf(psc),
                                            null,
                                            "", false)
                            );
                            // The pin of our current position
                            //ovm.setIcon(getResources().getDrawable(zz.aimsicd.lite.R.drawable.ic_map_pin_blue));
                            ovm.setIcon(ResourcesCompat.getDrawable(getResources(), zz.aimsicd.lite.R.drawable.ic_map_pin_blue, null));

                            items.add(ovm);
                        }

                    } while (c.moveToNext());
                } else {
                    runOnUiThread(new Runnable() {
                        @Override
                        public void run() {
                            Helpers.msgLong(MapViewerOsmDroid.this, getString(R.string.no_tracked_locations_found));
                        }
                    });
                }

                GeoPoint ret = new GeoPoint(0, 0);
                if (mBound) {
                    try {
                        int mcc = mAimsicdService.getCell().getMCC();
                        double[] d = mDbHelper.getDefaultLocation(mcc);
                        ret = new GeoPoint(d[0], d[1]);
                    } catch (Exception e) {
                       Log.e(TAG, mTAG + "Error getting default location!", e);
                    }
                }
                if (c != null) {
                    c.close();
                }
                // plot neighbouring cells
                while (mAimsicdService == null) {
                    try {
                        if (isCancelled()) {
                            return null;
                        }
                        Thread.sleep(100);
                    } catch (InterruptedException e) {
                       Log.w(TAG, mTAG + "thread interrupted", e);
                    }
                }
                List<Cell> nc = mAimsicdService.getCellTracker().updateNeighbouringCells();
                for (Cell cell : nc) {
                    if (isCancelled()) {
                        return null;
                    }
                    try {
                        loc = new GeoPoint(cell.getLat(), cell.getLon());
                        CellTowerMarker ovm = new CellTowerMarker(MapViewerOsmDroid.this, mMap,
                                getString(zz.aimsicd.lite.R.string.cell_id_label) + cell.getCID(),
                                "", loc,
                                new MarkerData(
                                        getApplicationContext(),
                                        String.valueOf(cell.getCID()),
                                        String.valueOf(loc.getLatitude()),
                                        String.valueOf(loc.getLongitude()),
                                        String.valueOf(cell.getLAC()),
                                        String.valueOf(cell.getMCC()),
                                        String.valueOf(cell.getMNC()),
                                        String.valueOf(cell.getPSC()),
                                        String.valueOf(cell.getRAT()),
                                        "", false));

                        // The pin of other BTS
                        //ovm.setIcon(getResources().getDrawable(R.drawable.ic_map_pin_orange));
                        ovm.setIcon(ResourcesCompat.getDrawable(getResources(), R.drawable.ic_map_pin_orange, null));
                        items.add(ovm);
                    } catch (Exception e) {
                       Log.e(TAG, mTAG + "Error plotting neighbouring cells", e);
                    }
                }

                mCellTowerGridMarkerClusterer.addAll(items);

                return ret;
            }

            /**
             *  TODO:  We need a manual way to add our own location in case:
             *          a) GPS is jammed or not working
             *          b) WiFi location is not used
             *          c) Default MCC is too far off
             *
             * @param defaultLoc Default location to open map on
             */
            @Override
            protected void onPostExecute(GeoPoint defaultLoc) {
                if (loc != null && (Double.doubleToRawLongBits(loc.getLatitude()) != 0
                        && Double.doubleToRawLongBits(loc.getLongitude()) != 0)) {
                    mMap.getController().setZoom(16);
                    mMap.getController().animateTo(new GeoPoint(loc.getLatitude(), loc.getLongitude()));
                } else {
                    if (mBound) {
                        // Try and find last known location and zoom there
                        GeoLocation lastLoc = mAimsicdService.lastKnownLocation();
                        if (lastLoc != null) {
                            loc = new GeoPoint(lastLoc.getLatitudeInDegrees(),
                                    lastLoc.getLongitudeInDegrees());

                            mMap.getController().setZoom(16);
                            mMap.getController().animateTo(new GeoPoint(loc.getLatitude(), loc.getLongitude()));
                        } else {
                            //Use MCC to move camera to an approximate location near Countries Capital
                            loc = defaultLoc;

                            mMap.getController().setZoom(12);
                            mMap.getController().animateTo(new GeoPoint(loc.getLatitude(), loc.getLongitude()));
                        }
                    }
                }
                if (mCellTowerGridMarkerClusterer != null) {
                    if (mCellTowerGridMarkerClusterer.getItems() != null) {
                        Log.i(TAG, mTAG + "CellTowerMarkers.invalidate() markers.size():" + mCellTowerGridMarkerClusterer.getItems().size());
                    }
                    //Drawing markers of cell tower immediately as possible
                    mCellTowerGridMarkerClusterer.invalidate();
                }
            }
        }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
    }

    private void loadOcidMarkersByNetwork() {
        // Check if OpenCellID data exists and if so load this now
        List<CellTowerMarker> items = new LinkedList<>();
        String networkOperator = tm.getNetworkOperator();
        int currentMmc = 0;
        int currentMnc = 0;
        if (networkOperator != null && networkOperator.length() > 3) {
            currentMmc = Integer.parseInt(networkOperator.substring(0, 3));
            currentMnc = Integer.parseInt(networkOperator.substring(3));
        }
        // DBe_import tower pins.
        //Drawable cellTowerMarkerIcon = getResources().getDrawable(R.drawable.ic_map_pin_green);
        Drawable cellTowerMarkerIcon = ResourcesCompat.getDrawable(getResources(), R.drawable.ic_map_pin_green, null);

        Cursor c = mDbHelper.returnOcidBtsByNetwork(currentMmc, currentMnc);
        if (c.moveToFirst()) {
            do {
                // CellID,Lac,Mcc,Mnc,Lat,Lng,AvgSigStr,Samples
                final int cellID = c.getInt(c.getColumnIndex(DBTableColumnIds.DBE_IMPORT_CID));
                final int lac = c.getInt(c.getColumnIndex(DBTableColumnIds.DBE_IMPORT_LAC));
                final int mcc = c.getInt(c.getColumnIndex(DBTableColumnIds.DBE_IMPORT_MCC));
                final int mnc = c.getInt(c.getColumnIndex(DBTableColumnIds.DBE_IMPORT_MNC));
                final int psc = c.getInt(c.getColumnIndex(DBTableColumnIds.DBE_IMPORT_PSC));
                final String rat = c.getString(c.getColumnIndex(DBTableColumnIds.DBE_IMPORT_RAT));
                final double dLat = Double.parseDouble(c.getString(c.getColumnIndex(DBTableColumnIds.DBE_IMPORT_GPS_LAT)));
                final double dLng = Double.parseDouble(c.getString(c.getColumnIndex(DBTableColumnIds.DBE_IMPORT_GPS_LON)));
                final GeoPoint location = new GeoPoint(dLat, dLng);
                //where is c.getString(6)AvgSigStr
                final int samples = c.getInt(c.getColumnIndex(DBTableColumnIds.DBE_IMPORT_SAMPLES));

                // Add map marker for CellID
                CellTowerMarker ovm = new CellTowerMarker(this, mMap, "Cell ID: " + cellID, "", location,
                        new MarkerData(
                                getApplicationContext(),
                                String.valueOf(cellID),
                                String.valueOf(location.getLatitude()),
                                String.valueOf(location.getLongitude()),
                                String.valueOf(lac),
                                String.valueOf(mcc),
                                String.valueOf(mnc),
                                String.valueOf(psc),
                                rat,
                                String.valueOf(samples),
                                false));

                ovm.setIcon(cellTowerMarkerIcon);
                items.add(ovm);
            } while (c.moveToNext());
        }
        c.close();

        mCellTowerGridMarkerClusterer.addAll(items);
    }

    public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {
        final String KEY_MAP_TYPE = getBaseContext().getString(zz.aimsicd.lite.R.string.pref_map_type_key);
        if (key.equals(KEY_MAP_TYPE)) {
            int item = Integer.parseInt(sharedPreferences.getString(key, "0"));
            setupMapType(item);
        }
    }

    public void setRefreshActionButtonState(final boolean refreshing) {
        if (mOptionsMenu != null) {
            final MenuItem refreshItem = mOptionsMenu.findItem(R.id.get_opencellid);
            if (refreshItem != null) {
                if (refreshing) {
                    refreshItem.setActionView(R.layout.actionbar_indeterminate_progress);
                } else {
                    refreshItem.setActionView(null);
                }
            }
        }
    }


    public void onStop() {
        super.onStop();
        ((AppAIMSICD) getApplication()).detach(this);
    }

    @Override
    public void onStart() {
        super.onStart();
        ((AppAIMSICD) getApplication()).attach(this);
        if (TinyDB.getInstance().getBoolean(TinyDbKeys.FINISHED_LOAD_IN_MAP)) {
            setRefreshActionButtonState(false);
        }
    }
}