package fr.univrennes1.cri.util.cacheManagement;

import java.util.Enumeration;
import java.util.HashMap;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.Vector;

import org.apache.log4j.Logger;

/**
 * @author Thomas Bellembois
 * @version 1.0
 * Created on 19 jan. 2005
 */

public class CacheManager {
	
	private static final String CLASS_NAME = "fr.univrennes1.cri.util.cacheManagement.CacheManager";
	
	// the default cacheCleaner thread spleeping time
	private int DEFAUT_SLEEPING_TIME=5000;
	
	// Hashtable that containing all objects in the cache
	private static Hashtable cacheHashMap;
	
	// thread in charge of cleaning the HashMap
	private static CacheCleaner cacheCleaner;

	public static Logger logger;
	
	
	public synchronized static Vector dumpCache() {
		Vector cacheDump = new Vector();
		Enumeration keys = cacheHashMap.keys();
		while (keys.hasMoreElements()) {
			Object key = (Object) keys.nextElement();
			Cacheable element = (Cacheable)cacheHashMap.get(key);
			cacheDump.add(element);
		}// while (keys.hasMoreElements())
		return cacheDump;
	}// dumpCache() 

	
	public CacheManager(int milliSecondSleepingTime){
		cacheHashMap = new Hashtable();
		cacheCleaner = new CacheCleaner(milliSecondSleepingTime);
		cacheCleaner.setDaemon(true);
		logger = Logger.getLogger(this.getClass().getName());
		cacheCleaner.start();
	}// CacheManager()
	
	
	public CacheManager(){
		cacheHashMap = new Hashtable();
		cacheCleaner = new CacheCleaner(DEFAUT_SLEEPING_TIME);
		cacheCleaner.setDaemon(true);
		logger = Logger.getLogger(this.getClass().getName());
		cacheCleaner.start();
	}// CacheManager()
	
	
	public synchronized static void putCache(Cacheable object){	
		if (object != null) {
			if(logger.isDebugEnabled()) logger.debug(CLASS_NAME+":putCache:"+object.toString());
			cacheHashMap.put(object.getIdentifier(), object);
		}// if (object != null)
		else {
			if(logger.isDebugEnabled()) logger.debug(CLASS_NAME+":putCache:null object - not put in the cache !!");
		}// else (object != null)
	}// putCache(Cacheable object)
	
	
	public synchronized static Cacheable getCache(String identifier){
		
		Cacheable object = null;
		
		if (identifier != null){
			if (logger.isDebugEnabled()) logger.debug(CLASS_NAME+":getCache:"+identifier.toString());
			
			// getting the object in the HashMap thanks to its identifier
			object = (Cacheable)cacheHashMap.get(identifier);
			
			// if the object is expired then removing it from the HashMap
			if (object != null && object.isExpired()){
				if (logger.isDebugEnabled() && object != null) logger.debug(CLASS_NAME+":getCache:Object "+object.toString()+" expired !");
				cacheHashMap.remove(identifier);
				object = null; // do not return an expired object !
			}// if (object != null && object.isExpired())
		}// if (identifier != null)
		else if (logger.isDebugEnabled()) logger.debug(CLASS_NAME+":getCache:identifier null !");
		
		if (logger.isDebugEnabled() && object != null) logger.debug(CLASS_NAME+":getCache:object returned from the cache - object="+object.toString());
		if (logger.isDebugEnabled() && object == null) logger.debug(CLASS_NAME+":getCache:object not found in the cache");
		return object;
	}// getCache(Object identifier)
	
	
	public synchronized static void deleteCache(){
		
		if (logger.isDebugEnabled()) logger.debug(CLASS_NAME+":deleteCache");
		cacheHashMap.clear();
		
	}// public synchronized static void deleteCache()
	
	
	public synchronized static void cleanCache(){
	
		if (logger.isDebugEnabled()) logger.debug(CLASS_NAME+":cleanCache");
		// Iterator used to parse the HashMap
		Iterator keysIterator = cacheHashMap.keySet().iterator();
		// Vector (and Enumeration) used to save entries to remove from the HashMap - we can not do that while iterating...
		Vector entriesToRemoveVector = new Vector();
		Enumeration entriesToRemoveEnumeration;
		
		// Iterating the HashMap to find expired entries
		while(keysIterator != null && keysIterator.hasNext()){
		
			Object key = keysIterator.next();
			Cacheable value = (Cacheable)cacheHashMap.get(key);
			
			if (value.isExpired()){
				entriesToRemoveVector.add(key);
				if (logger.isDebugEnabled()) logger.debug(CLASS_NAME+":cleanCache:expired entry found - key="+key.toString());
			}// if (value.isExpired())
			
		}// while(keysIterator.hasNext())
		
		// Removing expired entries
		entriesToRemoveEnumeration = entriesToRemoveVector.elements();
		while (entriesToRemoveEnumeration.hasMoreElements()) {
			Object key = (Object) entriesToRemoveEnumeration.nextElement();
			logger.debug(CLASS_NAME+":cleanCache:removing expired entry - key="+key.toString());
			cacheHashMap.remove(key);
		}// while (entriesToRemoveEnumeration.hasMoreElements())
		
	}// cleanCache()
	
	private class CacheCleaner extends Thread {
		
		private int milliSecondSleepingTime;
		
		public CacheCleaner(int milliSecondSleepingTime){
			// min. milliSecondSleepingTime = 1000 msec.
			if (milliSecondSleepingTime == 0) this.milliSecondSleepingTime = 1000;
			else this.milliSecondSleepingTime = milliSecondSleepingTime;
		}// CacheCleaner
		
		public void run(){
			
			while (true){
				
				logger.info(CLASS_NAME+":run:deleting the cache...");
				//cleanCache();
				deleteCache();	
				
				try {
					Thread.sleep(milliSecondSleepingTime);
				} catch (InterruptedException e) {
					logger.fatal(CLASS_NAME+"$CacheCleaner:run:"+e.getMessage());
				}
				
			}// while (true){
			
		}// run

	}// CacheCleaner
	
	
	
	
}// CacheManager2
