// ResourceCache.java
// $Id: ResourceCache.java,v 1.4 1997/07/30 14:07:21 ylafon Exp $  
// (c) COPYRIGHT MIT and INRIA, 1997.
// Please first read the full copyright statement in file COPYRIGHT.html

package w3c.tools.resources.impl;

import java.util.*;

import w3c.tools.resources.*;

class ResourceCacheFlusher extends Thread {
    public final static int THREAD_PRIORITY = 10;
    
    protected ResourceCache cache  = null;
    protected boolean       wakeup = false;

    public synchronized void sweep() {
	wakeup = true;
	notify();
    }

    protected void doSweep() {
System.out.println("doSweep: running !");
	long        since = cache.bumpLruDate();
	Enumeration e     = cache.references.elements();
	while ( e.hasMoreElements() ) {
	    ResourceReferenceImpl rr = (ResourceReferenceImpl) e.nextElement();
	    synchronized(rr) {
		if ( rr.pointer == null )
		    continue;
		if (rr.lruDate <= since 
		    && rr.lockCount == 0
		    && rr.pointer.unload())
		    rr.invalidate();
	    } 
	}
    }

    public void run() {
System.out.println("cacheSweeper: started");
	while ( true ) {
	    synchronized(this) {
		while ( ! wakeup ) {
		    try {
			wait();
		    } catch (InterruptedException ex) {
			return;
		    }
		}
	    }
	    try {
		doSweep();
	    } catch (Exception ex) {
		ex.printStackTrace();
	    }
	    synchronized(this) {
		wakeup = false;
	    }
	}
    }

    ResourceCacheFlusher(ResourceCache cache) {
	this.cache = cache;
	setName("CacheFlusher");
	setDaemon(true);
	setPriority(THREAD_PRIORITY);
	start();
    }


}

/**
 * A generic resource cache to be used by resource space implementations.
 * This class provides the management of resource references as required
 * by a resource space. It works in conjunction with the PersistentReference
 * interface.
 */

public class ResourceCache {
    /**
     * The space we 're caching.
     */
    protected CacheRevalidator space = null;
    protected ResourceCacheFlusher flusher = null;
    /**
     * The set of references already created.
     */
    protected Hashtable references = null;
    /**
     * Current LRU date.
     */
    protected long lruDate = 0;
    /**
     * Number of resources "marked" since lruDate was incremented.
     */
    protected int lruMarked = 0;
    /**
     * Size of the cache (number of simultaneously valid resource references)
     */    
    protected int cacheSize = 200;  
    protected int cacheWaterMark = -1;
    
    protected synchronized long bumpLruDate() {
	long oldDate = lruDate;
	lruDate++;
	// WRONG !! lruMarked = 0;
	return oldDate;
    }

    protected void flush() {
	flusher.sweep();
    }

    protected final synchronized void incrMarkCount() {
	if (++lruMarked >= cacheWaterMark)
	    flush();
    }

    protected final synchronized void decrMarkCount() {
	--lruMarked;
    }
    
    protected Resource revalidate(ResourceReferenceImpl rr) {
	return space.revalidate(rr.pr);
    }

    public ResourceReference getReference(PersistentReference pr
					  , Resource resource) {
	ResourceReferenceImpl rr = (ResourceReferenceImpl) references.get(pr);
	if ( rr == null ) {
	    synchronized(this) {
		// Create a new reference, mark it valid:
		rr = new ResourceReferenceImpl(this, pr, resource);
		rr.lruDate = lruDate;
		incrMarkCount();
		references.put(pr, rr);
	    }
	}
	return rr;
    }

    public synchronized void setCacheSize(int cacheSize) {
	this.cacheSize      = cacheSize ;
	this.cacheWaterMark = (int) (((double) cacheSize) * 0.8);
    }

    public int getCacheSize() {
	return cacheSize;
    }

    public ResourceCache(CacheRevalidator space) {
	this.space      = space;
	this.references = new Hashtable(23);
	this.flusher    = new ResourceCacheFlusher(this);
	setCacheSize(100);
    }
}
