package com.github.davidmoten.grumpy.wms;

import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * Caches images keyed on the {@link WmsRequest}.
 * 
 * @author dxm
 * 
 */
public class ImageCache {

    private static Logger log = LoggerFactory.getLogger(ImageCache.class);

    private static int DEFAULT_SIZE = 250;// 50MB at 200K per image

    /**
     * Records the keys and the order they went into the cache so we can trim
     * the cache if needed.
     */
    private volatile List<String> keys = new ArrayList<String>();

    private volatile Set<String> layers = new HashSet<String>();

    private volatile Map<String, byte[]> cache = new ConcurrentHashMap<String, byte[]>();

    private final int maxSize;

    /**
     * Constructor.
     */
    public ImageCache() {
        this(DEFAULT_SIZE);
    }

    /**
     * Constructor.
     * 
     * @param size
     *            the maximum number of elements in the cache
     */
    public ImageCache(int size) {
        this.maxSize = size;
    }

    public static ImageCache create(int size) {
        return new ImageCache(size);
    }

    public void clear(String layerName) {
        synchronized (this) {
            log.info("clearing cache for layer " + layerName);
            for (String key : cache.keySet()) {
                if (key.contains(layerName))
                    remove(key);
            }
        }
    }

    private void remove(String key) {
        cache.remove(key);
        log.info("removed cache entry " + key);
    }

    public void clear() {
        synchronized (this) {
            cache.clear();
        }
    }

    public void setEnabled(String layerName, boolean enabled) {
        synchronized (this) {
            if (enabled)
                layers.add(layerName);
            else
                layers.remove(layerName);
        }
    }

    private static String getKey(WmsRequest request) {
        StringBuffer s = new StringBuffer();
        for (String name : request.getParameterNames())
            // make sure we exclude the _OLSALT parameter which changes with
            // every request
            if (!name.startsWith("_"))
                add(s, name, request.getParam(name));
        return s.toString();
    }

    private static void add(StringBuffer s, String name, Object value) {
        s.append(name);
        s.append("=");
        s.append(String.valueOf(value));
        s.append(";");
    }

    public byte[] get(WmsRequest request) {
        synchronized (this) {
            log.info("cache size=" + cache.size());
            return cache.get(getKey(request));
        }
    }

    public synchronized void put(WmsRequest request, byte[] image) {
        synchronized (this) {
            String key = getKey(request);
            // make sure it's the last on the list of keys so won't be dropped
            // from cache
            keys.remove(key);
            keys.add(key);
            if (keys.size() > maxSize)
                remove(keys.get(0));
            if (maxSize > 0 && layers.containsAll(request.getLayers())) {
                cache.put(key, image);
                log.info("cached image with key=" + key);
            }
        }
    }

    public ImageCache add(String layerName) {
        setEnabled(layerName, true);
        return this;
    }

}
@@ -1194,95 +1194,355 @@
     
-public static ImageCache create(int size) %7B%0A        return new ImageCache(size);%0A    %7D%0A
+/**%0A     * Factory method. Returns a new %7B@link ImageCache%7D of given maximum size.%0A     * %0A     * @param size%0A     * @return%0A     */%0A    public static ImageCache create(int size) %7B%0A        return new ImageCache(size);%0A    %7D%0A%0A    /**%0A     * Clears the cache for the given layer name.%0A     * %0A     * @param layerName%0A     */%0A    // TOOD improve this
 %0A   
@@ -1942,32 +1942,73 @@
  + key);%0A    %7D%0A%0A
+    /**%0A     * Clears the cache.%0A     */%0A
     public void 
@@ -2083,32 +2083,147 @@
        %7D%0A    %7D%0A%0A
+    /**%0A     * Enables/disables a layer for caching.%0A     * %0A     * @param layerName%0A     * @param enabled%0A     */%0A
     public void 
@@ -3014,32 +3014,228 @@
 nd(%22;%22);%0A    %7D%0A%0A
+    /**%0A     * Get the bytes of the image returned by a %7B@link WmsRequest%7D. Returns null%0A     * if no corresponding image exists in the cache.%0A     * %0A     * @param request%0A     * @return%0A     */%0A
     public byte%5B
@@ -3400,32 +3400,144 @@
        %7D%0A    %7D%0A%0A
+    /**%0A     * Sets the cached image for the request.%0A     * %0A     * @param request%0A     * @param image%0A     */%0A
     public synch
@@ -4097,24 +4097,141 @@
    %7D%0A    %7D%0A%0A
+    /**%0A     * Flags the given layer as a layer to be cached.%0A     * %0A     * @param layerName%0A     * @return%0A     */%0A
     public I

add javadoc