package levik.util;

import java.util.*;

public class Cache {
    protected Hashtable cache;
    protected int maxSize;
    protected CacheTimer timer;

    public Cache(int maxSize) {
	this(maxSize,10*60*1000);
    }

    public Cache(int maxSize, int timeOut) {
	this.maxSize=maxSize;
	cache = new Hashtable();
	timer = new CacheTimer(timeOut);
	timer.start();
    }

    public synchronized Object get(Object key) {
	CacheEntry entry = (CacheEntry)cache.get(key);
	if (entry==null) return null;
	return entry.getObject();
    }
    
    public synchronized Object put(Object key, Object value) {
	System.err.println("CACHE:: put("+key+","+value+")");
	Object oldObject = null;
        CacheEntry entry = (CacheEntry)cache.get(key);
        if (entry == null) {
            if (cache.size()==maxSize) {
                Object removeKey=findOldestEntry();
                entry = (CacheEntry)cache.get(removeKey);
                cache.remove(removeKey);
		oldObject=entry.setObject(value);
            } else {
                entry = new CacheEntry(value);
            }
            cache.put(key, entry);
        } else {
	    entry.setObject(value);
	}
	return oldObject;
    }


    protected Object findOldestEntry() {
	int maxAge=-1;
	Object key;
	Object oldKey=null;
	CacheEntry entry;
	for (Enumeration en = cache.keys();en.hasMoreElements();) {
	    entry = (CacheEntry)cache.get(key=en.nextElement());
	    if (maxAge<entry.getAge()) {
		oldKey=key;
		maxAge=entry.getAge();
	    }
	}
	return oldKey;
    }

    protected void finalize() throws Throwable {
        timer.die();
        super.finalize();
    }

    public Enumeration getCacheKeys() {
	return cache.keys();
    }

    class CacheTimer extends Thread {
	protected int timeOut;
	protected boolean keepRunning;

	CacheTimer(int timeOut) {
	    this.timeOut=timeOut;
	    keepRunning=true;
	}
	
	public void die() {
	    keepRunning=false;
	    interrupt();
	}

	public void run() {
	    while (true) {
		try {
		    Thread.sleep(timeOut);
		} catch (InterruptedException ie) {
		    if (!keepRunning) 
			break;
		}
		for (Enumeration e = cache.elements(); e.hasMoreElements(); ) {
                    ((CacheEntry)e.nextElement()).age();
                }
	    }
	}
    }
}
