/********************************************************************
 * Copyright © 2006 VHA. All rights reserved
 ********************************************************************/
// Package
package gov.va.med.esr.common.report.data.impl;

// Java classes
import java.util.ArrayList;
import java.util.Date;
import java.util.Iterator;
import java.util.List;
import java.util.Set;

// Library classes
import org.hibernate.Query;
import org.hibernate.Session;

// Framework classes
import gov.va.med.fw.report.ReportConfiguration;
import gov.va.med.fw.report.data.QueryCriteria;
import gov.va.med.fw.report.data.ReportDataException;
import gov.va.med.fw.util.StopWatchLogger;
import gov.va.med.fw.util.StringUtils;

// ESR classes
import gov.va.med.esr.common.model.lookup.ReportEligibilityFactorLU;
import gov.va.med.esr.common.model.lookup.ReportFacilityDisplayBy;
import gov.va.med.esr.common.model.lookup.VAFacility;
import gov.va.med.esr.common.model.report.ReportEligibilityFactor;
import gov.va.med.esr.common.model.report.ReportFacility;
import gov.va.med.esr.common.model.report.ReportSetup;
import gov.va.med.esr.common.model.report.ReportParameterSet;
import gov.va.med.esr.common.report.data.CommonCriteria;
import gov.va.med.esr.common.report.data.StandardReportCriteria;

/**
 * EED10 reports DAO.
 *
 * @author Muddaiah Ranga
 */
public class EED10ReportDataDAOImpl extends AbstractStandardReportDataDAOImpl {

	private static final long serialVersionUID = -5657439440961807171L;
    private static final int MAX_VALUES_IN_LIST = 1000;

	/**
	 * A default constructor
	 */
	public EED10ReportDataDAOImpl() {
		super();
	}

    /*
     * @see gov.va.med.fw.report.data.ReportDataDAO#preDataRetrieval(gov.va.med.fw.report.ReportConfiguration)
     */
    public void preDataRetrieval( ReportConfiguration config ) throws ReportDataException {
        QueryCriteria reportCriteria = config.getQueryCriteria();

        if( reportCriteria instanceof StandardReportCriteria ) {
           StandardReportCriteria criteria = (StandardReportCriteria)reportCriteria;
           ReportSetup setup = criteria.getReportSetup();
           ReportParameterSet parameters = setup != null ? setup.getParameterSet() : null;
           if( parameters == null ) {
                throw new ReportDataException("Missing report parameters in report " + config.getReportID() );
            }

           
           Date asOfdate = parameters.getAsOfDate();
           if (asOfdate != null) {
        	   criteria.addCriterion(CommonCriteria.AS_OF_DATE, asOfdate);
        	   criteria.setStartDate(null);
        	   criteria.setEndDate(null);
           }
           else {
        	   criteria.addCriterion(CommonCriteria.AS_OF_DATE, null);
        	   criteria.setStartDate( this.getDate( config, true ) );
        	   criteria.setEndDate( this.getDate( config, false ) );
           }
        }
    }

