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

// Java classes

import java.util.Calendar;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import gov.va.med.esr.common.model.report.CompletedReport;
import gov.va.med.esr.common.model.report.ReportSetup;
import gov.va.med.esr.common.model.report.ReportSetupLite;
import gov.va.med.esr.common.model.report.SimpleCompletedReport;
import gov.va.med.esr.common.persistent.report.ReportDAO;
import gov.va.med.esr.service.CompletedReportsSearchQueryInfo;
import gov.va.med.esr.service.ReportFilterSearchQueryInfo;
import gov.va.med.esr.service.ReportStatus;
import gov.va.med.fw.model.EntityKey;
import gov.va.med.fw.model.UserPrincipalImpl;
import gov.va.med.fw.persistent.DAOException;
import gov.va.med.fw.persistent.MaxRecordsExceededException;
import gov.va.med.fw.persistent.hibernate.AbstractDAOAction;
import gov.va.med.fw.persistent.hibernate.GenericDAOImpl;
import gov.va.med.fw.persistent.hibernate.PaginatedQueryExecutor;
import gov.va.med.fw.security.UserPrincipal;
import gov.va.med.fw.util.DateUtils;

import org.apache.commons.lang.Validate;
import org.hibernate.Criteria;
import org.hibernate.Query;
import org.hibernate.Session;
import org.hibernate.criterion.Expression;
import org.hibernate.criterion.Projections;
import org.hibernate.criterion.Property;
import org.hibernate.criterion.Restrictions;
import org.springframework.dao.DataAccessException;
import org.springframework.orm.hibernate3.HibernateCallback;
import org.springframework.orm.hibernate3.HibernateSystemException;

/**
 * Provides data access methods to perfomr CRUD operations on
 * generated reports 
 *
 * Project: Common</br>
 * Created on: 11:50:52 AM </br>
 *
 * @author DNS   LEV
 */
public class ReportDAOImpl extends GenericDAOImpl implements ReportDAO {

	/**
	 * An instance of serialVersionUID
	 */
	private static final long serialVersionUID = -4505636222269910037L;

	// These static final values match the names in hibernate mapping files.
	
	private static final String SQL_QUERY_GET_ALL_REPORT_SETUP_BY_USER = "SQL_reportSetupQuery_GetAllReportSetupByUser";
	private static final String QUERY_GET_ALL_COMPLETED_REPORT_BY_USER = "completedReportQuery_GetAllCompletedReportByUser";
    private static final String QUERY_GET_DISTINCT_STANDARD_REPORTS_FROM_COMPLETED_REPORTS="standardReportQuery_GetDistinctStandardReportsFromCompletedReports";
    private static final String QUERY_GET_DISTINCT_EXPORT_TYPES_FROM_COMPLETED_REPORTS= "reportExportTypeQuery_GetDistinctExportTypesFromCompletedReports";
    private static final String QUERY_GET_ACTIVE_INACTIVE_COMPLETED_REPORTS_COUNT="completedReportQuery_GetActiveInactivateCompletedReportsCount";
    private static final String USER_PARAM_NAME = "user";
    private static final String USER_NAME="userName";
    private static final String REPORT_SETUP_LITE="reportSetupLite";
 
    /**
	 * A default constructor
	 */
	public ReportDAOImpl() {
		super();
	}
	
	
	public List getReportSetups( UserPrincipal user ) throws DAOException {
		List reportList=null;
		try {			
			Map contextData = new HashMap();
			contextData.put("user", user);				        		        	
			HibernateCallback callback = new AbstractDAOAction(contextData) { 
				public Object execute(Session session) {
				    Query query = session.getNamedQuery(SQL_QUERY_GET_ALL_REPORT_SETUP_BY_USER);
				    session.createSQLQuery(query.getQueryString()).addEntity(REPORT_SETUP_LITE,ReportSetupLite.class);
				    query.setParameter(USER_NAME,((UserPrincipalImpl)getContextData().get("user")).getName());
				    return query.list();					
				}
			};			
			 reportList=this.getHibernateTemplate().executeFind(callback);			
		}catch(DataAccessException e) {
			throw new DAOException("Failed to get report setup by user",e);
		}
		return reportList;
	}
	
	public ReportSetup getReportSetup(EntityKey identifier) throws DAOException {
        Validate.notNull(identifier, "ReportSetup identifier can not be null.");
        Validate.notNull(identifier.getKeyValue(), "ReportSetup identifier key can not be null.");
        
        try {        
            ReportSetup reportSetup =  (ReportSetup) getHibernateTemplate().get( ReportSetup.class, identifier.getKeyValue());           
          return reportSetup;
        } catch (DataAccessException e) {
            throw new DAOException("Failed to get ReportSetup by id " + identifier.getKeyValueAsString(), e);
        }
   }

