/********************************************************************
 * Copyright © 2006 VHA. All rights reserved
 ********************************************************************/


package gov.va.med.esr.service;

import java.util.Iterator;
import java.util.Set;

import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
import org.apache.commons.lang.Validate;

import gov.va.med.esr.common.model.ee.EnrollmentDetermination;
import gov.va.med.esr.common.model.ee.ServiceConnectionAward;
import gov.va.med.esr.common.model.financials.IncomeTest;
import gov.va.med.esr.common.model.financials.IncomeTestStatus;
import gov.va.med.esr.common.model.lookup.IncomeTestType;
import gov.va.med.esr.common.model.person.Person;
import gov.va.med.esr.service.trigger.BulletinTriggerEvent;
import gov.va.med.esr.service.trigger.LetterTriggerEvent;
import gov.va.med.esr.service.trigger.PersonTriggerEvent;
import gov.va.med.fw.cache.EntityCacheManager;
import gov.va.med.fw.cache.TriggerEventCacheManager;
import gov.va.med.fw.model.AbstractEntity;
import gov.va.med.fw.service.AbstractComponent;
import gov.va.med.fw.service.trigger.TriggerEvent;

/**
 * Initial javadoc for class MigrateEEResultAdvice.
 * TODO - Add content here
 * 
 * Created Sep 14, 2006 1:24:19 PM
 * @author DNS   BOHMEG
 */
