package net.jspiner.cachebank;

import android.support.v4.util.LruCache;

/**
 * Created by JSpiner on 2017. 7. 13..
 * PRNDCompany
 * Contact : smith@prnd.co.kr
 */

public final class Bank {

    private static int memCacheSize;
    private static int diskCacheSize;
    private static boolean isInitialized = false;
    private static LruCache lruMemCache;
    private static CacheMode cacheMode;

    private Bank(Builder builder){
        this.memCacheSize = builder.memCacheSize;
        this.diskCacheSize = builder.diskCacheSize;
        this.cacheMode = builder.cacheMode;
        this.isInitialized = true;
        this.lruMemCache = new LruCache<String, CacheObject>(memCacheSize);
    }

    public static boolean isInitialized(){
        return isInitialized;
    }

    public static <T extends ProviderInterface> T get(String key, Class<T> targetClass){
        CacheObject<T> cachedObject = getCacheObjectInCache(key, targetClass);

        boolean isExpired = isExpired(cachedObject);

        if(isExpired){
            cachedObject.update(key);
        }

        return cachedObject.getValue();
    }

    private static <T extends ProviderInterface> CacheObject getCacheObjectInCache(String key, Class<T> targetClass){
        CacheObject cachedObject = findInMemory(key, targetClass);

        if(cachedObject == null){
            cachedObject = findInDisk(key, targetClass);

            if(cachedObject != null){
                registerInMemory(key, cachedObject);
            }
        }

        if(cachedObject == null){
            cachedObject = CacheObject.newInstance(key, targetClass);
        }

        return cachedObject;
    }

    private static <T extends ProviderInterface> CacheObject findInMemory(String key, Class<T> targetClass){
        CacheObject<T> cachedObject = (CacheObject<T>) lruMemCache.get(key);
        if(cachedObject == null){
            return null;
        }
        if(!cachedObject.getValue().getClass().equals(targetClass)){
            throw new ClassCastException();
        }
        return cachedObject;
    }

    // TODO : disk cache 구현하기
    private static <T extends ProviderInterface> CacheObject findInDisk(String key, Class<T> targetClass){
        return null;
    }

    private static void registerInMemory(String key, CacheObject cacheObject){
        lruMemCache.put(key, cacheObject);
    }

    public static <T extends ProviderInterface> void put(String key, T value){
        CacheObject<T> cacheObject = new CacheObject<>(
                key,
                value,
                System.currentTimeMillis() + value.getCacheTime()
        );
        lruMemCache.put(key, cacheObject);
    }

    public static boolean isExpired(CacheObject cacheObject){
        long currentTime = System.currentTimeMillis();
        if(cacheObject.getExpireTime() < currentTime){
            return true;
        }
        return false;
    }

    public static int getMemCacheSize() {
        return memCacheSize;
    }

    public static int getDiskCacheSize() {
        return diskCacheSize;
    }

    public static CacheMode getCacheMode() {
        return cacheMode;
    }

    public static void clear(){
        clearDiskCache();
        clearMemoryCache();
    }

    // TODO : 메모리와 디스크에서 캐시 초기화하는 함수 구현하기
    private static void clearMemoryCache(){

    }

    private static void clearDiskCache(){

    }

    public static void terminate(){
        isInitialized = false;
    }

    public final static class Builder {

        private int memCacheSize;
        private int diskCacheSize;
        private CacheMode cacheMode;

        public Builder(){
            memCacheSize = BankConstant.DEFAULT_MEM_CACHE_SIZE;
            diskCacheSize = BankConstant.DEFAULT_DISK_CACHE_SIZE;
            cacheMode = BankConstant.DEFAULT_CACHE_MODE;
        }

        public Bank init(){
            return new Bank(this);
        }

        public Builder setMemCacheSize(int memCacheSize) {
            this.memCacheSize = memCacheSize;
            return this;
        }

        public Builder setDiskCacheSize(int diskCacheSize) {
            this.diskCacheSize = diskCacheSize;
            return this;
        }

        public Builder setCacheMode(CacheMode cacheMode){
            this.cacheMode = cacheMode;
            return this;
        }
    }
}

@@ -854,32 +854,62 @@
 %3E targetClass)%7B%0A
+        checkInitAndThrow();%0A%0A
         CacheObj
@@ -2473,24 +2473,54 @@
 , T value)%7B%0A
+        checkInitAndThrow();%0A%0A
         Cach
@@ -2731,37 +2731,38 @@
 t);%0A    %7D%0A%0A    p
-ublic
+rivate
  static boolean 
@@ -2963,32 +2963,221 @@
 n false;%0A    %7D%0A%0A
+    private static void checkInitAndThrow()%7B%0A        if(isInitialized() == false)%7B%0A            throw new RuntimeException(%22You need to start it through the Bank.Builder%22);%0A        %7D%0A    %7D%0A%0A
     public stati
@@ -3194,32 +3194,62 @@
 emCacheSize() %7B%0A
+        checkInitAndThrow();%0A%0A
         return m
@@ -3303,32 +3303,62 @@
 skCacheSize() %7B%0A
+        checkInitAndThrow();%0A%0A
         return d
@@ -3419,24 +3419,54 @@
 cheMode() %7B%0A
+        checkInitAndThrow();%0A%0A
         retu
@@ -3514,24 +3514,54 @@
 id clear()%7B%0A
+        checkInitAndThrow();%0A%0A
         clea
@@ -3613,50 +3613,8 @@
  %7D%0A%0A
-    // TODO : %EB%A9%94%EB%AA%A8%EB%A6%AC%EC%99%80 %EB%94%94%EC%8A%A4%ED%81%AC%EC%97%90%EC%84%9C %EC%BA%90%EC%8B%9C %EC%B4%88%EA%B8%B0%ED%99%94%ED%95%98%EB%8A%94 %ED%95%A8%EC%88%98 %EA%B5%AC%ED%98%84%ED%95%98%EA%B8%B0%0A
     
@@ -3649,31 +3649,99 @@
 oryCache()%7B%0A
-%0A    %7D%0A
+        lruMemCache.evictAll();%0A    %7D%0A%0A    // TODO : %EB%94%94%EC%8A%A4%ED%81%AC%EC%97%90%EC%84%9C %EC%BA%90%EC%8B%9C %EC%B4%88%EA%B8%B0%ED%99%94%ED%95%98%EB%8A%94 %ED%95%A8%EC%88%98 %EA%B5%AC%ED%98%84%ED%95%98%EA%B8%B0
 %0A    private
@@ -3823,37 +3823,112 @@
         
-isInitialized = false
+checkInitAndThrow();%0A%0A        clear();%0A        isInitialized = false;%0A        lruMemCache = null
 ;%0A    %7D%0A

add checkInit function