/********************************************************************
 * Copyright © 2004-2005 VHA. All rights reserved
 ********************************************************************/
package gov.va.med.esr.service.impl;

// Java classes
import gov.va.med.esr.common.model.ee.EGTProcessStatistic;
import gov.va.med.esr.common.model.ee.EGTSetting;
import gov.va.med.esr.common.model.ee.EnrollmentDetermination;
import gov.va.med.esr.common.model.lookup.EnrollmentCategory;
import gov.va.med.esr.common.model.lookup.EnrollmentStatus;
import gov.va.med.esr.common.model.lookup.VAFacility;
import gov.va.med.esr.common.model.messaging.SiteIdentity;
import gov.va.med.esr.common.model.person.Person;
import gov.va.med.esr.common.model.person.id.PersonEntityKey;
import gov.va.med.esr.common.model.person.id.PersonIdEntityKeyImpl;
import gov.va.med.esr.common.persistent.ee.EGTSettingDAO;
import gov.va.med.esr.common.persistent.history.HistoryDAO;
import gov.va.med.esr.service.EGTService;
import gov.va.med.fw.model.EntityKey;
import gov.va.med.fw.persistent.DAOException;
import gov.va.med.fw.scheduling.SchedulingService;
import gov.va.med.fw.service.ServiceException;

import java.math.BigDecimal;
import java.util.Date;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.apache.commons.lang.Validate;
import org.quartz.JobDetail;
import org.quartz.SchedulerException;

/**
 * Services related to EGT settings.
 * <p/>
 * Project: Common
 *
 * @author DNS   LEV
 * @author Andrew Pach
 * @version 3.0
 */
public class EGTServiceImpl extends AbstractRuleAwareServiceImpl implements EGTService
{
    /**
     * An instance of serialVersionUID
     */
    private static final long serialVersionUID = 8932467141395519860L;

    /**
     * An instance of the EGT setting DAO.
     */
    private EGTSettingDAO egtDAO = null;

    /**
     * HistoryDAO that retrieves the demographic history data.
     */
    private HistoryDAO egtHistoryDAO = null;

    private SchedulingService schedulingService = null;
    private JobDetail egtJobDetail = null;

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

    
    /**
	 * @see gov.va.med.esr.service.impl.AbstractRuleAwareServiceImpl#afterPropertiesSet()
	 */
	public void afterPropertiesSet() throws Exception {
		super.afterPropertiesSet();
        Validate.notNull(egtDAO, "EGTSettingDAO is required");
        Validate.notNull(egtHistoryDAO, "HistoryDAO is required");
        Validate.notNull(schedulingService, "SchedulingService is required");
        Validate.notNull(egtJobDetail, "EGT process JobDetail is required");
 	}


	/**
     * Gets the currently active EGT setting.
     *
     * @return The currently active EGT setting.
     * @throws ServiceException if any problems were encountered.
     */
    public EGTSetting getCurrentEGTSetting() throws ServiceException
    {
        // TODO: Ensure this method only returns EGT Settings that are not DISABLED!
        EGTSetting egt = null;
        try
        {
            egt = (EGTSetting)egtDAO.getActive();
        }
        catch (DAOException ex)
        {
            throw new ServiceException("Failed to getActiveEGTSetting by date", ex);
        }

        return egt;
    }

    /**
     * Gets a set of all future EGT settings.  These settings may be enabled or disabled.
     *
     * @return The set of future EGT settings.
     * @throws ServiceException if any problems were encountered.
     */
    public Set getFutureEGTSettings() throws ServiceException
    {
        try
        {
            return egtDAO.getFutureEGTSettings();
        }
        catch (DAOException ex)
        {
            throw new ServiceException("Failed to getActiveEGTSetting by date", ex);
        }
    }
    
    /**
     * Gets the EGTSetting that was active as of the given date.
     */
    public EGTSetting getActiveEGTSetting(Date asOf) throws ServiceException {
        EGTSetting egt = null;
        try
        {
            egt = (EGTSetting)egtDAO.getActive(asOf);
        }
        catch (DAOException ex)
        {
            throw new ServiceException("Failed to getActiveEGTSetting by date: " + asOf, ex);
        }

        return egt;
    }

