package org.jgroups.util;

import java.util.Collection;
import java.util.Map;
import java.util.concurrent.*;

/** Cache which removes its elements after a certain time
 * @author Bela Ban
 */
public class AgeOutCache<K> {
    private final TimeScheduler timer;
    private long timeout;
    private final ConcurrentMap<K,Future> map=new ConcurrentHashMap<K,Future>();
    private Handler handler=null;

    public interface Handler<K> {
        void expired(K key);
    }


    public AgeOutCache(TimeScheduler timer, long timeout) {
        this.timer=timer;
        this.timeout=timeout;
    }

    public AgeOutCache(TimeScheduler timer, long timeout, Handler handler) {
        this(timer, timeout);
        this.handler=handler;
    }

    public long getTimeout() {
        return timeout;
    }

    public void setTimeout(long timeout) {
        this.timeout=timeout;
    }

    public Handler getHandler() {
        return handler;
    }

    public void setHandler(Handler handler) {
        this.handler=handler;
    }

    public void add(final K key) {
        Future<?> future=timer.schedule(new Runnable() {
            public void run() {
                if(handler != null) {
                    try {
                        handler.expired(key);
                    }
                    catch(Throwable t) {
                    }
                }
                Future<?> tmp=map.remove(key);
                if(tmp != null)
                    tmp.cancel(true);
            }
        }, timeout, TimeUnit.MILLISECONDS);
        Future<?> result=map.putIfAbsent(key, future);
        if(result != null)
            future.cancel(true);
    }

    public boolean contains(K key) {
        return key != null && map.containsKey(key);
    }

    public void remove(K key) {
        Future<?> future=map.remove(key);
        if(future != null)
            future.cancel(true);
    }

    public void removeAll(Collection<K> keys) {
        if(keys != null) {
            for(K key: keys)
                remove(key);
        }
    }

    public void clear() {
        for(Future<?> future: map.values())
            future.cancel(true);
        map.clear();
    }

    public int size() {
        return map.size();
    }

    public String toString() {
        StringBuilder sb=new StringBuilder();
        for(Map.Entry<K,Future> entry: map.entrySet()) {
            sb.append(entry.getKey()).append(": ").append(entry.getValue()).append("\n");
        }
        return sb.toString();
    }
}
