/*******************************************************************************
 * Copyright © 2004 EDS. All rights reserved
 ******************************************************************************/
// Package
package gov.va.med.esr.messaging.service.retransmission;

// Framework classes
import java.math.BigDecimal;
import java.util.Collection;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;

import org.apache.commons.lang.Validate;

import gov.va.med.fw.hl7.InvalidMessageException;
import gov.va.med.fw.hl7.Message;
import gov.va.med.fw.hl7.MessageParser;
import gov.va.med.fw.hl7.constants.SegmentConstants;
import gov.va.med.fw.hl7.segment.QRD;
import gov.va.med.fw.hl7.segment.RF1;
import gov.va.med.fw.hl7.segment.ZGD;
import gov.va.med.fw.hl7.segment.ZIC;
import gov.va.med.fw.hl7.segment.ZIV;
import gov.va.med.fw.hl7.segment.ZPD;
import gov.va.med.fw.model.EntityKey;
import gov.va.med.fw.security.SecurityContextHelper;
import gov.va.med.fw.service.AbstractComponent;
import gov.va.med.fw.service.ServiceException;
import gov.va.med.fw.util.StringUtils;

import gov.va.med.esr.common.model.CommonEntityKeyFactory;
import gov.va.med.esr.common.model.ee.EGTSetting;
import gov.va.med.esr.common.model.ee.IncomingMessageInfo;
import gov.va.med.esr.common.model.insurance.InsurancePolicy;
import gov.va.med.esr.common.model.lookup.AddressType;
import gov.va.med.esr.common.model.lookup.AssociationType;
import gov.va.med.esr.common.model.lookup.MessageStatus;
import gov.va.med.esr.common.model.lookup.MessageType;
import gov.va.med.esr.common.model.lookup.VAFacility;
import gov.va.med.esr.common.model.messaging.MessageLogEntry;
import gov.va.med.esr.common.model.messaging.SiteIdentity;
import gov.va.med.esr.common.model.party.Address;
import gov.va.med.esr.common.model.person.Association;
import gov.va.med.esr.common.model.person.Person;
import gov.va.med.esr.common.model.system.SystemParameter;
import gov.va.med.esr.messaging.constants.HL7Constants;
import gov.va.med.esr.messaging.service.outbound.OutboundMessageMasterFileService;
import gov.va.med.esr.messaging.service.outbound.OutboundMessageService;
import gov.va.med.esr.service.EGTService;
import gov.va.med.esr.service.LookupService;
import gov.va.med.esr.service.MessagingService;
import gov.va.med.esr.service.PersonService;
import gov.va.med.esr.service.SystemParameterService;
import gov.va.med.esr.service.trigger.IncomeYearTriggerEvent;
import gov.va.med.esr.service.trigger.PartialPersonTriggerEvent;
import gov.va.med.esr.service.trigger.PersonTrigger;
import gov.va.med.esr.service.trigger.PersonTriggerEvent;
import gov.va.med.esr.service.trigger.RetransmitTriggerEvent;
import gov.va.med.esr.service.trigger.TargetedPersonTriggerEvent;

/**
 * Class that resends messages by deligating to the appropriate outbound
 * services.
 * 
 * @author Alex Yoon
 * @version 1.0
 */
