package br.ufrgs.inf.prosoft.adaptivecaching.cachemanager.model.support;


import br.ufrgs.inf.prosoft.adaptivecaching.cachemanager.model.Cache;
import br.ufrgs.inf.prosoft.adaptivecaching.cachemanager.model.CacheManager;
import br.ufrgs.inf.prosoft.adaptivecaching.cachemanager.model.ValueWrapper;

import java.util.Collection;
import java.util.Collections;
import java.util.LinkedHashSet;
import java.util.Set;
import java.util.concurrent.Callable;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;

/**
 * A basic, no operation {@link br.ufrgs.inf.prosoft.adaptivecaching.cachemanager.model.CacheManager} implementation suitable
 * for disabling caching, typically used for backing model declarations
 * without an actual backing store.
 * <p>
 * <p>Will simply accept any items into the model not actually storing them.
 */
public class NoOpCacheManager implements CacheManager {

    private final ConcurrentMap<String, Cache> caches = new ConcurrentHashMap<String, Cache>(16);

    private final Set<String> cacheNames = new LinkedHashSet<String>(16);


    /**
     * This implementation always returns a {@link Cache} implementation that will not store items.
     * Additionally, the request model will be remembered by the manager for consistency.
     */
    @Override
    public Cache getCache(String name) {
        Cache cache = this.caches.get(name);
        if (cache == null) {
            this.caches.putIfAbsent(name, new NoOpCache(name));
            synchronized (this.cacheNames) {
                this.cacheNames.add(name);
            }
        }

        return this.caches.get(name);
    }

    /**
     * This implementation returns the name of the caches previously requested.
     */
    @Override
    public Collection<String> getCacheNames() {
        synchronized (this.cacheNames) {
            return Collections.unmodifiableSet(this.cacheNames);
        }
    }


    private static class NoOpCache implements Cache {

        private final String name;

        /**
         * Instantiates a new No op cache.
         *
         * @param name the name
         */
        public NoOpCache(String name) {
            this.name = name;
        }

        @Override
        public void clear() {
        }

        @Override
        public void evict(Object key) {
        }

        @Override
        public ValueWrapper get(Object key) {
            return null;
        }

        @Override
        public <T> T get(Object key, Class<T> type) {
            return null;
        }

        @Override
        public <T> T get(Object key, Callable<T> valueLoader) {
            return null;
        }

        @Override
        public String getName() {
            return this.name;
        }

        @Override
        public Object getNativeCache() {
            return null;
        }

        @Override
        public void put(Object key, Object value) {
        }

        @Override
        public ValueWrapper putIfAbsent(Object key, Object value) {
            return null;
        }
    }

}
