package com.fp.persistence.commondb.cache;

import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.StringTokenizer;

/**
 * Clase utilitaria, que mantiene las variables de cache de informacion de beans
 * asociados a tablas de la base de datos.
 * 
 * @author Jorge Vaca.
 * @version 2.1
 */
public final class CacheManager {

	private CacheManager() {
	}

	/** Almacena una instancia de DataHelper. */
	private static CacheManager cache;

	/**
	 * Entrega una instancia de DataHelper.
	 * 
	 * @return DataHelper
	 */
	public static CacheManager getInstance() {
		if (cache != null) {
			return cache;
		}
		synchronized (CacheManager.class) {
			if (cache == null) {
				cache = new CacheManager();
			}
		}
		return cache;
	}

	/** Map que almacena definicion de un bean </br>  
	 * El key del map es el nombre del bean.
	 * Object contiene registros dado el pk o una clave que identifica al registro.
	 * */
	private Map<String, Object> mcache = new HashMap<String,Object>();

	/**
	 * Metodo que entrega datos dado el bean y el key de los datos.
	 * @param pBeanName  Nombre del bean.
	 * @param pKey  Key de datos.
	 * @return  Object.
	 * @throws Exception
	 */
	@SuppressWarnings("unchecked")
	public Object getData(String pBeanName,String pKey) throws Exception {
		Map<String, Object> mdata = (Map<String, Object>)mcache.get(pBeanName);
		if(mdata == null){
			return null;
		}
		return mdata.get(pKey);
	}
	
	/**
	 * Metodo que entrega la defincion de un map, si existe en cache. 
	 * @param pBeanName  Nombre del bean.
	 * @return  Map<String, Object>
	 * @throws Exception
	 */
	@SuppressWarnings("unchecked")
	public Map<String, Object> getMapDefinition(String pBeanName) throws Exception {
		Map<String, Object> mdata = (Map<String, Object>)mcache.get(pBeanName);
		if(mdata == null){
			mdata = new HashMap<String, Object>();
		}
		return mdata;
	}
	
	/**
	 * Metodo que adiciona datos de una tabla al cache. 
	 * @param pBeanName  Nombre del bean.
	 * @param pData  Datos a adicionar al cache. 
	 * @throws Exception
	 */
	public void putData(String pBeanName,Object pData) throws Exception {
		synchronized (mcache) {
			if (!mcache.containsKey(pBeanName)) {
				mcache.put(pBeanName, pData);
			}
		}	
	}
	
	/**
	 * Metodo que se encarga de limpiar el cache de un objeto. 
	 * @param pBeanName  Key del objeto a limpiar el cache. 
	 * @throws Exception
	 */
	@SuppressWarnings("unchecked")
	public void clean(String pBeanName) throws Exception {
		String name = pBeanName;
		if(pBeanName.indexOf(".") > 0){
			StringTokenizer st = new StringTokenizer(pBeanName, ".");				
			while (st.hasMoreElements()) {
				name = (String) st.nextElement();					
			}				
		}
		this.cleanByObject((Map<String, Object>)mcache.get(name));
		synchronized (mcache){
			mcache.remove(name);	
		}		
	}

	/**
	 * Metodo que se encarga el cache total de datos. 
	 * @throws Exception
	 */
	@SuppressWarnings("unchecked")
	public void cleanAll() throws Exception {
		Iterator<Object> itr = mcache.values().iterator();
		while (itr.hasNext()) {
			this.cleanByObject((Map<String, Object>) itr.next());
		}
	}

	/**
	 * Metodo que limpia el cache de un objeto, este puede ser una lista de registros de una tabla 
	 * o un registro unico de la tabla.
	 * @param mData  Map que contiene los datos a encerar.
	 * @throws Exception
	 */
	@SuppressWarnings("rawtypes")
	private void cleanByObject(Map<String, Object> mData) throws Exception {
		if(mData == null){
			return;
		}
		synchronized (mData) {
			Iterator<Object> itr = mData.values().iterator();
			while (itr.hasNext()) {
				Object obj = itr.next();
				if (obj instanceof List) {
					((List) obj).clear();
				}
			}
			mData.clear();
		}
	}

}