public class RetransmissionMessagePublisher extends AbstractComponent implements
		MessageResender {
    
	private Map servicesMap;

	private MessagingService messagingService;

	private LookupService lookupService;

	private PersonService personService;
	
	private EGTService egtService;
	
	private SystemParameterService systemParameterService;

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

	public MessagingService getMessagingService() {
		return this.messagingService;
	}

	public void setMessagingService(MessagingService messagingService) {
		this.messagingService = messagingService;
	}

	public LookupService getLookupService() {
		return this.lookupService;
	}

	public void setLookupService(LookupService lookupService) {
		this.lookupService = lookupService;
	}
	
	/**
	 * @return Returns the servicesMap.
	 */
	public Map getServicesMap() {
		return servicesMap;
	}

	/**
	 * @param servicesMap The servicesMap to set.
	 */
	public void setServicesMap(Map servicesMap) {
		this.servicesMap = servicesMap;
	}
	
	/**
	 * @return Returns the personService.
	 */
	public PersonService getPersonService() {
		return personService;
	}

    /**
     * @return Returns the egtService.
     */
    public EGTService getEgtService()
    {
        return egtService;
    }
    /**
     * @param egtService The egtService to set.
     */
    public void setEgtService(EGTService egtService)
    {
        this.egtService = egtService;
    }
	/**
	 * @param personService The personService to set.
	 */
	public void setPersonService(PersonService personService) {
		this.personService = personService;
	}

    /**
     * @return Returns the systemParameterService.
     */
    public SystemParameterService getSystemParameterService()
    {
        return systemParameterService;
    }
    
    /**
     * @param systemParameterService The systemParameterService to set.
     */
    public void setSystemParameterService(
            SystemParameterService systemParameterService)
    {
        this.systemParameterService = systemParameterService;
    }
    

	/* (non-Javadoc)
	 * @see gov.va.med.esr.messaging.service.retransmission.MessageResender#resend(gov.va.med.esr.service.trigger.RetransmitTriggerEvent)
	 */
	public void resend(RetransmitTriggerEvent triggerEvent)
			throws ServiceException {
		MessageLogEntry messageLog = null;
		String messageType = null;

		try {
			messageLog = messagingService.getMessageLogEntryById(triggerEvent
					.getMessageLogEntryId());
			if (messageLog != null) {
				messageType = messageLog.getType().getCode();
	
				SecurityContextHelper.getSecurityContext().setLogicalName(
						"VAMC-" + messageLog.getVaFacility().getCode()
								+ " - RETRANSMIT");
				
				invokeService(messageType, messageLog, true);
			}

		} catch (Exception e) {
			// Eat the exception and exit. Do not throw an exception which will
			// put the message back to the queue
			logger.info("Could not retransmit message for message type "
							+ messageType + "and person id "
							+ ((messageLog != null) ? messageLog.getPersonId() : null) + " and Message Id "
							+ ((messageLog != null) ? messageLog.getControlIdentifier() : null), e);
		}
	} 

	/**
	 * Method to deligate message resending to the appropriate services.
	 * 
	 * @param messageLog
	 *            The MessageLogEntry object to use for resending a message.
	 */
	public void resend(MessageLogEntry messageLog) throws ServiceException {
		if (logger.isDebugEnabled()) {
			logger.debug("resend() was called. ");
		}
		String messageType = null;
		try {
			// If status is not "Awaiting Acknowledgement", do not process.
			// Other process could have process this message.
			if (!MessageStatus.AWAITING_ACKNOWLEDGEMENT.getName().equals(
					messageLog.getStatus().getCode())) {
				return;
			}

			messageType = messageLog.getType().getCode();

			SecurityContextHelper.getSecurityContext()
					.setLogicalName( "VAMC-" + messageLog.getVaFacility().getCode() + " - RETRANSMIT");
			
			
			boolean hasReachedRetransmitLimit = hasReachedRetransmitLimit(messageLog);
			
			if(!hasReachedRetransmitLimit)
			{
			    invokeService(messageType, messageLog, false);
			}
			    
			// Set status to Retransmit (if didn't error out during processing) 
			if(!MessageStatus.ERROR.getCode().equals(messageLog.getStatus().getCode())) 
			{
				MessageStatus status = hasReachedRetransmitLimit ? 
				    this.lookupService.getMessageStatusByCode( MessageStatus.RETRANSMISSION_FAILED ):
					this.lookupService.getMessageStatusByCode( MessageStatus.RETRANSMIT );
				messageLog.setStatus(status);
				messageLog.setErrorText(null);
				messageLog.setInternalErrorText(null);
				this.messagingService.logMessage(messageLog);
			}
		}
		// Added logics to properly catch and throw exception - DNS   lev
		catch( Exception e ) {
		    //Eat the exception and exit. Do not throw an exception which will put teh message back to the queue
		    logger.info("Could not retransmit message for message type "
                    + messageType + "and person id " + messageLog.getPersonId() + " and Message Id "
                    + messageLog.getControlIdentifier(), e); 
		}
	}
	
	public void resend(String controlId) throws ServiceException {}	
	
	/**
     * @param messageLog
     * @return
	 * @throws ServiceException
     */
    private boolean hasReachedRetransmitLimit(MessageLogEntry messageLog) throws ServiceException
    {
        int maxRetransmissionCount = 0;
        if(MessageType.CODE_MFNZEG_TO_SITE.getCode().equals(messageLog.getType().getCode()))
        {
            maxRetransmissionCount = new Integer(systemParameterService.getByName(
                    SystemParameter.MAXIMUM_RETRANSMITION_COUNT_EGT)
                    .getValue()).intValue();
            
        }else
        {
            maxRetransmissionCount = new Integer(systemParameterService.getByName(
                    SystemParameter.MAXIMUM_RETRANSMITION_COUNT)
                    .getValue()).intValue();
        }
        
        return (messageLog.getRetransmissionCount() == maxRetransmissionCount);
        
    }

    private void updateErrorMessageLogEntry(MessageLogEntry mle, String error) throws ServiceException {
		if(logger.isErrorEnabled())
			logger.error("Error during Retransmit for Message Control Number: " + mle.getControlIdentifier() +
					" Setting to MessageStatus.ERROR for reason: " + error);
		
		mle.setStatus(lookupService.getMessageStatusByCode(MessageStatus.ERROR));
		mle.setInternalErrorText("Unable to retransmit message due to error: " + error);
		messagingService.logMessage(mle);
	}

	/**
	 * Determine which service to invoke for the particular retransmisison
	 * message
	 * 
	 * @param msgType A message type
	 * @param mle A message log entry 
	 * @throws ServiceException In case of unknown message type
	 * @history: Added logics to guard for missing outbound service and NULL ID - DNS   lev
	 */
	private void invokeService( String msgType, MessageLogEntry mle, boolean isRetransmitFromUI ) throws ServiceException {
		
		// Get an outbound service for the specific message type
		Object service = servicesMap.get(msgType);
		
		if(service == null)
		{
			if( this.logger.isInfoEnabled() ) {
				logger.info( "An outbound service was not configured for message type " + msgType );
			}
		}
		
		try {
			if (service instanceof OutboundMessageService)
			{
			    OutboundMessageService outboundService = (OutboundMessageService)service ;
				 
				// Get a person id
				BigDecimal id = mle != null ? mle.getPersonId() : null;	

				if( id != null ) {
					// Get SiteIdentity object.
					Person person = this.personService.getPerson( 
							CommonEntityKeyFactory.createPersonIdEntityKey( id.toString() ) );
					
					// Get a side identity to send out messages
					SiteIdentity siteIdentity = 
						this.messagingService.getIdentity( person, mle.getVaFacility() );
					
					String targetSiteCode = mle.getVaFacility() != null ? mle.getVaFacility().getCode() : null;			
					if(siteIdentity == null && !VAFacility.CODE_MVR.getCode().equals(targetSiteCode)) {
						/* unable to retransmit this message to the same site since we need this
						 * to get the DFN for the site
						 */
						updateErrorMessageLogEntry(mle, "The PSIM api determined that the original site " + targetSiteCode + 
								" is no longer valid for this VPID/Person");
						return;
					}

					// For all unsolicitated messages, get the site to be sent
					if( msgType.startsWith( HL7Constants.UNSOLICITATED_MESSAGE ) ) {
						resendUnsolicitatedMessage( person, 
															 msgType, 
															 mle,
															 outboundService, 
															 siteIdentity, isRetransmitFromUI );
					}
					else if( msgType.startsWith( HL7Constants.SOLICITATED_MESSAGE ) ) {
						resendSolicitatedMessage( person, 
														  msgType, 
														  mle,
														  outboundService, 
														  siteIdentity, isRetransmitFromUI );
					}
					else if( msgType.startsWith( HL7Constants.QRY_MESSAGE ) ) {
						resendQueryMessage( person, 
												  msgType, 
												  mle,
												  outboundService, 
												  siteIdentity , isRetransmitFromUI);
					}
					else { 
						updateErrorMessageLogEntry(mle, "Failed to resend a message due to an unknown message type: " + msgType);
					}
				}
				
			}else if (service instanceof OutboundMessageMasterFileService)
			{
				
			    OutboundMessageMasterFileService masterFileService = (OutboundMessageMasterFileService)service;
			    String egtSettingId = mle.getRetransmissionInfo();
			    
			    EntityKey egtKey = CommonEntityKeyFactory.createEGTSettingEntityKey(egtSettingId);
			    EGTSetting egtSetting = getEgtService().getEGTSetting(egtKey);
			    
			    masterFileService.processMessage(egtSetting, mle.getVaFacility(), mle);
			    
			}
		} catch (Exception e) {
			//If unable to resend a message due to any unforseen error, log it as an error and do not retransmit again 
			updateErrorMessageLogEntry(mle, "Failed to resend a message: " + e.getMessage());
		}
	}

	/**
	 * @param messageType
	 * @param messageLogEntry
	 * @param outboundService
	 * @param siteIdentity
	 * @throws ServiceException
	 */
	private void resendQueryMessage(Person person, String messageType,
			MessageLogEntry messageLogEntry,
			OutboundMessageService outboundService, SiteIdentity siteIdentity, boolean isRetransmitFromUI)
			throws ServiceException {
		if (MessageType.CODE_QRYZ07_TO_SITE.getName().equals(messageType)) {
			IncomeYearTriggerEvent triggerEvent = getQRYZ07TriggerEventFromMessage(messageLogEntry);
			outboundService.processMessage(person, siteIdentity, triggerEvent,
					messageLogEntry,isRetransmitFromUI);
		}
		else {
			outboundService.processMessage(person, null, null, messageLogEntry, isRetransmitFromUI);
		}

		if (logger.isDebugEnabled()) {
			logger.debug("Retransmitted " + messageType);
		}
	}

	/**
	 * @param person
	 * @param messageType
	 * @param messageLogEntry
	 * @param outboundService
	 * @param siteIdentity
	 * @throws ServiceException
	 */
	private void resendSolicitatedMessage(Person person, String messageType,
			MessageLogEntry messageLogEntry,
			OutboundMessageService outboundService, SiteIdentity siteIdentity, boolean isRetransmitFromUI)
			throws ServiceException {
		if (MessageType.CODE_ORFZ10_TO_SITE.getName().equals(messageType)) {
			PersonTriggerEvent triggerEvent = getORFZ10TriggerEventFromMessage(messageLogEntry);

			outboundService.processMessage(person, siteIdentity, triggerEvent,
					messageLogEntry,isRetransmitFromUI);

		}
		else if (MessageType.CODE_ORFZ11_TO_SITE.getName().equals(messageType)) {
			PersonTriggerEvent triggerEvent = getORFZ11TriggerEventFromMessage(messageLogEntry);

			outboundService.processMessage(person, siteIdentity, triggerEvent,
					messageLogEntry,isRetransmitFromUI);

		}
		else {
			outboundService.processMessage(person, siteIdentity, null, messageLogEntry,isRetransmitFromUI);
		}
	}

	/**
	 * @param person
	 * @param messageType
	 * @param messageLogEntry
	 * @param outboundService
	 * @param siteIdentity
	 * @throws ServiceException
	 */
	private void resendUnsolicitatedMessage(Person person, String messageType,
			MessageLogEntry messageLogEntry,
			OutboundMessageService outboundService, SiteIdentity siteIdentity, boolean isRetransmitFromUI)
			throws ServiceException {
		if (MessageType.CODE_ORUZ05_TO_SITE.getName().equals(messageType)) {
			// ORUZ05 builders need to know what data change triggered the
			// Z05e.g address data change or Date of death change.
			PersonTriggerEvent triggerEvent = getZ05TriggerEventFromMessage(person, messageLogEntry);
            
            if(triggerEvent != null) {
                outboundService.processMessage(person, siteIdentity, triggerEvent,messageLogEntry,isRetransmitFromUI);
            }
		}
		else if (MessageType.CODE_ORUZ10_TO_SITE.getName().equals(messageType)) {
			PersonTriggerEvent triggerEvent = getORUZ10IncomeYearTriggerEventMessage(messageLogEntry);
			if(triggerEvent != null)
			{
				outboundService.processMessage(person, siteIdentity, triggerEvent,
						messageLogEntry, isRetransmitFromUI);
			}

		} else if (MessageType.CODE_ORUZ04_TO_SITE.getName().equals(messageType)) {
			PersonTriggerEvent triggerEvent = getORUZ04TriggerEventFromMessage(person, messageLogEntry);
			if(triggerEvent != null)
			{
				outboundService.processMessage(person, siteIdentity, triggerEvent,
						messageLogEntry, isRetransmitFromUI);
			}

		} else if (MessageType.CODE_ORUZ06_TO_SITE.getName().equals(messageType)) {
			PersonTriggerEvent triggerEvent = getORUZ06IncomeYearTriggerEventMessage(messageLogEntry);
			if(triggerEvent != null)
			{
				outboundService.processMessage(person, siteIdentity, triggerEvent,
						messageLogEntry, isRetransmitFromUI);
			}

		}			
		else {
			outboundService.processMessage(person, siteIdentity, null, messageLogEntry, isRetransmitFromUI);
		}
		if (logger.isDebugEnabled()) {
			logger.debug("Retransmitted " + messageType);
		}
	}

	/**
	 * @param messageLogEntry
	 * @return
	 * @throws ServiceException
	 */
	private IncomeYearTriggerEvent getQRYZ07TriggerEventFromMessage(
			MessageLogEntry messageLogEntry) throws ServiceException {
		IncomeYearTriggerEvent incomeYearTriggerEvent = null;

		String messageBody = messageLogEntry.getBody();
		Message message;
		try {
			// get the message from the MessageLogEntry and parse it to create
			// the segments
			message = new Message(messageBody, messageLogEntry.getType().getCode());
			MessageParser.parseSegments(message.getDelimiterCharacter(), message
					.getEncodingCharacters(), message.getMessageData());

			VAFacility sendingFacility = getLookupService()
					.getVaFacilityByStationNumber(message.getSendingFacility());

			// Get the income year
			QRD qrd = (QRD) message.getSegment("QRD");
			String departmentDataCode = qrd.getWhatDepartmentDataCode();
			Integer incomeYear = Integer.valueOf(departmentDataCode == null ? null
					: departmentDataCode.substring(0, 4));

			incomeYearTriggerEvent = new IncomeYearTriggerEvent(
					PersonTrigger.DestinationType.MESSAGING,
					PersonTrigger.TargetType.VISTA,
					PersonTrigger.DispatchType.QUERY,
					PersonTrigger.DataType.FULL_DATA);

			incomeYearTriggerEvent.setIncomeYear(incomeYear);
			Set sites = new HashSet();
			sites.add(sendingFacility);
			incomeYearTriggerEvent.setTargetSites(sites);

		}
		catch (InvalidMessageException e) {
			throw new ServiceException(
					"Could not parse message body into message segments ", e);
		}
		return incomeYearTriggerEvent;
	}

	/**
	 * @param messageLogEntry
	 * @return
	 */
	private IncomeYearTriggerEvent getORUZ10IncomeYearTriggerEventMessage(
			MessageLogEntry messageLogEntry) throws ServiceException {
		IncomeYearTriggerEvent incomeYearTriggerEvent = null;

		String messageBody = messageLogEntry.getBody();
		Message message;
		try {
			// get the message from the MessageLogEntry and parse it to create
			// the segments
			message = new Message(messageBody, messageLogEntry.getType().getCode());
			MessageParser.parseSegments(message.getDelimiterCharacter(), message
					.getEncodingCharacters(), message.getMessageData());

			VAFacility sendingFacility = getLookupService()
					.getVaFacilityByStationNumber(message.getSendingFacility());

			// Get the income year from ZIC.
			ZIC zic = (ZIC) message.getSegment(SegmentConstants.ZIC);
			String incomeYearFromMsg = zic.getIncomeYear();
			//If there is a Z10 without financial data(no income year), then do not retransmit -ESR_CodeCR4009	
			if (StringUtils.isNotEmpty(incomeYearFromMsg)) {
			    //The income year is in the message is in the format '20040000'. Only get the year part. 
				Integer incomeYear = Integer.valueOf(incomeYearFromMsg.substring(0,4));
	
				incomeYearTriggerEvent = new IncomeYearTriggerEvent(
						PersonTrigger.DestinationType.MESSAGING,
						PersonTrigger.TargetType.VISTA,
						PersonTrigger.DispatchType.RESPOND,
						PersonTrigger.DataType.FINANCIAL);
	
				incomeYearTriggerEvent.setIncomeYear(incomeYear);
				Set sites = new HashSet();
				sites.add(sendingFacility);
				incomeYearTriggerEvent.setTargetSites(sites);
			}

		}
		catch (InvalidMessageException e) {
			throw new ServiceException(
					"Could not parse message body into message segments ", e);
		}
		return incomeYearTriggerEvent;

	}

	/**
	 * @param messageLogEntry
	 * @return
	 */
	private IncomeYearTriggerEvent getORUZ06IncomeYearTriggerEventMessage(
			MessageLogEntry messageLogEntry) throws ServiceException {
		IncomeYearTriggerEvent incomeYearTriggerEvent = null;
		//Income year info is stored in RetransmissionInfo
		String incomeYearFromMsg = messageLogEntry.getRetransmissionInfo();
		
		if (StringUtils.isNotEmpty(incomeYearFromMsg)) {		   
			Integer incomeYear = Integer.valueOf(incomeYearFromMsg);
			incomeYearTriggerEvent = new IncomeYearTriggerEvent(
					PersonTrigger.DestinationType.MESSAGING,
					PersonTrigger.TargetType.VISTA,
					PersonTrigger.DispatchType.RESPOND,
					PersonTrigger.DataType.FINANCIAL);

			incomeYearTriggerEvent.setIncomeYear(incomeYear);
		}	
		return incomeYearTriggerEvent;

	}

	/**
	 * @param messageLogEntry
	 * @return
	 */
	private IncomeYearTriggerEvent getORFZ10TriggerEventFromMessage(
			MessageLogEntry messageLogEntry) throws ServiceException {
		IncomeYearTriggerEvent incomeYearTriggerEvent = null;

		String messageBody = messageLogEntry.getBody();
		Message message;
		try {
			// get the message from the MessageLogEntry and parse it to create
			// the segments
			message = new Message(messageBody, messageLogEntry.getType().getCode());
			MessageParser.parseSegments(message.getDelimiterCharacter(), message
					.getEncodingCharacters(), message.getMessageData());

			VAFacility sendingFacility = getLookupService()
					.getVaFacilityByStationNumber(message.getSendingFacility());

			// Get the income year
			QRD qrd = (QRD) message.getSegment("QRD");
			String departmentDataCode = qrd.getWhatDepartmentDataCode();
			Integer incomeYear = Integer.valueOf(departmentDataCode == null ? null
					: departmentDataCode.substring(0, 4));

			// Create a IncomingMessageInfo needed by the builders
			IncomingMessageInfo messageInfo = new IncomingMessageInfo();
			// messageInfo.setQueryDateTime(super.buildDate(qrd.getQueryDate()));
			messageInfo.setReferenceMessageId(message.getMessageID());
			messageInfo.setSendingFacility(sendingFacility);

			incomeYearTriggerEvent = new IncomeYearTriggerEvent(
					PersonTrigger.DestinationType.MESSAGING,
					PersonTrigger.TargetType.VISTA,
					PersonTrigger.DispatchType.RESPOND,
					PersonTrigger.DataType.FINANCIAL);

			incomeYearTriggerEvent.setIncomeYear(incomeYear);
			incomeYearTriggerEvent.setIncomingMessageInfo(messageInfo);
			Set sites = new HashSet();
			sites.add(sendingFacility);
			incomeYearTriggerEvent.setTargetSites(sites);

		}
		catch (InvalidMessageException e) {
			throw new ServiceException(
					"Could not parse message body into message segments ", e);
		}
		return incomeYearTriggerEvent;

	}

	/**
	 * @param messageLogEntry
	 * @return
	 */
	private PersonTriggerEvent getORFZ11TriggerEventFromMessage(
			MessageLogEntry messageLogEntry) throws ServiceException {
		TargetedPersonTriggerEvent targetedPersonTriggerEvent = null;

		String messageBody = messageLogEntry.getBody();
		Message message;
		try {
			// get the message from the MessageLogEntry and parse it to create
			// the segments
			message = new Message(messageBody, messageLogEntry.getType().getCode());
			MessageParser.parseSegments(message.getDelimiterCharacter(), message
					.getEncodingCharacters(), message.getMessageData());

			VAFacility sendingFacility = getLookupService()
					.getVaFacilityByStationNumber(message.getSendingFacility());

			// Create a IncomingMessageInfo needed by the builders
			IncomingMessageInfo messageInfo = new IncomingMessageInfo();
			// messageInfo.setQueryDateTime(super.buildDate(qrd.getQueryDate()));
			messageInfo.setReferenceMessageId(message.getMessageID());
			messageInfo.setSendingFacility(sendingFacility);

			targetedPersonTriggerEvent = new TargetedPersonTriggerEvent(
					PersonTrigger.DestinationType.MESSAGING,
					PersonTrigger.TargetType.VISTA,
					PersonTrigger.DispatchType.RESPOND,
					PersonTrigger.DataType.ELIGIBILITY);

			Set sites = new HashSet();
			sites.add(sendingFacility);

			targetedPersonTriggerEvent.setIncomingMessageInfo(messageInfo);
			sites.add(sendingFacility);
			targetedPersonTriggerEvent.setTargetSites(sites);

		}
		catch (InvalidMessageException e) {
			throw new ServiceException(
					"Could not parse message body into message segments ", e);
		}
		return targetedPersonTriggerEvent;

	}

	/**
	 * @see org.springframework.beans.factory.InitializingBean#afterPropertiesSet()
	 */
	public void afterPropertiesSet() throws Exception {
		super.afterPropertiesSet();
		
		Validate.notNull(egtService,"egtService cannot be null");
		Validate.notNull(messagingService,"MessagingService cannot be null");
		Validate.notNull(lookupService,"lookupService cannot be null");
		Validate.notNull(personService,"personService cannot be null");
		Validate.notNull(servicesMap,"servicesMap cannot be null");
	}


	/**
	 * Create a PartialPersonTriggerEvent based on the data type that triggered
	 * the Z05.
	 * 
	 * @param messageLogEntry
	 * @return
	 * @throws ServiceException
	 */
	private PersonTriggerEvent getZ05TriggerEventFromMessage(Person person,
			MessageLogEntry messageLogEntry) throws ServiceException {
		PersonTriggerEvent triggerEvent = null;

		String messageBody = messageLogEntry.getBody();
		Message message;
		try {
			message = new Message(messageBody, messageLogEntry.getType().getCode());
			MessageParser.parseSegments(message.getDelimiterCharacter(), message
					.getEncodingCharacters(), message.getMessageData());

			// Now determine what type of data change caused triggered the Z05

			// If RF1 - 6 OR 7 are populated, it was due to a address
			// change(Correspondence Address)
			RF1 rf1 = (RF1) message.getSegment(SegmentConstants.RF1);
			if (rf1.getOriginalReferralID() != null) {
				Address address = Address.getAddressOfType(person.getAddresses(),
						AddressType.CODE_PERMANENT_ADDRESS.getName());
				if(address != null) {
					Set entityKeys = new HashSet();
					entityKeys.add(address.getEntityKey());
	
					triggerEvent = new PartialPersonTriggerEvent(
							PersonTrigger.DestinationType.MESSAGING,
							PersonTrigger.TargetType.VISTA,
							PersonTrigger.DispatchType.NOTIFY,
							PersonTrigger.DataType.ADDRESS);
					triggerEvent.setPersonId(person.getPersonEntityKey());
					// Cast it to a PartialPersonTriggerEvent
					((PartialPersonTriggerEvent) triggerEvent)
							.setEntityKeys(entityKeys);
				} else {
					updateErrorMessageLogEntry(messageLogEntry,
						"Retransmission determined this was a Z05 for an Address change, but the Person " +
								person.getPersonEntityKey().getKeyValueAsString() + " has no permanent Address on file, ignoring");
					return null;
				}
			}

			// If ZPD - 9,31 and 32 are populated, it was due to Date of death.
			ZPD zpd = (ZPD) message.getSegment(SegmentConstants.ZPD);
			if (zpd.getDeathDate() != null || zpd.getNotificationSource() != null
					|| zpd.getLastModifiedDate() != null) {
				triggerEvent = new PersonTriggerEvent(
						PersonTrigger.DestinationType.MESSAGING,
						PersonTrigger.TargetType.VISTA,
						PersonTrigger.DispatchType.NOTIFY,
						PersonTrigger.DataType.DATE_OF_DEATH);
				triggerEvent.setPersonId(person.getPersonEntityKey());
			}

			// If ZGD - 2 is populated, it was due to a change in Guardian Info
			ZGD zgd = (ZGD) message.getSegment(SegmentConstants.ZGD);
			String guardianType = zgd.getGuardianType();
			if (guardianType != null) {
				Association target = null;
				if(guardianType.equals("1")) {
					// get Association of Person for AssociationType.CODE_GUARDIAN_VA (if still there)
					target = Association.getAssociationOfType(person.getAssociations(), AssociationType.CODE_GUARDIAN_VA.getCode());
				} else if(guardianType.equals("2")) {
					// get Association of Person for AssociationType.CODE_GUARDIAN_CIVIL (if still there)
					target = Association.getAssociationOfType(person.getAssociations(), AssociationType.CODE_GUARDIAN_CIVIL.getCode());
				}
				
				if(target != null) {
					triggerEvent = new PartialPersonTriggerEvent(
							PersonTrigger.DestinationType.MESSAGING,
							PersonTrigger.TargetType.VISTA,
							PersonTrigger.DispatchType.NOTIFY,
							PersonTrigger.DataType.GUARDIAN_INFO);
					triggerEvent.setPersonId(person.getPersonEntityKey());
	
					// Cast it to a PartialPersonTriggerEvent
					((PartialPersonTriggerEvent) triggerEvent)
							.addEntityKey(target.getEntityKey());
				}
			}
			
            if(triggerEvent == null) {
            	updateErrorMessageLogEntry(messageLogEntry, "Could not retransmit Z05 message with ID = " + message.getMessageID()
                               + " as the original reason for the trigger of Z05 could not be determined "
                               + "(Change of address, Date of death or Change in Guardian info). Could be because "
                               + " of a invalid/malformed Z05 message or the data has changed since the last transmit");
            }
		}
		catch (InvalidMessageException e) {
			throw new ServiceException(
					"Could not parse message body into message segments ", e);
		}
		return triggerEvent;
	}
	
	/**
	 * Create a PartialPersonTriggerEvent based on the data type that triggered
	 * the Z05.
	 * 
	 * @param messageLogEntry
	 * @return
	 * @throws ServiceException
	 */
	private PersonTriggerEvent getORUZ04TriggerEventFromMessage(Person person,
			MessageLogEntry messageLogEntry) throws ServiceException {
		PersonTriggerEvent triggerEvent = null;

		String messageBody = messageLogEntry.getBody();
		Message message;
		try {
			message = new Message(messageBody, messageLogEntry.getType().getCode());
			MessageParser.parseSegments(message.getDelimiterCharacter(), message
					.getEncodingCharacters(), message.getMessageData());

			//Get the Insurance Policy entity key from ZIV-9
			ZIV ziv = (ZIV) message.getSegment(SegmentConstants.ZIV);
			if(ziv.getIvmID() != null)
			{
			    Set entityKeys = new HashSet();
			    InsurancePolicy insurance = getInsuranceByEntityKeyValue(person.getInsurances(), ziv.getIvmID());
                entityKeys.add(insurance.getEntityKey());
			    
			    triggerEvent = new PartialPersonTriggerEvent(
			            PersonTrigger.DestinationType.MESSAGING, PersonTrigger.TargetType.VISTA,
			            PersonTrigger.DispatchType.NOTIFY, PersonTrigger.DataType.INSURANCE);	
			    triggerEvent.setPersonId(person.getPersonEntityKey());
				// Cast it to a PartialPersonTriggerEvent
				((PartialPersonTriggerEvent) triggerEvent)
						.setEntityKeys(entityKeys);			    
			}
		}catch (InvalidMessageException e) {
			throw new ServiceException(
					"Could not parse message body into message segments ", e);
		}
		return triggerEvent;
	}	
	
	private InsurancePolicy getInsuranceByEntityKeyValue(Collection insurances, String entityKey )
	{
	    InsurancePolicy matchingPolicy = null;
	    
	    if(insurances != null)
	    {
	        for (Iterator iter = insurances.iterator(); iter.hasNext();)
            {
                InsurancePolicy policy = (InsurancePolicy) iter.next();
                
                if(policy.getEntityKey().getKeyValueAsString().equals(entityKey))
                {
                    matchingPolicy = policy;
                    break;
                }
            }
	    }
	    return matchingPolicy;
	}
}