    public void saveReportSetup(ReportSetup reportSetup) throws DAOException {
        super.saveObject(reportSetup);
    }

	/*
	 * @see gov.va.med.esr.common.persistent.report.ReportDAO#deleteReportSetup(gov.va.med.fw.model.EntityKey)
	 */
    public void deleteReportSetup(EntityKey identifier) throws DAOException {
        Validate.notNull(identifier, "ReportSetup identifier can not be null.");
        Validate.notNull(identifier.getKeyValue(), "ReportSetup identifier key can not be null.");
    	super.removeObject(identifier);
	}
    
    public List getCompletedReports(UserPrincipal user) throws DAOException {
		try {
	        return this.getHibernateTemplate().findByNamedQueryAndNamedParam(QUERY_GET_ALL_COMPLETED_REPORT_BY_USER,USER_PARAM_NAME,user);
		} catch(DataAccessException e) {
			throw new DAOException("Failed to get completed reports by user",e);
		}
		
	}

	public CompletedReport getCompletedReport(EntityKey identifier) throws DAOException {
        Validate.notNull(identifier, "CompletedReport identifier can not be null.");
        Validate.notNull(identifier.getKeyValue(), "CompletedReport identifier key can not be null.");
        
        try {        
            CompletedReport completedReport =  (CompletedReport) getHibernateTemplate().get( CompletedReport.class, identifier.getKeyValue());
            return completedReport;
        } catch (DataAccessException e) {
            throw new DAOException("Failed to get ReportSetup by id " + identifier.getKeyValueAsString(), e);
        }
	}
    
    /*
     * @see gov.va.med.esr.common.persistent.report.ReportDAO#getSimpleCompletedReport(gov.va.med.fw.model.EntityKey)
     */
    public SimpleCompletedReport getSimpleCompletedReport(EntityKey identifier) throws DAOException {
        Validate.notNull(identifier, "SimpleCompletedReport identifier can not be null.");
        Validate.notNull(identifier.getKeyValue(), "SimpleCompletedReport identifier key can not be null.");
        
        try {        
            SimpleCompletedReport simpleCompletedReport =  (SimpleCompletedReport) getHibernateTemplate().get( SimpleCompletedReport.class, identifier.getKeyValue());
            return simpleCompletedReport;
        } catch (DataAccessException e) {
            throw new DAOException("Failed to get SimpleCompletedReport by id " + identifier.getKeyValueAsString(), e);
        }
    }
    
    public void saveCompletedReport(CompletedReport completedReport) throws DAOException {
        super.saveObject(completedReport);
    }


	/*
	 * @see gov.va.med.esr.common.persistent.report.ReportDAO#searchCompletedReports(gov.va.med.esr.service.CompletedReportsSearchQueryInfo)
	 */
    public List searchCompletedReports(CompletedReportsSearchQueryInfo searchCriteriaInfo) throws DAOException, MaxRecordsExceededException 
    {
 		Map contextData = new HashMap();
		contextData.put("cmpRptSearchCriteriaInfo", searchCriteriaInfo);				        		        	        	
		HibernateCallback callback = new AbstractDAOAction(contextData) { 
			public Object execute(Session session) throws DAOException {
				CompletedReportsSearchQueryInfo targetQueryInfo = (CompletedReportsSearchQueryInfo) getContextData().get("cmpRptSearchCriteriaInfo");
		        // Build the count and data retrieval criteria queries
		        Criteria countCriteria = getSearchSelectCountCriteria(session, targetQueryInfo);                
		        Criteria dataCriteria = getSearchSelectDataCriteria(session, targetQueryInfo);      
		        // Execute the query to retrieve the results
		        PaginatedQueryExecutor queryExecutor = new PaginatedQueryExecutor(countCriteria, dataCriteria, targetQueryInfo, false);
	            try {
	            	List results = queryExecutor.executeQuery();
	            	return results;
	            } catch(MaxRecordsExceededException e) {
	            	throw new DAOException("max records exceeded", e);
	            }	            
			}

		};
		try {
			return this.getHibernateTemplate().executeFind(callback);
		} catch(HibernateSystemException e) {
			MaxRecordsExceededException rootCause = (MaxRecordsExceededException) getRootExceptionOfType(e, MaxRecordsExceededException.class);
			if(rootCause != null)
				throw rootCause;
			throw e;
		}    		
	}


