content: /*
   * Created by LuaView.
   * Copyright (c) 2017, Alibaba Group. All rights reserved.
   *
   * This source code is licensed under the MIT.
   * For the full copyright and license information,please view the LICENSE file in the root directory of this source tree.
   */
  
  package com.taobao.luaview.cache;
  
  import android.app.Application;
  import android.support.v4.util.LruCache;
  
  import java.util.HashMap;
  import java.util.Map;
  
  /**
   * App级别的引用cache，不会在退出的时候清空
   *
   * @author song
   * @date 16/1/27
   */
  public class AppCache {
      public static final int DEFAULT_LRU_CACHE_PROTOTYPE_SIZE = (int) (1.5 * 1024 * 1024);//1.5M
      public static final int DEFAULT_LRU_CACHE_SIZE = 5;//5个
      public static final String CACHE_METHODS = "cache_methods";//方法名称缓存
      public static final String CACHE_PUBLIC_KEY = "cache_public_key";//公钥
      public static final String CACHE_METATABLES = "cache_metatables";//metatable缓存
      public static final String CACHE_SCRIPTS = "cache_scripts";//脚本文件缓存，占用空间2%
      public static final String CACHE_PROTOTYPE = "cache_prototype";//lua中间码缓存，占用空间3%
  
      //全局静态cache
      private static Map<String, AppCache> mCachePool;
  
      //simple cache
      private Map<Object, Object> mCache;
  
      //lru cache
      private LuaLruCache mLruCache;
  
  
      /**
       * on received memory warning
       *
       * @param level
       */
      public static void onTrimMemory(int level) {
          switch (level) {
              case Application.TRIM_MEMORY_COMPLETE:// = 80; clear all
                  clear();
                  break;
              case Application.TRIM_MEMORY_MODERATE:// = 60;
                  clear(CACHE_PROTOTYPE, CACHE_SCRIPTS);
                  break;
              case Application.TRIM_MEMORY_BACKGROUND:// = 40;
                  clear(CACHE_SCRIPTS);
                  break;
              case Application.TRIM_MEMORY_UI_HIDDEN:// = 20;
                  clear(CACHE_SCRIPTS);
                  break;
              case Application.TRIM_MEMORY_RUNNING_CRITICAL:// = 15;
                  break;
              case Application.TRIM_MEMORY_RUNNING_LOW:// = 10;
                  break;
              case Application.TRIM_MEMORY_RUNNING_MODERATE:// = 5;
                  break;
          }
      }
  
      private AppCache() {
          this(DEFAULT_LRU_CACHE_SIZE);
      }
  
      private AppCache(int size) {
          mCache = new HashMap<Object, Object>();
          if (size > 0) {
              mLruCache = new LuaLruCache(size);
          }
      }
  
      /**
       * get a named cache
       *
       * @return
       */
      public static AppCache getPrototpyeCache() {
          return getCache(CACHE_PROTOTYPE, DEFAULT_LRU_CACHE_PROTOTYPE_SIZE);
      }
  
      public static AppCache getCache(String cacheName) {
          return getCache(cacheName, DEFAULT_LRU_CACHE_SIZE);
      }
  
      public static AppCache getCache(String cacheName, int size) {
          if (mCachePool == null) {
              mCachePool = new HashMap<String, AppCache>();
          }
          if (!mCachePool.containsKey(cacheName)) {
              final AppCache appCache = new AppCache(size);
              mCachePool.put(cacheName, appCache);
              return appCache;
          }
          return mCachePool.get(cacheName);
      }
  
  
      /**
       * should call when LuaView is destroy
       */
      public static void clear() {
          if (mCachePool != null) {
              mCachePool.clear();
          }
      }
  
      /**
       * clear certain cache
       *
       * @param keys
       */
      public static void clear(String... keys) {
          if (mCachePool != null && keys != null) {
              AppCache appCache = null;
              for (String key : keys) {
                  if (mCachePool.containsKey(key)) {
                      appCache = mCachePool.remove(key);
                      if (appCache != null) {
                          if (appCache.mCache != null) {
                              appCache.mCache.clear();
                          }
                          if (appCache.mLruCache != null) {
                              appCache.mLruCache.evictAll();
                          }
                      }
                  }
              }
          }
      }
  
      /**
       * get from cache
       *
       * @param key
       * @param <T>
       * @return
       */
      public <T> T get(final Object key) {
          if (mCache != null && mCache.get(key) != null) {
              return (T) mCache.get(key);
          }
          return null;
      }
  
      /**
       * get from cache
       *
       * @param key
       * @param <T>
       * @return
       */
      public <T> T getLru(final Object key) {
          if (mLruCache != null && mLruCache.get(key) != null) {
              return (T) mLruCache.getWrap(key);
          }
          return null;
      }
  
      /**
       * update cache
       *
       * @param key
       * @param value
       * @param <T>
       * @return
       */
      public <T> T put(final Object key, T value) {
          if (mCache != null) {
              mCache.put(key, value);
          }
          return value;
      }
  
      /**
       * update cache
       *
       * @param key
       * @param value
       * @param <T>
       * @return
       */
      public <T> T putLru(final Object key, T value, Integer size) {
          if (mLruCache != null) {
              mLruCache.putWrap(key, value, size);
          }
          return value;
      }
  
      public <T> T putLru(final Object key, T value) {
          if (mLruCache != null) {
              mLruCache.putWrap(key, value, null);
          }
          return value;
      }
  
      //----------------------------------------extend lru object-------------------------------------
      static class LuaLruCache extends LruCache<Object, Object> {
          public LuaLruCache(int maxSize) {
              super(maxSize);
          }
  
          public void putWrap(Object key, Object value, Integer size) {
              if (key != null && value != null) {
                  if (size != null) {
                      super.put(key, new WrapLruObject(value, size));
                  } else {
                      super.put(key, value);
                  }
              }
          }
  
          public Object getWrap(Object key) {
              if (key != null) {
                  Object result = super.get(key);
                  if (result instanceof WrapLruObject) {
                      return ((WrapLruObject) result).obj;
                  } else {
                      return result;
                  }
              } else {
                  return null;
              }
          }
  
          @Override
          protected int sizeOf(Object key, Object value) {
              if (value instanceof WrapLruObject) {
                  return ((WrapLruObject) value).size;
              }
              return super.sizeOf(key, value);
          }
      }
  
      static class WrapLruObject extends Object {
          Object obj;
          int size;
  
          WrapLruObject(Object obj, int size) {
              this.obj = obj;
              this.size = size;
          }
      }
  }
  