    /**
     * Gets a specific EGT setting based on an entity key.
     *
     * @param key The key of the EGT setting to retrieve.
     *
     * @return The EGT setting based on the specified entity key.
     * @throws ServiceException if any problems were encountered.
     */
    public EGTSetting getEGTSetting(EntityKey key) throws ServiceException
    {
    	try {
			return (EGTSetting)getEgtDAO().getByKey(key);
		} catch (DAOException ex) {
			throw new ServiceException("Error loading EGTSetting using the key. ", ex);
		}
    }

    /**
     * Adds or updates an EGT setting.
     *
     * @param egt The EGT setting to add or update.
     *
     * @return The added or updated EGT setting.
     * @throws ServiceException if any problems were encountered.
     */
    public EGTSetting updateEGTSetting(EGTSetting egt) throws ServiceException {
        Validate.notNull(egt, "EGTSetting must not be null.");
        try {
            EGTSetting onFile = (egt.getEntityKey() != null) ? getEGTSetting(egt.getEntityKey()) : new EGTSetting();

            // Ensure that something changed on the incoming entity as compared to the onFile entity
            ensureEntityChanged(egt, onFile);

            this.getEgtRuleService().manageEGT(egt, onFile);
			getEgtDAO().saveObject(onFile);
            return onFile;
		} catch (DAOException ex) {
			throw new ServiceException("Error persisting EGTSetting Object.", ex);
		}
    }

    /**
     * Stops a currently executing EGT batch job.
     *
     * @return The current EGT setting that contains the statistical records associated with the previously scheduled or
     *         currently executing batch job.
     * @throws ServiceException if any problems were encountered.
     */
    public EGTSetting stopEGTProcessing() throws ServiceException
    {
    	try {
			schedulingService.interrupt(egtJobDetail);
		} catch (SchedulerException ex) {
			throw new ServiceException("Error in interrupting EGTSetting batch process.", ex);
		}
		return getCurrentEGTSetting();
    }

    /**
     * Gets a list of history change times for EGT setting records.
     *
     * @return The list of change times.
     * @throws ServiceException if any problems were encountered.
     */
    public Set getEGTSettingHistoryChangeTimes() throws ServiceException
    {
        try
        {
            return egtHistoryDAO.getHistoryChangeTimes(null);
        }
        catch (DAOException e)
        {
            throw new ServiceException(e.getMessage(), e);
        }
    }

    /**
     * Gets EGT setting historical information for a specific change event.  The change event should be obtained using
     * the getEGTSettingHistoryChangeTimes method call.
     *
     * @param changeEvent The change event used to determine the point of time to retrieve historical information for.
     *
     * @return The historical information for the specified change time.
     * @throws ServiceException if any problems were encountered.
     */
    public HistoricalInfo getEGTSettingHistoryByChangeTime(ChangeEvent changeEvent) throws ServiceException
    {
        try
        {
            return egtHistoryDAO.getHistoryByChangeTime(changeEvent);
        }
        catch (DAOException e)
        {
            throw new ServiceException(e.getMessage(), e);
        }
    }

    /**
     * @see gov.va.med.esr.service.EGTService#processEGTSetting(java.util.List, gov.va.med.esr.common.model.ee.EGTSetting)
     */
    public void updateEnrollmentWithNewEGTSetting(List veteranIds, EGTSetting newSetting, Map siteStats) throws ServiceException {
        
    	for (int v = 0; v < veteranIds.size(); v++) {
            PersonEntityKey key = new PersonIdEntityKeyImpl((BigDecimal)veteranIds.get(v));
            Person onFile = this.getPersonService().getPerson(key);
            Person calculated = this.getEnrollmentRuleService().determineEnrollment(onFile, newSetting);
            
            EnrollmentDetermination enrollment = calculated.getEnrollmentDetermination();
			EnrollmentStatus enrollmentStatus = enrollment != null ? enrollment.getEnrollmentStatus() : null;
			EnrollmentCategory category = enrollmentStatus != null ? enrollmentStatus.getEnrollmentCategory() : null;
			if (category != null && EnrollmentCategory.CODE_NOT_ENROLLED.getCode().equals(category.getCode()))
       		{                
    			Set sites = this.getPsDelegateService().getSites(calculated.getVPIDEntityKey());
    			for (Iterator iter = sites.iterator(); iter.hasNext();) {
    				SiteIdentity siteIdentity = (SiteIdentity) iter.next();
    				incrementSiteCount(siteStats, siteIdentity.getVaFacility());
    			}
       		}
    		// Handle post events
            this.getEventRuleService().handleCommonPostEvents(calculated, false);
            this.getPersonService().save(calculated);
        }
    }
    