	/**
	 * Builds the count criteria search
	 * @param session
	 * @param targetQueryInfo
	 * @return
	 */
    private Criteria getSearchSelectCountCriteria(Session session, CompletedReportsSearchQueryInfo targetQueryInfo) {
		Criteria criteria = buildBaseCriteria(session, targetQueryInfo);
		criteria.setProjection(Projections.rowCount());
		
		return criteria;
	}
	
 
	/**
	 * Builds the data criteria search
	 * @param session
	 * @param targetQueryInfo
	 * @return
	 */
    private Criteria getSearchSelectDataCriteria(Session session, CompletedReportsSearchQueryInfo targetQueryInfo) {
		Criteria criteria = buildBaseCriteria(session, targetQueryInfo);		
		return criteria;
	}

    /**
     * Builds the generic Criteria
     * @param session
     * @param targetQueryInfo
     * @return
     */
    private Criteria buildBaseCriteria(Session session, CompletedReportsSearchQueryInfo targetQueryInfo) {
    	Criteria criteria = session.createCriteria(SimpleCompletedReport.class);
      	
    	if (targetQueryInfo.getReportType() != null) 
    	{
   			criteria.createCriteria("standardReport")
    			.createCriteria("type").add(
					Restrictions.eq("identifier", targetQueryInfo.getReportType().getIdentifier()));
    	}
    	
    	if (targetQueryInfo.getStandardReport() != null) 
    	{
    		Property stdRptId = Property.forName("standardReport.identifier");
   			criteria.add(stdRptId.eq(targetQueryInfo.getStandardReport().getIdentifier()));
     	}
    	
    	if (targetQueryInfo.getReportExportedType() != null)
    	{
  			criteria.createCriteria("fileType")
  			.add(Restrictions.eq("identifier", targetQueryInfo.getReportExportedType().getIdentifier()));
    	}
    	
       	if (targetQueryInfo.getDateFrom() != null)
    	{
  			criteria
  				.add(Expression.ge("completedDate", targetQueryInfo.getDateFrom()));
    	}
       	
      	if (targetQueryInfo.getDateTo() != null)
    	{
  			criteria
  				.add(Expression.le("completedDate", targetQueryInfo.getDateTo()));
    	}
      	
     	if (targetQueryInfo.getReportParameters() != null)
    	{
  			criteria
  				.add(Expression.ilike("reportParameterText", "%" + targetQueryInfo.getReportParameters() + "%"));
    	}
        if(targetQueryInfo.getReportStatus()!=null)
        {
            if(targetQueryInfo.getReportStatus().getCode().equals(ReportStatus.ACTIVE.getCode()))
            {
                criteria
                .add(Expression.isNull("inactivatedDate"));
            }
            else if(targetQueryInfo.getReportStatus().getCode().equals(ReportStatus.INACTIVE.getCode()))
            {
                criteria
                .add(Expression.isNotNull("inactivatedDate"));
            }
        }
        if (targetQueryInfo.getInactivatedDate() != null)
        {
            criteria
                .add(Expression.ge("inactivatedDate", targetQueryInfo.getInactivatedDate()));
            criteria
            .add(Expression.lt("inactivatedDate", DateUtils.getAddedDate(Calendar.DATE,1, DateUtils.createCalendar(targetQueryInfo.getInactivatedDate()))));
        }

 
		return criteria;
    }
    
	/*
	 * @see gov.va.med.esr.common.persistent.report.ReportDAO#searchScheduledReports(gov.va.med.esr.service.ScheduledReportsSearchQueryInfo)
	 */
    public List searchScheduledReports(ReportFilterSearchQueryInfo searchCriteriaInfo) throws DAOException, MaxRecordsExceededException 
    {
        
		Map contextData = new HashMap();
		contextData.put("schRptSearchCriteriaInfo", searchCriteriaInfo);				        		        	        	
		HibernateCallback callback = new AbstractDAOAction(contextData) { 
			public Object execute(Session session) throws DAOException {
				ReportFilterSearchQueryInfo targetQueryInfo = (ReportFilterSearchQueryInfo) getContextData().get("schRptSearchCriteriaInfo");
		        // Build the count and data retrieval criteria queries
		        Criteria countCriteria = getSearchSelectCountCriteria(session, targetQueryInfo);                
		        Criteria dataCriteria = getSearchSelectDataCriteria(session, targetQueryInfo);      
		        // Execute the query to retrieve the results
		        PaginatedQueryExecutor queryExecutor = new PaginatedQueryExecutor(countCriteria, dataCriteria, targetQueryInfo, false);
	            try {
	            	List results = queryExecutor.executeQuery();
	            	return results;
	            } catch(MaxRecordsExceededException e) {
	            	throw new DAOException("max records exceeded", e);
	            }	            
			}

		};
		try {
			return this.getHibernateTemplate().executeFind(callback);
		} catch(HibernateSystemException e) {
			MaxRecordsExceededException rootCause = (MaxRecordsExceededException) getRootExceptionOfType(e, MaxRecordsExceededException.class);
			if(rootCause != null)
				throw rootCause;
			throw e;
		}    		
	}