public class MigrateEEResultAdvice extends AbstractComponent implements
		MethodInterceptor {
	private EntityCacheManager entityCacheManager;
	private TriggerEventCacheManager triggerEventCacheManager;
	private PersonHelperService personHelperService;
	private String modelPropertiesSetId;

	/* (non-Javadoc)
	 * @see org.aopalliance.intercept.MethodInterceptor#invoke(org.aopalliance.intercept.MethodInvocation)
	 */
	public Object invoke(MethodInvocation arg0) throws Throwable {
		EEResult result = (EEResult) arg0.proceed();
		if(result.isCalculated()) {
			// EE was calculated so do checks here
			Person incomingPerson = (Person) entityCacheManager.getItem(result.getPersonEntityKey());
			Person resultPerson = result.getUpdatedPerson();
			
			EnrollmentDetermination incomingEnrollmentDetermation = incomingPerson.getEnrollmentDetermination();
			EnrollmentDetermination resultEnrollmentDetermation = resultPerson.getEnrollmentDetermination(); // assume not-null
			ServiceConnectionAward incomingSCA = incomingPerson.getServiceConnectionAward();
			ServiceConnectionAward resultSCA = resultPerson.getServiceConnectionAward(); // assume not-null
			
			// check the set of EE properties (from model_properties.xml) to determine if any of that data changed
			if(didAbstractEntityChange(incomingEnrollmentDetermation, resultEnrollmentDetermation) ||
					didAbstractEntityChange(incomingSCA, resultSCA) ||
					didIncomeTestDataChange(incomingPerson, resultPerson)) {
				if(logger.isInfoEnabled())
					logger.info("EE data changed for Person: " + result.getPersonEntityKey().getKeyValueAsString());
				result.setEeChange(true);
				
				Set triggerEvents = triggerEventCacheManager.getTriggerEvents();
				Iterator itr = triggerEvents != null ? triggerEvents.iterator() : null;
				TriggerEvent event = null;
				while(itr != null && itr.hasNext()) {
					event = (TriggerEvent) itr.next();
					
					if(event instanceof BulletinTriggerEvent)
						result.addBulletinTriggerEvent((BulletinTriggerEvent) event);
					else if(event instanceof LetterTriggerEvent)
						result.addLetterTriggerEvent((LetterTriggerEvent) event);
					else if(event instanceof PersonTriggerEvent)
						result.addHL7MessageTriggerEvent((PersonTriggerEvent) event);					
				}
			} else {
				if(logger.isInfoEnabled())
					logger.info("EE data did NOT change for Person: " + result.getPersonEntityKey().getKeyValueAsString());
				
				// nothing changed so flush the events since not applicable
				triggerEventCacheManager.removeTriggerEvents();
				result.setEeChange(false);
			}
		}

		return result;
	}
	
	private boolean didAbstractEntityChange(AbstractEntity incoming, AbstractEntity result) {
		boolean changed = false;
		if(incoming == null && result != null)
			changed = true;
		else if(incoming != null && !incoming.isModelPropertiesEqual(result, modelPropertiesSetId))
			changed = true;
		if(changed && logger.isInfoEnabled())
			logger.info("EE data changed for class: " + result.getClass().getName());
		return changed;		
	}
	
	private boolean didIncomeTestDataChange(Person incoming, Person result) {
		boolean changed = false;
		IncomeTest incomingCurrentIncomeTest = personHelperService.getCurrentIncomeTest(incoming);
		IncomeTest resultCurrentIncomeTest = personHelperService.getCurrentIncomeTest(result);
		if((incomingCurrentIncomeTest != null && resultCurrentIncomeTest == null) ||
				(incomingCurrentIncomeTest == null && resultCurrentIncomeTest != null))
			changed = true;
		
		if(!changed && incomingCurrentIncomeTest != null && resultCurrentIncomeTest != null) {
			// assume the type is non-null
			if(!incomingCurrentIncomeTest.getType().equals(resultCurrentIncomeTest.getType())) {
				changed = true;
			} else {
				// same test type, now check statuses...
				IncomeTestStatus incomingCopayExemptionStatus = incomingCurrentIncomeTest.getIncomeTestStatus(IncomeTestType.CODE_CO_PAY_EXEMPTION_TEST);
				IncomeTestStatus incomingMeansTestStatus = incomingCurrentIncomeTest.getIncomeTestStatus(IncomeTestType.CODE_MEANS_TEST);

				IncomeTestStatus resultCopayExemptionStatus = resultCurrentIncomeTest.getIncomeTestStatus(IncomeTestType.CODE_CO_PAY_EXEMPTION_TEST);
				IncomeTestStatus resultMeansTestStatus = resultCurrentIncomeTest.getIncomeTestStatus(IncomeTestType.CODE_MEANS_TEST);

				if(incomingCopayExemptionStatus != null && resultCopayExemptionStatus != null) {
					if((incomingCopayExemptionStatus != null && resultCopayExemptionStatus == null) ||
							(incomingCopayExemptionStatus == null && resultCopayExemptionStatus != null))
						changed = true;
					else if(!incomingCopayExemptionStatus.equals(resultCopayExemptionStatus))
						changed = true;
				}
				
				if(!changed && incomingMeansTestStatus != null && resultMeansTestStatus != null) {
					if((incomingMeansTestStatus != null && resultMeansTestStatus == null) ||
							(incomingMeansTestStatus == null && resultMeansTestStatus != null))
						changed = true;
					else if(!incomingMeansTestStatus.equals(resultMeansTestStatus))
						changed = true;
				}
			}				
		}
		if(changed && logger.isInfoEnabled())
			logger.info("EE data changed for IncomeTest data");
		
		return changed;
	}

	/**
	 * @return Returns the entityCacheManager.
	 */
	public EntityCacheManager getEntityCacheManager() {
		return entityCacheManager;
	}

	/**
	 * @param entityCacheManager The entityCacheManager to set.
	 */
	public void setEntityCacheManager(EntityCacheManager entityCacheManager) {
		this.entityCacheManager = entityCacheManager;
	}

	/**
	 * @return Returns the triggerEventCacheManager.
	 */
	public TriggerEventCacheManager getTriggerEventCacheManager() {
		return triggerEventCacheManager;
	}

	/**
	 * @param triggerEventCacheManager The triggerEventCacheManager to set.
	 */
	public void setTriggerEventCacheManager(
			TriggerEventCacheManager triggerEventCacheManager) {
		this.triggerEventCacheManager = triggerEventCacheManager;
	}

	public void afterPropertiesSet() {
		Validate.notNull(entityCacheManager, "entityCacheManager is required");
		Validate.notNull(triggerEventCacheManager, "triggerEventCacheManager is required");
		Validate.notNull(personHelperService, "personHelperService is required");		
		Validate.notNull(modelPropertiesSetId, "modelPropertiesSetId is required");
	}

	/**
	 * @return Returns the modelPropertiesSetId.
	 */
	public String getModelPropertiesSetId() {
		return modelPropertiesSetId;
	}

	/**
	 * @param modelPropertiesSetId The modelPropertiesSetId to set.
	 */
	public void setModelPropertiesSetId(String modelPropertiesSetId) {
		this.modelPropertiesSetId = modelPropertiesSetId;
	}

	/**
	 * @return Returns the personHelperService.
	 */
	public PersonHelperService getPersonHelperService() {
		return personHelperService;
	}

	/**
	 * @param personHelperService The personHelperService to set.
	 */
	public void setPersonHelperService(PersonHelperService personHelperService) {
		this.personHelperService = personHelperService;
	}
}
