/*******************************************************************************
 * This file is part of GECAMed.
 * 
 * GECAMed is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License (L-GPL) as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 * 
 * GECAMed is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License for more details.
 * 
 * You should have received a copy of the GNU Lesser General Public License (L-GPL)
 * along with GECAMed.  If not, see <http://www.gnu.org/licenses/>.
 * 
 * GECAMed is Copyrighted by the Centre de Recherche Public Henri Tudor (http://www.tudor.lu)
 * (c) CRP Henri Tudor, Luxembourg, 2008
 *******************************************************************************/
package lu.tudor.santec.gecamed.core.gui;

import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.lang.reflect.Array;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import lu.tudor.santec.gecamed.core.ejb.entity.beans.GECAMedEntityBean;
import lu.tudor.santec.gecamed.core.ejb.session.beans.ListManagerBean;
import lu.tudor.santec.gecamed.core.ejb.session.interfaces.ListManagerInterface;
import lu.tudor.santec.gecamed.core.utils.ManagerFactory;

import org.apache.log4j.Level;
import org.apache.log4j.Logger;

/**
 *
 * class with static methods to store and retrieve global lists in the GECAMed client.
 * use this class to avoid the loading and storing of global lists in each module you 
 * use them.
 *
 * @author Johannes Hermen johannes.hermen(at)tudor.lu
 * 
 * @Version
 * <br>$Log: GECAMedLists.java,v $
 * <br>Revision 1.17  2013-12-27 18:09:26  donak
 * <br>Cleanup of imports
 * <br>
 * <br>Revision 1.16  2013-07-15 06:18:35  ferring
 * <br>logging changed
 * <br>
 * <br>Revision 1.15  2013-02-20 10:45:41  ferring
 * <br>GECAMedLists only for GECAMedEntityBeans
 * <br>
 * <br>Revision 1.14  2013-02-20 07:24:22  ferring
 * <br>logging changed
 * <br>
 * <br>Revision 1.13  2013-02-19 12:07:34  ferring
 * <br>GECAMedLists changed. Will now automatically load list of all beans
 * <br>
 * <br>Revision 1.12  2009-01-13 09:15:42  hermen
 * <br>reload addresses/zips after import bug id 209
 * <br>
 * <br>Revision 1.11  2008-12-05 17:34:46  heinemann
 * <br>added method to get a real list from the lists
 * <br>
 * <br>Revision 1.10  2008-09-25 09:43:07  heinemann
 * <br>fixed copyrights
 * <br>
 * <br>Revision 1.9  2008-06-11 07:46:55  heinemann
 * <br>*** empty log message ***
 * <br>
 * <br>Revision 1.8  2008-01-15 09:29:39  hermen
 * <br>updated Javadoc and refactured code
 * <br>
 * <br>Revision 1.7  2007-12-03 10:59:02  hermen
 * <br>updated Javadoc
 * <br>
 *
 */
/**
 * @author jens.ferring(at)tudor.lu
 */
public class GECAMedLists {

	/** the logger Object for this class */
	private static Logger logger = Logger.getLogger(GECAMedLists.class.getName());
	
	/**
	 * Hashmap to store the lists with their item class as key
	 */
	private static Map<Class<?>, ArrayList<?>> lists = Collections.synchronizedMap(new HashMap<Class<?>, ArrayList<?>>());
	
	/**
	 * Hashmap to stroe the maps that map the ID of an object to the object
	 */
	private static Map<Class<?>, HashMap<Integer, ?>> idMap = Collections.synchronizedMap(new HashMap<Class<?>, HashMap<Integer, ?>>());
	
	/**
	 * Hashmap to store the listeners that are registered for list updates
	 */
	private static Map<Class<?>, List<PropertyChangeListener>> listenerHashMap = Collections.synchronizedMap(new HashMap<Class<?>, List<PropertyChangeListener>>());
	
	
	/**
	 * returns an array of the specified class.
	 * e.g. Gender.class -> list of all genders
	 * if no list is found in the hash, null is returned 
	 * @param clazz the class of list items
	 * @return the list with all items
	 */
	@SuppressWarnings("unchecked")
	public static <T extends GECAMedEntityBean> T[] getArray (Class<T> clazz) 
	{
		List<T> list;
		T[]		array;
		
		
		list	= getListReference(clazz);
		if (list != null)
		{
			array	= (T[]) Array.newInstance(clazz, list.size());
			array	= list.toArray(array);
			return array;
		}
		
		return null;
	}
	
	