	/**
	 * @see gov.va.med.fw.report.data.hibernate.HibernateReportDataDAO#buildQuery(gov.va.med.fw.report.ReportConfiguration)
	 */
	protected Query buildQuery( ReportConfiguration config, Session session ) throws ReportDataException {

		QueryCriteria reportCriteria = config.getQueryCriteria();
		Query query = null;

		if( reportCriteria instanceof StandardReportCriteria ) {

			// Get a named query
			query = this.getNamedQuery( session );

			// Get a criteria
			StandardReportCriteria criteria = (StandardReportCriteria)reportCriteria;
			ReportSetup setup = criteria.getReportSetup();
			ReportParameterSet parameters = setup != null ? setup.getParameterSet() : null;
			if( parameters == null ) {
				throw new ReportDataException("Missing report parameters in report " + config.getReportID() );
			}

            //Date asOfDate = parameters.getAsOfDate();
			//query.setDate(CommonCriteria.AS_OF_DATE, parameters.getAsOfDate());
            Date startDate = parameters.getFromDate();
            Date endDate = parameters.getToDate();
            Date asOfdate = parameters.getAsOfDate();
            if (asOfdate != null) {
            	query.setString(CommonCriteria.AS_OF_DATE_FLAG, "Y");
            	query.setDate(CommonCriteria.START_DATE, null);
            	query.setDate(CommonCriteria.END_DATE,asOfdate);
            }
            else {
            	query.setString(CommonCriteria.AS_OF_DATE_FLAG, "N");
            	query.setDate(CommonCriteria.START_DATE, startDate);
            	query.setDate(CommonCriteria.END_DATE,endDate);
            }

          
		    ReportFacilityDisplayBy facilityDisplayBy = parameters.getFacilityDisplayBy();
		    if(facilityDisplayBy != null && StringUtils.isNotEmpty(facilityDisplayBy.getCode())) {
		        if(StringUtils.equals(facilityDisplayBy.getCode(),ReportFacilityDisplayBy.CODE_VISN.getCode())) {
		            List visns = this.getFacilitityIds(parameters.getFacilities());
		            if(visns != null && !visns.isEmpty()) {
		            	query.setString(CommonCriteria.FACILITY_TYPE, "VISN");
		                query.setParameterList(CommonCriteria.FIRST_FACILITY_LIST, visns.toArray() );
                        // secondFacilityList not used for VISN but has to be set
                        query.setParameterList(CommonCriteria.SECOND_FACILITY_LIST, visns.toArray() );
		                query.setString(CommonCriteria.IS_ALL,"notAll");
		            }
		            else {
		            	visns.add(new Integer(0));
		            	query.setString(CommonCriteria.FACILITY_TYPE, "VISN");
		                query.setParameterList(CommonCriteria.FIRST_FACILITY_LIST, visns.toArray() );
		                // secondFacilityList not used for VISN but has to be set
                        query.setParameterList(CommonCriteria.SECOND_FACILITY_LIST, visns.toArray() );
		                query.setString(CommonCriteria.IS_ALL,"all");
		            }
		        }
		        else {
		        
		            Set reportFacilities = parameters.getFacilities();
		            if(reportFacilities == null || reportFacilities.isEmpty()) {
		                List facilities = new ArrayList();
		                facilities.add(new Integer(0));
		                query.setString(CommonCriteria.FACILITY_TYPE, "FACILITY");
                        // Use the same list for both since this part of the query is ignored
		                query.setParameterList(CommonCriteria.FIRST_FACILITY_LIST, facilities);
                        query.setParameterList(CommonCriteria.SECOND_FACILITY_LIST, facilities);
		                query.setString(CommonCriteria.IS_ALL,"all");
		            } else {
		            	query.setString(CommonCriteria.FACILITY_TYPE, "FACILITY");
                       
                        List facilities = this.getFacilitityIds(reportFacilities);
                        
                        // A values list in the query cannot contain more than 1000 values - 
                        // break the list of facilities into 2 (around 1800 facility values are possible)
                        List firstFacilityList = new ArrayList();
                        List secondFacilityList = new ArrayList();
                        getFacilitySubLists(facilities, firstFacilityList, secondFacilityList);
                        query.setParameterList(CommonCriteria.FIRST_FACILITY_LIST, firstFacilityList);
                        query.setParameterList(CommonCriteria.SECOND_FACILITY_LIST, secondFacilityList);
                        
		                query.setString(CommonCriteria.IS_ALL,"notAll");
		            }
		        }
	
		    }
		    else {
		    	List visns = new ArrayList();
            	visns.add(new Integer(0));
            	query.setString(CommonCriteria.FACILITY_TYPE, "VISN");
                query.setParameterList(CommonCriteria.FIRST_FACILITY_LIST, visns.toArray() );
                query.setParameterList(CommonCriteria.SECOND_FACILITY_LIST, visns.toArray() );
                query.setString(CommonCriteria.IS_ALL,"all");
		    }
		    
			List eligibilityFactorIds = getEligFactorIds(parameters.getEligibilityFactors());
					//Set enrollment sdtatuses if provided
			if(!eligibilityFactorIds.isEmpty()) {
				query.setString(CommonCriteria.IS_ALL_ELIG, "notAll");
			}
			else {
				query.setString(CommonCriteria.IS_ALL_ELIG, "all");
                eligibilityFactorIds.add(new Integer(0));
			}
			query.setParameterList(CommonCriteria.ELIGIBILITY_FACTORS, eligibilityFactorIds);
		}
		return query;
	}

    private List getFacilitityIds(Set facilities) {
        Iterator i = facilities != null ? facilities.iterator() : null;
        ArrayList list = new ArrayList();
        while( i != null && i.hasNext() ) {
            ReportFacility rFacility = (ReportFacility)i.next();
            if(rFacility != null) list.add(((VAFacility)rFacility.getLookup()).getIdentifier());
        }
        return list;
    }
    
    private List getEligFactorIds(Set eligibilityFactors) {
        List statusIds = new ArrayList ();
        if(eligibilityFactors != null && !eligibilityFactors.isEmpty()) {
                //set the ids
            for (Iterator i=eligibilityFactors.iterator(); i.hasNext();){
                ReportEligibilityFactor factor = (ReportEligibilityFactor)i.next();
                ReportEligibilityFactorLU status = factor.getReportEligibilityFactorLU();
                statusIds.add(new Integer(status.getCode()));
            }
        }
        
        return statusIds;
    }
    
