/*
 * SupplierCache.java
 * 
 * Copyright (c) 2014 Rhythm & Hues Studios. All rights reserved.
 */
package com.rhythm.louie.cache;

import java.util.Map;
import java.util.concurrent.Callable;
import java.util.concurrent.TimeUnit;

import com.google.common.base.Supplier;
import com.google.common.base.Suppliers;

/**
 * A Guava Supplier based cache.  Uses Suppliers.memoize to persist the original 
 * supplier value.  
 * 
 * DO NOT create a memoized supplier outside of this class 
 * and use it to create a cache.  This will result in undesirable caching behavior
 * as certain functionality will be indeterminate.
 * 
 * @param <V> the type of the value
 */
public class SupplierCache <V> implements Cache<Object, V> {
    
    private final String name;
    private final Supplier<V> supplier;
    private Supplier<V> cache;
    private final long duration;
    private final TimeUnit timeUnit;
    
    @SuppressWarnings("unchecked")
    public static <V> SupplierCache<V> nonCaching(String name, Supplier<V> supplier) {
        return new SupplierCache(name,supplier,0, TimeUnit.SECONDS);
    }
    
    @SuppressWarnings("unchecked")
    public static <V> SupplierCache<V> permanent(String name, Supplier<V> supplier) {
        return new SupplierCache(name,supplier,-1, TimeUnit.SECONDS);
    }
    
    /**
     * Creates an expiring,caching Supplier, 
     * if duration = 0 caching will effectively be disabled
     * if duration < 0 the cache will never expire
     * 
     * @param <V>
     * @param name
     * @param supplier
     * @param duration
     * @param timeUnit
     * @return 
     */
    @SuppressWarnings("unchecked")
    public static <V> SupplierCache<V> expiring(String name, Supplier<V> supplier,
            long duration, TimeUnit timeUnit) {
        return new SupplierCache(name,supplier,duration,timeUnit);
    }
    
    private SupplierCache(final String name, Supplier<V> supplier,
            long duration, TimeUnit timeUnit) {
        this.name = name;
        this.supplier = supplier;
        this.duration = duration;
        this.timeUnit = timeUnit;
        createCache();
    }

    private synchronized void createCache() {
        if (duration==0) {
            cache = supplier;
        } else if (duration<0) {
            cache = Suppliers.memoize(supplier);
        } else {
            cache = Suppliers.memoizeWithExpiration(supplier,duration,timeUnit);
        }
    }
    
    public Supplier<V> getBaseSupplier() {
        return supplier;
    }
    
    public V get() {
        return cache.get();
    }
    
    @Override
    public void put(Object key, V value) throws Exception {
        throw new UnsupportedOperationException("Singleton cache doesn't support this");
    }

    @Override
    public V get(Object key) {
        throw new UnsupportedOperationException("Singleton cache doesn't support this");
    }

    public V get(Object key, Callable<? extends V> valueLoader) {
        throw new UnsupportedOperationException("Singleton cache doesn't support this");
    }
    
    @Override
    public void remove(Object key) throws Exception {
        throw new UnsupportedOperationException("Singleton cache doesn't support this");
    }

    @Override
    public void clear() throws Exception {
        // Only way to clear a memoized supplier is to throw away the whole thing
        // So we do this here to just recreate the memoized supplier, if needed
        createCache();
    }

    @Override
    public String getCacheName() {
        return name;
    }

    @Override
    public void putAll(Map<Object, V> map) throws Exception {
        throw new UnsupportedOperationException("Singleton cache doesn't support this");
    }

    @Override
    public int getSize() {
        return 1;
    }
}

@@ -796,39 +796,8 @@
 er;%0A
-    private Supplier%3CV%3E cache;%0A
     
@@ -863,24 +863,60 @@
 eUnit;%0A    %0A
+    private Supplier%3CV%3E cache;%0A    %0A
     @Suppres
@@ -2096,32 +2096,40 @@
 imeUnit;%0A       
+ cache =
  createCache();%0A
@@ -2151,25 +2151,19 @@
 ate 
-synchronized void
+Supplier%3CV%3E
  cre
@@ -2214,23 +2214,22 @@
         
-cache =
+return
  supplie
@@ -2268,39 +2268,38 @@
 ) %7B%0A            
-cache =
+return
  Suppliers.memoi
@@ -2341,23 +2341,22 @@
         
-cache =
+return
  Supplie
@@ -2561,32 +2561,301 @@
 t();%0A    %7D%0A    %0A
+    /**%0A     * Calls the underlying supplier, re-storing it in the cache%0A     * %0A     * @return the new value%0A     */%0A    public V reload() %7B%0A        Supplier%3CV%3E temp = createCache();%0A        V value = temp.get();%0A        cache = temp;%0A        return value;%0A    %7D%0A    %0A
     @Override%0A  
@@ -3705,16 +3705,24 @@
 %0A       
+ cache =
  createC

add reload to supplier cache