	/**
	 * Builds the count criteria search
	 * @param session
	 * @param targetQueryInfo
	 * @return
	 */
    private Criteria getSearchSelectCountCriteria(Session session, ReportFilterSearchQueryInfo targetQueryInfo) {
		Criteria criteria = buildBaseCriteria(session, targetQueryInfo);
		criteria.setProjection(Projections.rowCount());
		
		return criteria;
	}
	
 
	/**
	 * Builds the data criteria search
	 * @param session
	 * @param targetQueryInfo
	 * @return
	 */
    private Criteria getSearchSelectDataCriteria(Session session, ReportFilterSearchQueryInfo targetQueryInfo) {
		Criteria criteria = buildBaseCriteria(session, targetQueryInfo);		
		return criteria;
	}

    /**
     * Builds the generic Criteria
     * @param session
     * @param targetQueryInfo
     * @return
     */
    private Criteria buildBaseCriteria(Session session, ReportFilterSearchQueryInfo targetQueryInfo) {
    	Criteria criteria = session.createCriteria(ReportSetupLite.class);
      	
    	if (targetQueryInfo.getReportType() != null) 
    	{
   			criteria.createCriteria("report")
    			.createCriteria("type").add(
					Restrictions.eq("identifier", targetQueryInfo.getReportType().getIdentifier()));
    	}
    	
    	if (targetQueryInfo.getStandardReport() != null) 
    	{
       		Property stdRptId = Property.forName("report.identifier");
   			criteria.add(stdRptId.eq(targetQueryInfo.getStandardReport().getIdentifier()));
    	}
    	
    	if (targetQueryInfo.getReportExportedType() != null)
    	{
  			criteria.createCriteria("fileType")
  				.add(Restrictions.eq("identifier", targetQueryInfo.getReportExportedType().getIdentifier()));
    	}
 
		return criteria;
    }
    

    /*
     * @see gov.va.med.esr.common.persistent.report.ReportDAO#deleteCompletedReport(gov.va.med.fw.model.EntityKey)
     */
    public void deleteCompletedReport(EntityKey identifier) throws DAOException {
        Validate.notNull(identifier, "CompletedReport identifier can not be null.");
        Validate.notNull(identifier.getKeyValue(), "CompletedReport identifier key can not be null.");
        super.removeObject(identifier);
    } 
    
    /*
     * @see gov.va.med.esr.common.persistent.report.ReportDAO#getDistinctStandardReports()
     */
    public List getDistinctStandardReports() throws DAOException {
        try {
            return this.getHibernateTemplate().findByNamedQuery(QUERY_GET_DISTINCT_STANDARD_REPORTS_FROM_COMPLETED_REPORTS);
        } catch(DataAccessException e) {
            throw new DAOException("Failed to get distinct standard reports from complated reports",e);
        }        
    }
    
    /*
     * @see gov.va.med.esr.common.persistent.report.ReportDAO#getDistinctReportExportTypes()
     */
    public List getDistinctReportExportTypes() throws DAOException {
        try {
            return this.getHibernateTemplate().findByNamedQuery(QUERY_GET_DISTINCT_EXPORT_TYPES_FROM_COMPLETED_REPORTS);
        } catch(DataAccessException e) {
            throw new DAOException("Failed to get distinct report export types from complated reports",e);
        }        
    }
    
    /*
     * @see gov.va.med.esr.common.persistent.report.ReportDAO#getActiveInactiveReportsCount()
     */
    public Map getActiveInactiveReportsCount() throws DAOException {
        Map counts=new HashMap();
        try {
            List list= this.getHibernateTemplate().findByNamedQuery(QUERY_GET_ACTIVE_INACTIVE_COMPLETED_REPORTS_COUNT);
            Object [] obj=list!=null && list.size() >0 ? (Object [])list.get(0):null;  
            if(obj!=null && obj.length > 0)
                counts.put("activeReportsCount", obj[0]);
            if(obj!=null && obj.length > 1)
                counts.put("inactiveReportsCount", obj[1]);            
        } catch(DataAccessException e) {
            throw new DAOException("Failed to get active and inactive report counts from complated reports",e);
        } 
        return counts;
    }
}