/********************************************************************
 * Copyright © 2004 VHA. All rights reserved
 ********************************************************************/
package gov.va.med.esr.common.persistent.lookup.hibernate;

// Java Classes
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import org.hibernate.CacheMode;
import org.hibernate.Criteria;
import org.hibernate.HibernateException;
import org.hibernate.Query;
import org.hibernate.Session;
import org.hibernate.criterion.Expression;
import org.hibernate.criterion.Order;
import org.springframework.orm.hibernate3.HibernateCallback;

import gov.va.med.esr.common.persistent.lookup.LookupsDAO;
import gov.va.med.fw.model.lookup.AbstractLookup;
import gov.va.med.fw.persistent.DAOException;
import gov.va.med.fw.persistent.hibernate.AbstractDAOAction;
import gov.va.med.fw.persistent.hibernate.AbstractDAOImpl;

/**
 * @author Martin Francisco
 */
public class LookupsDAOImpl
    extends AbstractDAOImpl
    implements LookupsDAO
{
    /**
	 * 
	 */
	private static final long serialVersionUID = -8031874358812536848L;

	private boolean cacheQuery = false;
    
    private String cacheRegion = null;
    
    private String queryNameFindAll = null;
    
    private String queryNameGetByCode = null;
    
    private String sortByColumnName = null;
    
    private boolean reverseSort = false;
    
    private boolean filterInactiveRows = true;
    
	private List validCodes = null;

	/**
     * Default constructor.
     */
    public LookupsDAOImpl()
    {
        super();
    }

    public boolean isCacheQuery()
    {
        return this.cacheQuery;
    }

    public void setCacheQuery(boolean cacheQuery)
    {
        this.cacheQuery = cacheQuery;
    }

    public String getCacheRegion()
    {
        return this.cacheRegion;
    }

    public void setCacheRegion(String cacheRegion)
    {
        this.cacheRegion = cacheRegion;
    }

    /**
     * Returns the query name for the find all method.
     * 
     * @return The query name for the find all method.
     */
    public String getQueryNameFindAll()
    {
        return this.queryNameFindAll;
    }

    /**
     * Sets the query name for the find all method.
     * 
     * @param queryNameFindAll
     *        The query name for the find all method.
     */
    public void setQueryNameFindAll(String queryNameFindAll)
    {
        this.queryNameFindAll = queryNameFindAll;
    }

    /**
     * Returns the query name for the get by code method.
     * 
     * @return The query name for the get by code method.
     */
    public String getQueryNameGetByCode()
    {
        return this.queryNameGetByCode;
    }

    /**
     * Sets the query name for the get by code method.
     * 
     * @param queryNameGetByCode
     *        The query name for the get by code method.
     */
    public void setQueryNameGetByCode(String queryNameGetByCode)
    {
        this.queryNameGetByCode = queryNameGetByCode;
    }

    /*
     * 
     * 
     * @see gov.va.med.esr.common.persistent.LookupsDAO#findAll()
     */
    public List findAll(Class lookupType) throws DAOException
    {
    	List data = null;
        try
        {
        	// if subclasss provided custom query, use it.
        	if (getQueryNameFindAll() != null) {
        		HibernateCallback callback = new AbstractDAOAction() {
        			public Object execute(Session session) {
        	            Query query = session.getNamedQuery(getQueryNameFindAll());
    	
    	 				if (isCacheQuery()) {
    						query.setCacheable(true);
    						query.setCacheRegion(getCacheRegion());
    					}
    	
    					return query.list();        				
        			}
        		};
        		data = this.getHibernateTemplate().executeFind(callback);
        		
			} else {        	        	
				// otherwise use default mechanism
        		Map contextData = new HashMap();
        		contextData.put("lookupType", lookupType);				
	    		HibernateCallback callback = new AbstractDAOAction(contextData) { 
	    			public Object execute(Session session) {
	    				Criteria criteria = createBaseCriteria(session, (Class) getContextData().get("lookupType"));
	    				
	    				// add optional sorting column and sorting order
	    				String orderColumn = getSortByColumnName();
	    				if (orderColumn != null) {
	    					if (isReverseSort())
	    						criteria.addOrder(Order.desc(orderColumn));
	    					else
	    						criteria.addOrder(Order.asc(orderColumn));
	    				}									
	    								
	    				return criteria.list();    				
	    			}
	    		};
	    		data = this.getHibernateTemplate().executeFind(callback);
			}
        	return data;
		}
        catch(HibernateException e)
        {
            throw new DAOException("Failed to find all the lookups.", e);
        }
    }

    /*
     * Creates a basic criteria object with (optionally) inacive records filtered out;
     * and query cache enabled.
     */
    private Criteria createBaseCriteria(Session session, Class lookupType)
    {
    	return createBaseCriteria(session, lookupType, true /*default*/);
    }

    /*
     * Creates a basic criteria object with (optionally) inacive records filtered out;
     * and query cache enabled.  By default, we filter out inactive entries; but for UI
     * and messaging purposes, when getting by code, we need to allow Inactive entries 
     * for inbound or migrated records
     */
    private Criteria createBaseCriteria(Session session, Class lookupType, boolean doFiltering)
    {
		Criteria criteria = session.createCriteria(lookupType);
		
		if (doFiltering) {
			if (isFilterInactiveRows())
				criteria.add(Expression.eq("active", Boolean.TRUE));

			if (getValidCodes() != null) {
				criteria.add(Expression.in("code", getValidCodes()));
			}
		}
		
		criteria.setCacheable(true);
		criteria.setCacheRegion(this.cacheRegion);
		criteria.setCacheMode( CacheMode.NORMAL );
		return criteria;
  	
    }

    /*
     * 
     * 
     * @see gov.va.med.esr.common.persistent.LookupsDAO#getByCode()
     */
     public AbstractLookup getByCode(Class lookupType, String code) throws DAOException
    {
    	AbstractLookup lookupObj = null;
    	
        try
        {
           	// if subclasss provided custom query, use it.
        	if (getQueryNameGetByCode() != null) {
        		Map contextData = new HashMap();
        		contextData.put("lookupType", lookupType);				
        		contextData.put("code", code);        		
	    		HibernateCallback callback = new AbstractDAOAction(contextData) { 
	    			public Object execute(Session session) {
	            		Query query = session.getNamedQuery(getQueryNameGetByCode());
	            		query.setString("code", (String) getContextData().get("code"));

		                if(isCacheQuery())
		                {
		                    query.setCacheable(true);
		                    query.setCacheRegion(getCacheRegion());
		                    query.setCacheMode( CacheMode.NORMAL );
		                }

		                return query.setMaxResults(1).uniqueResult();	    				
	    			}
	    		};
	    		lookupObj = (AbstractLookup) this.getHibernateTemplate().execute(callback);	    		
        	}
			// otherwise use default mechanism
			else {
        		Map contextData = new HashMap();
        		contextData.put("lookupType", lookupType);				
        		contextData.put("code", code);        		
	    		HibernateCallback callback = new AbstractDAOAction(contextData) { 
	    			public Object execute(Session session) {
	    				Criteria criteria = createBaseCriteria(session, (Class) getContextData().get("lookupType"), false);
	    				criteria.add(Expression.eq("code", (String) getContextData().get("code")));
	    	
	    				Iterator iter = criteria.list().iterator();
	    				return iter.hasNext() ? iter.next() : null;
	    			}
	    		};
	    		lookupObj = (AbstractLookup) this.getHibernateTemplate().execute(callback);
			}
        	return lookupObj;
        }
        catch(HibernateException e)
        {
            throw new DAOException("Failed to get the lookup by code.  \n LookupType = " + lookupType.getName() + ", code = " + code + ".\n exception = " + e.getMessage(), e);
        }
    }

	/* (non-Javadoc)
	 * @see gov.va.med.esr.common.persistent.lookup.LookupsDAO#getByDescription(java.lang.Class, java.lang.String)
	 */
	public AbstractLookup getByDescription(Class lookupType, String description) 
		throws DAOException {
        try
        {
    		Map contextData = new HashMap();
    		contextData.put("lookupType", lookupType);				
    		contextData.put("description", description);        		
    		HibernateCallback callback = new AbstractDAOAction(contextData) { 
    			public Object execute(Session session) {
    				Criteria criteria = createBaseCriteria(session, (Class) getContextData().get("lookupType"));
    				criteria.add(Expression.ilike("description", (String) getContextData().get("description") +"%"));

    				//Returns the first object if found
    				Iterator iter = criteria.list().iterator();
    				return iter.hasNext() ? iter.next() : null;
    			}
    		};
    		return (AbstractLookup) this.getHibernateTemplate().execute(callback);
        }
        catch(HibernateException e)
        {
            throw new DAOException("Failed to get the lookup by description.  \n LookupType = " + lookupType.getName() + 
            		", description = " + description + ".\n exception = " + e.getMessage(), e);
        }
	}
    public AbstractLookup getByName(Class lookupType, String name) throws DAOException{
        try
        {
    		Map contextData = new HashMap();
    		contextData.put("lookupType", lookupType);				
    		contextData.put("name", name);        		
    		HibernateCallback callback = new AbstractDAOAction(contextData) { 
    			public Object execute(Session session) {
    				Criteria criteria = createBaseCriteria(session, (Class) getContextData().get("lookupType"));
    				criteria.add(Expression.ilike("name", (String) getContextData().get("name") +"%"));

    				//Returns the first object if found
    				Iterator iter = criteria.list().iterator();
    				return iter.hasNext() ? iter.next() : null;
    			}
    		};
    		return (AbstractLookup) this.getHibernateTemplate().execute(callback);
        }
        catch(HibernateException e)
        {
            throw new DAOException("Failed to get the lookup by description.  \n LookupType = " + lookupType.getName() + 
            		", description = " + name + ".\n exception = " + e.getMessage(), e);
        }
    }

	private String getSortByColumnName() {
		return sortByColumnName;
	}

	public void setSortByColumnName(String sortByColumnName) {
		this.sortByColumnName = sortByColumnName;
	}

	private boolean isReverseSort() {
		return reverseSort;
	}

	public void setReverseSort(boolean reverseSort) {
		this.reverseSort = reverseSort;
	}

	private boolean isFilterInactiveRows() {
		return filterInactiveRows;
	}

	public void setFilterInactiveRows(boolean filterInactiveRows) {
		this.filterInactiveRows = filterInactiveRows;
	}

	public List getValidCodes() {
		return validCodes;
	}

	public void setValidCodes(List validCodes) {
		this.validCodes = validCodes;
	}
}