/*
 * Copyright 2016-present Open Networking Laboratory
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.onosproject.store.primitives.impl;

import java.util.concurrent.CompletableFuture;
import java.util.function.BiFunction;
import java.util.function.Predicate;

import org.onosproject.store.service.AsyncConsistentMap;
import org.onosproject.store.service.MapEventListener;
import org.onosproject.store.service.Versioned;

import com.google.common.cache.CacheBuilder;
import com.google.common.cache.CacheLoader;
import com.google.common.cache.LoadingCache;

/**
 * {@code AsyncConsistentMap} that caches entries on read.
 * <p>
 * The cache entries are automatically invalidated when updates are detected either locally or
 * remotely.
 * <p> This implementation only attempts to serve cached entries for {@link AsyncConsistentMap#get get}
 * calls. All other calls skip the cache and directly go the backing map.
 *
 * @param <K> key type
 * @param <V> value type
 */
public class CachingAsyncConsistentMap<K, V> extends DelegatingAsyncConsistentMap<K, V> {

    private final LoadingCache<K, CompletableFuture<Versioned<V>>> cache =
            CacheBuilder.newBuilder()
                        .maximumSize(10000) // TODO: make configurable
                        .build(new CacheLoader<K, CompletableFuture<Versioned<V>>>() {
                            @Override
                            public CompletableFuture<Versioned<V>> load(K key)
                                    throws Exception {
                                return CachingAsyncConsistentMap.super.get(key);
                            }
                        });

    private final MapEventListener<K, V> cacheInvalidator = event -> cache.invalidate(event.key());

    public CachingAsyncConsistentMap(AsyncConsistentMap<K, V> backingMap) {
        super(backingMap);
        super.addListener(cacheInvalidator);
    }

    @Override
    public CompletableFuture<Void> destroy() {
        return super.destroy().thenCompose(v -> removeListener(cacheInvalidator));
    }

    @Override
    public CompletableFuture<Versioned<V>> get(K key) {
        return cache.getUnchecked(key);
    }

    @Override
    public CompletableFuture<Versioned<V>> computeIf(K key,
            Predicate<? super V> condition,
            BiFunction<? super K, ? super V, ? extends V> remappingFunction) {
        return super.computeIf(key, condition, remappingFunction)
                    .whenComplete((r, e) -> cache.invalidate(key));
    }

    @Override
    public CompletableFuture<Versioned<V>> put(K key, V value) {
        return super.put(key, value)
                    .whenComplete((r, e) -> cache.invalidate(key));
    }

    @Override
    public CompletableFuture<Versioned<V>> putAndGet(K key, V value) {
        return super.putAndGet(key, value)
                    .whenComplete((r, e) -> cache.invalidate(key));
    }

    @Override
    public CompletableFuture<Versioned<V>> remove(K key) {
        return super.remove(key)
                    .whenComplete((r, e) -> cache.invalidate(key));
    }

    @Override
    public CompletableFuture<Void> clear() {
        return super.clear()
                    .whenComplete((r, e) -> cache.invalidateAll());
    }

    @Override
    public CompletableFuture<Boolean> remove(K key, V value) {
        return super.remove(key, value)
                    .whenComplete((r, e) -> {
                        if (r) {
                            cache.invalidate(key);
                        }
                    });
    }

    @Override
    public CompletableFuture<Boolean> remove(K key, long version) {
        return super.remove(key, version)
                .whenComplete((r, e) -> {
                    if (r) {
                        cache.invalidate(key);
                    }
                });
    }

    @Override
    public CompletableFuture<Versioned<V>> replace(K key, V value) {
        return super.replace(key, value)
                    .whenComplete((r, e) -> cache.invalidate(key));
    }

    @Override
    public CompletableFuture<Boolean> replace(K key, V oldValue, V newValue) {
        return super.replace(key, oldValue, newValue)
                .whenComplete((r, e) -> {
                    if (r) {
                        cache.invalidate(key);
                    }
                });
    }

    @Override
    public CompletableFuture<Boolean> replace(K key, long oldVersion, V newValue) {
        return super.replace(key, oldVersion, newValue)
                .whenComplete((r, e) -> {
                    if (r) {
                        cache.invalidate(key);
                    }
                });
    }
}

@@ -611,17 +611,16 @@
 se.%0A */%0A
-%0A
 package 
@@ -1578,16 +1578,54 @@
 %3CK, V%3E %7B
+%0A    private int maxCacheSize = 10000;
 %0A%0A    pr
@@ -1751,20 +1751,16 @@
         
-    
 .maximum
@@ -1768,46 +1768,22 @@
 ize(
-10000) // TODO: make configurable%0A    
+maxCacheSize)%0A
     
@@ -1885,20 +1885,16 @@
         
-    
 @Overrid
@@ -1919,20 +1919,16 @@
         
- 
-   
 public C
@@ -2002,20 +2002,16 @@
         
-    
 throws E
@@ -2045,28 +2045,24 @@
             
-  
-  
 return Cachi
@@ -2118,34 +2118,26 @@
             
-    %7D%0A    
+%7D%0A
             
@@ -2246,24 +2246,151 @@
 nt.key());%0A%0A
+    /**%0A     * Default constructor.%0A     *%0A     * @param backingMap a distributed, strongly consistent map for backing%0A     */%0A
     public C
@@ -2524,32 +2524,449 @@
 idator);%0A    %7D%0A%0A
+    /**%0A     * Constructor to configure cache size of LoadingCache.%0A     *%0A     * @param backingMap a distributed, strongly consistent map for backing%0A     * @param cacheSize the maximum size of the cache%0A     */%0A    public CachingAsyncConsistentMap(AsyncConsistentMap%3CK, V%3E backingMap, int cacheSize) %7B%0A        super(backingMap);%0A        super.addListener(cacheInvalidator);%0A        maxCacheSize = cacheSize;%0A    %7D%0A%0A
     @Override%0A  
@@ -3488,36 +3488,32 @@
                 
-    
 .whenComplete((r
@@ -3675,36 +3675,32 @@
                 
-    
 .whenComplete((r
@@ -3874,36 +3874,32 @@
                 
-    
 .whenComplete((r
@@ -4051,36 +4051,32 @@
                 
-    
 .whenComplete((r
@@ -4198,28 +4198,24 @@
 per.clear()%0A
-    
             

Add constructor for configuration of CacheSize