package com.google.maps.android.clustering.algo;

import android.annotation.TargetApi;
import android.os.Build;
import android.util.Log;
import android.util.LruCache;

import com.google.maps.android.clustering.Cluster;
import com.google.maps.android.clustering.ClusterItem;

import java.util.Collection;
import java.util.Set;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;

@TargetApi(Build.VERSION_CODES.HONEYCOMB_MR1)
public class PreCachingDecorator<T extends ClusterItem> implements Algorithm<T> {
    private final static String TAG = PreCachingDecorator.class.getName();
    private final Algorithm<T> mAlgorithm;
    private final LruCache<Integer, Set<? extends Cluster<T>>> mCache = new LruCache<Integer, Set<? extends Cluster<T>>>(5);
    private final ReadWriteLock mCacheLock = new ReentrantReadWriteLock();

    public PreCachingDecorator(Algorithm<T> algorithm) {
        mAlgorithm = algorithm;
    }

    public void addItem(T item) {
        mAlgorithm.addItem(item);
        clearCache();
    }

    @Override
    public void addAllItems(Collection<T> items) {
        mAlgorithm.addAllItems(items);
        clearCache();
    }

    @Override
    public void clearItems() {
        mAlgorithm.clearItems();
        clearCache();
    }

    public void removeItem(T item) {
        mAlgorithm.removeItem(item);
        clearCache();
    }

    private void clearCache() {
        mCache.evictAll();
    }

    @Override
    public Set<? extends Cluster<T>> getClusters(double zoom) {
        int discreteZoom = (int) zoom;
        Set<? extends Cluster<T>> results = getClustersInternal(discreteZoom);
        new Thread(new PrecacheRunnable(discreteZoom + 1)).start();
        new Thread(new PrecacheRunnable(discreteZoom - 1)).start();
        return results;
    }

    @Override
    public Collection<T> getItems() {
        return null;
    }

    public Set<? extends Cluster<T>> getClustersInternal(int discreteZoom) {
        Log.d(TAG, "getClusters: zoom " + discreteZoom);
        Set<? extends Cluster<T>> results;
        mCacheLock.readLock().lock();
        results = mCache.get(discreteZoom);
        mCacheLock.readLock().unlock();

        if (results == null) {
            mCacheLock.writeLock().lock();
            results = mCache.get(discreteZoom);
            if (results == null) {
                Log.d(TAG, "cache miss for zoom " + discreteZoom);
                results = mAlgorithm.getClusters(discreteZoom);
                mCache.put(discreteZoom, results);
            } else {
                Log.d(TAG, "cache hit for zoom " + discreteZoom);
            }
            mCacheLock.writeLock().unlock();
        } else {
            Log.d(TAG, "cache hit for zoom " + discreteZoom);
        }
        return results;
    }

    private class PrecacheRunnable implements Runnable {
        private final int mZoom;

        public PrecacheRunnable(int zoom) {
            mZoom = zoom;
        }

        @Override
        public void run() {
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                // ignore. keep going.
            }
            long start = System.currentTimeMillis();
            getClustersInternal(mZoom);
            Log.d(TAG, "pre-filling cache for zoom " + mZoom + " took " + (System.currentTimeMillis() - start));
        }
    }
}

@@ -124,32 +124,18 @@
 oid.
-util.Log;%0Aimport android
+support.v4
 .uti
@@ -1892,20 +1892,37 @@
  return 
-null
+mAlgorithm.getItems()
 ;%0A    %7D%0A
@@ -2003,65 +2003,8 @@
 ) %7B%0A
-        Log.d(TAG, %22getClusters: zoom %22 + discreteZoom);%0A
     
@@ -2326,75 +2326,8 @@
 ) %7B%0A
-                Log.d(TAG, %22cache miss for zoom %22 + discreteZoom);%0A
     
@@ -2454,217 +2454,51 @@
    %7D
- else %7B%0A                Log.d(TAG, %22cache hit for zoom %22 + discreteZoom);%0A            %7D%0A            mCacheLock.writeLock().unlock();%0A        %7D else %7B%0A            Log.d(TAG, %22cache hit for zoom %22 + discreteZoom
+%0A            mCacheLock.writeLock().unlock(
 );%0A 
@@ -2793,25 +2793,102 @@
     
-Thread.sleep(1000
+// Wait between 500 - 1000 ms.%0A                Thread.sleep((long) (Math.random() * 500 + 500)
 );%0A 
@@ -3002,199 +3002,33 @@
     
-long start = System.currentTimeMillis();%0A            getClustersInternal(mZoom);%0A            Log.d(TAG, %22pre-filling cache for zoom %22 + mZoom + %22 took %22 + (System.currentTimeMillis() - start)
+getClustersInternal(mZoom
 );%0A 

Remove logging from Precaching decorator