	/**
	 * 
	 * @param clazz
	 * @return
	 */
	public static <T extends GECAMedEntityBean> List<T> getListCopy (Class<T> clazz)
	{
		return new ArrayList<T>(getListReference(clazz));
	}
	
	
	@SuppressWarnings("unchecked")
	public static synchronized <T extends GECAMedEntityBean> List<T> getListReference (Class<T> clazz) 
	{
		ListManagerInterface	manager;
		ArrayList<T>			list;
		
		list	= (ArrayList<T>) lists.get(clazz);
		if (list == null)
		{
			try
			{
				manager	= (ListManagerInterface) ManagerFactory.getRemote(ListManagerBean.class);
				list	= new ArrayList<T>(manager.getAllBeans(clazz));
				// sort the list
				Collections.sort(list);
				lists.put(clazz, list);
			}
			catch (Exception e)
			{
//				logger.fine("List: " + clazz.getName() + " does not exist");
				logger.log(Level.ERROR, "Couldn't load list of "+clazz.getSimpleName()+" beans.", e);
			}
		}
		
		return list;
	}
	
	
	public static <T extends GECAMedEntityBean> HashMap<Integer, T> getIdMapReference (Class<T> clazz)
	{
		HashMap<Integer, T> map = (HashMap<Integer, T>) idMap.get(clazz);
		
		if (map == null)
		{
			List<T> list = getListReference(clazz);
			if (list == null)
				return null;
			
			map = new HashMap<Integer, T>();
			for (T t : list)
				map.put(t.getId(), t);
			
			idMap.put(clazz, map);
		}
		
		return map;
	}
	
	
//	/**
//	 * stores the specified list with the specified class in the hashmap
//	 * @param cl the item class
//	 * @param list the list to store
//	 */
//	public static <T extends GECAMedEntityBean> void storeList (Class<T> clazz, T[] list) 
//	{
//		storeList(clazz, Arrays.asList(list));
//	}
//	
//	
//	public static <T extends GECAMedEntityBean> void storeList (Class<T> clazz, List<T> list)
//	{
//		ArrayList<T>	arrayList;
//		
//		
//		if (list instanceof ArrayList)
//		{
//			arrayList	= (ArrayList<T>) list;
//		}
//		else
//		{
//			arrayList	= new ArrayList<T>(list);
//		}
//		
//		lists.put(clazz, arrayList);
//	}
	
	
	public static <T extends GECAMedEntityBean> void resetListAndNotifyListener (Class<T> clazz)
	{
		lists.remove(clazz);
		idMap.remove(clazz);
		notifyListeners(clazz);
	}
	
	
	/**
	 * registers a listener that is informed if the list belonging to the specified item class changes
	 * @param listener the listener to register 
	 * @param clazz the item class of the list to be informed about
	 */
	public static <T extends GECAMedEntityBean> void addPropertyChangeListener (PropertyChangeListener listener, Class<T> clazz)
	{
		if (!listenerHashMap.keySet().contains(clazz))
			listenerHashMap.put(clazz, new ArrayList<PropertyChangeListener>());
		
		listenerHashMap.get(clazz).add(listener);
	}


	/**
	 * Notifies all registered listeners
	 * @param clazz item class for the list that changed.
	 */
	public static <T extends GECAMedEntityBean> void notifyListeners (Class<T> clazz)
	{
		T[] values = getArray(clazz);
		PropertyChangeEvent ev = new PropertyChangeEvent(values, clazz.getSimpleName(), null, values);
		
		if (!listenerHashMap.containsKey(clazz))
			return;
		
		for (PropertyChangeListener l : listenerHashMap.get(clazz))
		{
			try
			{
				l.propertyChange(ev);
			}
			catch (Exception e)
			{
				logger.log(Level.ERROR, "Error while notifying GECAMedLists listener \""+l.getClass().getName()+"\" of the change of bean "+clazz.getSimpleName(), e);
			}
		}
	}
}