	/**
	 * @param vaFacility
	 */
	private synchronized void incrementSiteCount(Map siteStats, VAFacility vaFacility) {
		Integer currentCount = (Integer)siteStats.get(vaFacility);
		currentCount = currentCount == null ? new Integer(1) : new Integer(currentCount.intValue()+1);
		siteStats.put(vaFacility, currentCount);
	}

	public void saveEGTProcessStatistics(EGTProcessStatistic processStatistic) throws ServiceException {
//        EGTSetting currentEgt = null;
//        try
//        {
//            currentEgt = (EGTSetting)getEgtDAO().getActive();
//        }
//        catch (DAOException ex)
//        {
//            throw new ServiceException("Failed to get current EGTSetting. ", ex);
//        }
    	try {
//    		getMergeRuleService().mergeEGTProcessStatistic(processStatistic, getOrCreateEgtProcessStatistic(currentEgt));    		
//    		getEgtDAO().saveObject(currentEgt);
    		getEgtDAO().saveObject(processStatistic);
		} catch (Exception ex) {
            throw new ServiceException("Failed to save the current EGTSetting. ", ex);
		}
    }
    
    /**
     * @see gov.va.med.esr.service.EGTService#saveAsCurrentEGTSetting(gov.va.med.esr.common.model.ee.EGTSetting)
     */
    public EGTSetting saveAsCurrentEGTSetting(EGTSetting egt) throws ServiceException {
        EGTSetting currentEgt = null;
        try
        {
            currentEgt = (EGTSetting)egtDAO.getActive();
        }
        catch (DAOException ex)
        {
            throw new ServiceException("Failed to get current EGTSetting. ", ex);
        }
        currentEgt.setCurrent(Boolean.FALSE);
        currentEgt.setDisable(Boolean.TRUE);

        egt.setPreviousEGT(currentEgt);
        egt.setDisable(Boolean.FALSE);
        egt.setCurrent(Boolean.TRUE);
        
        try {
			getEgtDAO().saveObject(currentEgt);
		} catch (DAOException ex) {
            throw new ServiceException("Failed to get save existing EGTSetting.", ex);
		}
        
        try {
			getEgtDAO().saveObject(egt);
		} catch (DAOException ex) {
            throw new ServiceException("Failed to get new EGTSetting.", ex);
		}
		return egt;
    }

    public EGTSettingDAO getEgtDAO()
    {
        return egtDAO;
    }

    public void setEgtDAO(EGTSettingDAO egtDAO)
    {
        this.egtDAO = egtDAO;
    }

    public HistoryDAO getEgtHistoryDAO()
    {
        return egtHistoryDAO;
    }

    public void setEgtHistoryDAO(HistoryDAO egtHistoryDAO)
    {
        this.egtHistoryDAO = egtHistoryDAO;
    }

	/**
	 * @return Returns the egtJobDetail.
	 */
	public JobDetail getEgtJobDetail() {
		return egtJobDetail;
	}
	
	/**
	 * @param egtJobDetail The egtJobDetail to set.
	 */
	public void setEgtJobDetail(JobDetail egtJobDetail) {
		this.egtJobDetail = egtJobDetail;
	}
	
	/**
	 * @return Returns the schedulingService.
	 */
	public SchedulingService getSchedulingService() {
		return schedulingService;
	}

	/**
	 * @param schedulingService The schedulingService to set.
	 */
	public void setSchedulingService(SchedulingService schedulingService) {
		this.schedulingService = schedulingService;
	}
}