    /**
     * If the facilities list has more than the maximum allowable values, split the list into 
     * two lists (firstSubList, secondSubList).
     * 
     * @param facilities
     * @param firstSubList
     * @param secondSubList
     */
    private void getFacilitySubLists(List facilities, List firstSubList, List secondSubList) {
        
        if (facilities.size() > MAX_VALUES_IN_LIST) {
            firstSubList.addAll(facilities.subList(0, MAX_VALUES_IN_LIST));
            secondSubList.addAll(facilities.subList(MAX_VALUES_IN_LIST, facilities.size()-1));
        }
        else {
            firstSubList.addAll(facilities);
            secondSubList.add(new Integer(0));
        }
       
    }
    
    /**
     * Overridden to provide log information on the parameters selected and to log the
     * performance statistics.
     */
    public List getRecords(ReportConfiguration config, int firstRecord, int maxRecord) throws ReportDataException {
        List records = null;
        
        String executionInfo = "Executing EED10 Report query " + getQueryName() + " with criteria = " 
            + getReportCriteria(config) + " First Record: " + firstRecord + " Max Record: " + maxRecord;
        
        if(logger.isInfoEnabled())
            logger.info(executionInfo);
        
        StopWatchLogger watch = new StopWatchLogger(executionInfo);
        watch.start();
        records = super.getRecords(config, firstRecord, maxRecord);
        watch.stopAndLog(records.size() + " records returned. ");
        
        return records;
    }
    
    
    /**
     * Returns a string identifying the parameters passed to the report for logging purposes.
     * @param config
     * @return
     */
    private String getReportCriteria(ReportConfiguration config) {
        StringBuffer result = new StringBuffer();
        QueryCriteria reportCriteria = config.getQueryCriteria();

        if( reportCriteria instanceof StandardReportCriteria ) {
            StandardReportCriteria criteria = (StandardReportCriteria)reportCriteria;
            ReportSetup setup = criteria.getReportSetup();
            
            if (setup != null)
            {
                ReportParameterSet parameters = setup.getParameterSet();
                
                Date startDate = parameters.getFromDate();
                Date endDate = parameters.getToDate();
                Date asOfDate = parameters.getAsOfDate();
                
                result.append("Start Date: " + startDate);
                result.append(" End Date: " + endDate);
                result.append(" As Of Date: " + asOfDate);
                
                ReportFacilityDisplayBy facilityDisplayBy = parameters.getFacilityDisplayBy();
                if(facilityDisplayBy != null && StringUtils.isNotEmpty(facilityDisplayBy.getCode())) {
                    if(StringUtils.equals(facilityDisplayBy.getCode(),ReportFacilityDisplayBy.CODE_VISN.getCode())) {
                        
                        result.append(" Facility Type: VISN");
                        
                        List visns = this.getFacilitityIds(parameters.getFacilities());
                        if(visns != null && !visns.isEmpty()) {
                            result.append(" VISNs: ");
                            result.append(buildValuesList(visns));
                            result.append(" " + CommonCriteria.IS_ALL + " = notAll");
                        }
                        else {
                            result.append(" " + CommonCriteria.IS_ALL + " = all");
                        }
                    }
                    else {
                        
                        result.append(" Facility Type: FACILITY");
                        
                        Set reportFacilities = parameters.getFacilities();
                        if(reportFacilities == null || reportFacilities.isEmpty()) {
                            result.append(" " + CommonCriteria.IS_ALL + " = all");
                        } else {
                             
                            List facilities = this.getFacilitityIds(reportFacilities);
                            List firstFacilityList = new ArrayList();
                            List secondFacilityList = new ArrayList();
                            getFacilitySubLists(facilities, firstFacilityList, secondFacilityList);
                            
                            result.append(" First Facility List: ");
                            result.append(buildValuesList(firstFacilityList));
                            result.append(" Second Facility List: ");
                            result.append(buildValuesList(secondFacilityList));
                            
                            result.append(" " + CommonCriteria.IS_ALL + " = notAll");
                        }
                    }
        
                }
                else {
                    result.append(" Default Facility Type: VISN");
                    result.append(" " + CommonCriteria.IS_ALL + " = notAll");
                }
                
                List eligibilityFactorIds = getEligFactorIds(parameters.getEligibilityFactors());
                   
                if(!eligibilityFactorIds.isEmpty()) {
                    result.append(" " + CommonCriteria.IS_ALL_ELIG + " = notAll");
                }
                else {
                    result.append(" " + CommonCriteria.IS_ALL_ELIG + " = all");
                }
                
                result.append(" Eligibility Factors: " + eligibilityFactorIds);
            }
        }
        
        return result.toString();
    }
    
    private String buildValuesList(List list) {
        StringBuffer sb = new StringBuffer();
        
        sb.append("[");
        
        for (int i=0; i < list.size(); i++) {
            sb.append(list.get(i));
               
            if (i != list.size() - 1)
                sb.append(", ");
        }
        
        sb.append("]");
        
        return sb.toString();
    }
}