package com.datdo.mobilib.util;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;

import junit.framework.Assert;

/**
 * <pre>
 * TBD
 * </pre>
 *
 * @param <T>
 */
@SuppressWarnings("rawtypes")
public class MblMemCache<T> {

    private static class CacheItem<V> {

        V mObject;
        long mPutAt;

        CacheItem(V object, long putAt) {
            mObject = object;
            mPutAt = putAt;
        }
    }

    public static interface IterateCallback<T> {
        public void onInterate(T object);
    }

    private static final Map<String, CacheItem> sMap = new HashMap<String, CacheItem>();

    private String getCacheId(String id) {
        return mTypeName + "#" + id;
    }

    private String mTypeName;
    private long mDuration;

    public MblMemCache(Class<T> type, long duration) {
        Assert.assertNotNull(type);
        mTypeName = type.getName();
        mDuration = duration;
    }

    /**
     * <pre>
     * TBD
     * </pre>
     * @param id
     * @param object
     */
    @SuppressWarnings("unchecked")
    public void put(String id, T object) {
        synchronized (MblMemCache.class) {
            String cachId = getCacheId(id);
            CacheItem<T> cacheItem = sMap.get(cachId);
            if (cacheItem == null) {
                cacheItem = new CacheItem<T>(object, System.currentTimeMillis());
            } else {
                cacheItem.mObject = object;
                cacheItem.mPutAt = System.currentTimeMillis();
            }
            sMap.put(cachId, cacheItem);
        }
    }

    /**
     * <pre>
     * TBD
     * </pre>
     * @param id
     * @return
     */
    @SuppressWarnings("unchecked")
    public T get(String id) {
        synchronized (MblMemCache.class) {
            String cachId = getCacheId(id);
            CacheItem<T> cacheItem = sMap.get(cachId);
            if (cacheItem == null) {
                return null;
            } else {
                if (mDuration <= 0 || System.currentTimeMillis() - cacheItem.mPutAt < mDuration) {
                    return cacheItem.mObject;
                } else {
                    sMap.remove(cachId);
                    return null;
                }
            }
        }
    }

    /**
     * <pre>
     * TBD
     * </pre>
     * @param ids
     * @return
     */
    public List<T> get(List<String> ids) {

        if (MblUtils.isEmpty(ids)) {
            return new ArrayList<T>();
        }

        synchronized (MblMemCache.class) {
            List<T> ret = new ArrayList<T>();
            for (String id : ids) {
                T object = get(id);
                if (object != null) {
                    ret.add(object);
                }
            }
            return ret;
        }
    }

    /**
     * <pre>TBD</pre>
     * @param id
     * @return
     */
    @SuppressWarnings("unchecked")
    public T remove(String id) {
        synchronized (MblMemCache.class) {
            String cachId = getCacheId(id);
            CacheItem<T> cacheItem = sMap.get(cachId);
            if (cacheItem == null) {
                return null;
            } else {
                sMap.remove(cachId);
                return cacheItem.mObject;
            }
        }
    }

    /**
     * <pre>TBD</pre>
     * @param cb
     */
    public void iterateWithCallback(IterateCallback<T> cb) {

        if (cb == null) {
            return;
        }

        synchronized (MblMemCache.class) {
            Set<String> cacheIds = sMap.keySet();
            String prefix = mTypeName + "#";
            for (String cid : cacheIds) {
                if (cid.startsWith(prefix)) {
                    T o = get(cid);
                    if (o != null) {
                        cb.onInterate(o);
                    }
                }
            }
        }
    }

    /**
     * <pre>
     * TBD
     * </pre>
     * @param id
     * @return
     */
    public boolean containsKey(String id) {
        synchronized (MblMemCache.class) {
            return get(id) != null;
        }
    }

    /**
     * <pre>
     * TBD
     * </pre>
     */
    public void clear() {
        synchronized (MblMemCache.class) {
            List<String> removedIds = new ArrayList<String>();
            for (String mapId : sMap.keySet()) {
                if (mapId.startsWith(mTypeName)) {
                    removedIds.add(mapId);
                }
            }
            for (String id : removedIds) {
                sMap.remove(id);
            }
        }
    }
}

@@ -76,24 +76,50 @@
 il.HashMap;%0A
+import java.util.HashSet;%0A
 import java.
@@ -760,35 +760,235 @@
     
-return mTypeName + %22#%22 + id
+if (isCacheIdOfThisClass(id)) %7B%0A            return id;%0A        %7D else %7B%0A            return mCacheIdPrefix + id;%0A        %7D%0A    %7D%0A%0A    private boolean isCacheIdOfThisClass(String id) %7B%0A        return id.startsWith(mCacheIdPrefix)
 ;%0A  
@@ -1022,16 +1022,51 @@
 peName;%0A
+    private String mCacheIdPrefix;%0A
     priv
@@ -1177,16 +1177,16 @@
 (type);%0A
-
         
@@ -1209,24 +1209,66 @@
 .getName();%0A
+        mCacheIdPrefix = mTypeName + %22#%22;%0A
         mDur
@@ -3856,66 +3856,42 @@
 s = 
-sMap.keySet();%0A            String prefix = mTypeName + %22#%22
+new HashSet%3CString%3E(sMap.keySet())
 ;%0A  
@@ -3954,29 +3954,32 @@
 if (
-cid.startsWith(prefix
+isCacheIdOfThisClass(cid
 )) %7B
@@ -4593,91 +4593,153 @@
     
-for (
+Set%3C
 String
- mapId : sMap.keySet()) %7B%0A                if (mapId.startsWith(mTypeName
+%3E cacheIds = new HashSet%3CString%3E(sMap.keySet());%0A            for (String cid : cacheIds) %7B%0A                if (isCacheIdOfThisClass(cid
 )) %7B
@@ -4778,12 +4778,10 @@
 add(
-mapI
+ci
 d);%0A

Fix bug: MemCache: unable to iterate cache (onIterate is not invoked)