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

// Java classes
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;

// Library classes
import org.apache.commons.lang.Validate;

// Framework classes
import gov.va.med.fw.cache.TriggerEventCacheManager;
import gov.va.med.fw.hl7.constants.SegmentConstants;
import gov.va.med.fw.hl7.segment.ZCT;
import gov.va.med.fw.service.ServiceException;
import gov.va.med.fw.service.trigger.TriggerEvent;
import gov.va.med.fw.service.trigger.TriggerIdentity;
import gov.va.med.fw.validation.ValidationMessages;
import gov.va.med.fw.validation.ValidationServiceException;

// EDB classes
import gov.va.med.esr.common.model.lookup.AssociationType;
import gov.va.med.esr.common.model.lookup.VAFacility;
import gov.va.med.esr.common.model.person.Association;
import gov.va.med.esr.common.model.person.Name;
import gov.va.med.esr.common.model.person.Person;
import gov.va.med.esr.common.rule.data.AssociationInputData;
import gov.va.med.esr.common.rule.service.AssociationRuleService;
import gov.va.med.esr.service.trigger.FilterSitesPersonTriggerEvent;
import gov.va.med.esr.service.trigger.PersonTrigger;
import gov.va.med.esr.service.trigger.PersonTriggerIdentity;

/**
 * Processes business rules related to a veteran Project: Common
 *
 * @author DNS   LEV
 * @version 1.0
 */
public class AssociationRuleServiceImpl extends AbstractRuleValidationServiceAwareImpl
      implements AssociationRuleService {

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

   /**
    * An instance of associationRuleParameters
    */
   //private RuleParameters associationRuleParameters = null;
   private String associationRuleParameters = null;
   // Association Delete flag
   public static final String DELETE_FLAG = "DELETEFLAG";
   public static final String TRIGGER_Z05 = "TRIGGER_Z05";

   private TriggerEventCacheManager triggerEventCacheManager;
   
   /**
    * A default constructor
    */
   public AssociationRuleServiceImpl() {
      super();
   }

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

   /**
    * @param triggerEventCacheManager
    *            The triggerEventCacheManager to set.
    */
   public void setTriggerEventCacheManager(
           TriggerEventCacheManager triggerEventCacheManager)
   {
       this.triggerEventCacheManager = triggerEventCacheManager;
   }
   
   /**
    * @see gov.va.med.esr.common.rule.service.impl.AbstractRuleValidationServiceAwareImpl#afterPropertiesSet()
    */
   public void afterPropertiesSet() throws Exception {
      super.afterPropertiesSet();
      Validate.notNull( this.associationRuleParameters, "Association parameters must not be null");
   }

   /**
    * @see gov.va.med.esr.common.rule.service.AssociationRuleService#manageAssociation(gov.va.med.esr.common.model.person.Association,
    *      gov.va.med.esr.common.model.person.Person)
    */
   public Person manageAssociation(Association association, Person onFile)
         throws ServiceException {
      Validate.notNull(association, "Incoming association must not be null");
      Validate.notNull(onFile, "A veteran on file must not be null");

      // Process the incoming association
      this.doProcessAssociation(association, onFile, UI, null);

      return onFile;
   }

   /**
    * @see gov.va.med.esr.common.rule.service.AssociationRuleService#processAssociations(java.util.Set,
    *      gov.va.med.esr.common.model.person.Person)
    */
   public boolean processAssociations(boolean isVistA, Set incoming, Person onFile, VAFacility sendingFacility) throws ServiceException {
	   	  boolean isRemoved = false;
	      Validate.notNull(onFile, "A veteran on file must not be null");

	      // If the incoming message does not have a VAGuardian, delete
	      // the VAGuardian from the onFile person object
	      Association incomingVAGuardian = Association.getAssociationOfType(incoming, AssociationType.CODE_GUARDIAN_VA.getCode());
	      if (incomingVAGuardian == null) {
	          Association onFileVAGuardian = onFile.getVAGuardian();
	          if (onFileVAGuardian != null) {
	              onFile.removeAssociation(onFileVAGuardian);
	          }

	          //CCR12710
	          if (logger.isDebugEnabled()) {
		            logger.debug("AssociationRuleServiceImpl processAssociations : Removed Assoication: incoming" + incoming + " onfile: " + onFile);
				}
	      }

	      //    If incoming message has delete signal for association,
	      // then remove the association
    	  //Remove associations in case of caller is other than VistA
	      Association incomingNok1 = Association.getAssociationOfType(incoming, AssociationType.CODE_PRIMARY_NEXT_OF_KIN.getCode());
	      Association incomingNok2 = Association.getAssociationOfType(incoming, AssociationType.CODE_OTHER_NEXT_OF_KIN.getCode());
	      Association incomingDesig = Association.getAssociationOfType(incoming, AssociationType.CODE_DESIGNEE.getCode());
	      Association incomingEmer1 = Association.getAssociationOfType(incoming, AssociationType.CODE_EMERGENCY_CONTACT.getCode());
	      Association incomingEmer2 = Association.getAssociationOfType(incoming, AssociationType.CODE_OTHER_EMERGENCY_CONTACT.getCode());

	      Association onFileNok1 = Association.getAssociationOfType(onFile.getAllAssociations(), AssociationType.CODE_PRIMARY_NEXT_OF_KIN.getCode());
	      Association onFileNok2 = Association.getAssociationOfType(onFile.getAllAssociations(), AssociationType.CODE_OTHER_NEXT_OF_KIN.getCode());
	      Association onFileDesig = Association.getAssociationOfType(onFile.getAllAssociations(), AssociationType.CODE_DESIGNEE.getCode());
	      Association onFileEmer1 = Association.getAssociationOfType(onFile.getAllAssociations(), AssociationType.CODE_EMERGENCY_CONTACT.getCode());
	      Association onFileEmer2 = Association.getAssociationOfType(onFile.getAllAssociations(), AssociationType.CODE_OTHER_EMERGENCY_CONTACT.getCode());
	      // Added check for LastupdateDate in both onfile and incoming
	      if(incomingNok1 != null && incomingNok1.getRepresentativeName() != null && incomingNok1.getRepresentativeName().getFamilyName() != null
	              && incomingNok1.getRepresentativeName().getFamilyName().equals(DELETE_FLAG)){
	          if(onFileNok1 != null  && onFileNok1.getLastUpdateDate() != null && incomingNok1.getLastUpdateDate()!= null &&
	                  onFileNok1.getLastUpdateDate().getTime() < incomingNok1.getLastUpdateDate().getTime()) {
	        	  	if (!isVistA) {
	                  onFile.removeAssociation(onFileNok1);
	        	  	}
	        	  	isRemoved = true;
	                  
	          }
	      }
	      if(incomingNok2 != null && incomingNok2.getRepresentativeName() != null && incomingNok2.getRepresentativeName().getFamilyName() != null
	              && incomingNok2.getRepresentativeName().getFamilyName().equals(DELETE_FLAG)){
	          if(onFileNok2 != null && onFileNok2.getLastUpdateDate() != null && incomingNok2.getLastUpdateDate() != null &&
	                  onFileNok2.getLastUpdateDate().getTime() < incomingNok2.getLastUpdateDate().getTime()){
	        	  if (!isVistA) {
	                  onFile.removeAssociation(onFileNok2);
	        	  }
	        	  isRemoved = true;
	          }
	      }
	      if(incomingEmer1 != null && incomingEmer1.getRepresentativeName() != null && incomingEmer1.getRepresentativeName().getFamilyName() != null
	              && incomingEmer1.getRepresentativeName().getFamilyName().equals(DELETE_FLAG)){
	          if(onFileEmer1 != null && onFileEmer1.getLastUpdateDate() != null && incomingEmer1.getLastUpdateDate() != null &&
	                  onFileEmer1.getLastUpdateDate().getTime() < incomingEmer1.getLastUpdateDate().getTime()){
	        	  if (!isVistA) {
	                  onFile.removeAssociation(onFileEmer1);
	        	  }
	        	  isRemoved = true;
	          }
	      }
	      if(incomingEmer2 != null && incomingEmer2.getRepresentativeName() != null && incomingEmer2.getRepresentativeName().getFamilyName() != null
	              && incomingEmer2.getRepresentativeName().getFamilyName().equals(DELETE_FLAG)){
	          if(onFileEmer2 != null && onFileEmer2.getLastUpdateDate()!= null && incomingEmer2.getLastUpdateDate()!= null &&
	                  onFileEmer2.getLastUpdateDate().getTime() < incomingEmer2.getLastUpdateDate().getTime()){
	        	  if (!isVistA) {
	                  onFile.removeAssociation(onFileEmer2);
	        	  }
	        	  isRemoved = true;
	          }
	      }
	      if(incomingDesig != null && incomingDesig.getRepresentativeName() != null && incomingDesig.getRepresentativeName().getFamilyName() != null
	              && incomingDesig.getRepresentativeName().getFamilyName().equals(DELETE_FLAG)){
	          if(onFileDesig != null && onFileDesig.getLastUpdateDate() != null && incomingDesig.getLastUpdateDate() != null &&
	                  onFileDesig.getLastUpdateDate().getTime() < incomingDesig.getLastUpdateDate().getTime()){
	        	  if (!isVistA) {
	                  onFile.removeAssociation(onFileDesig);
	        	  }
	        	  isRemoved = true;
	          }
	      } 

	      this.doProcessAssociations(incoming, onFile, null, sendingFacility);
	      return isRemoved;
	}
   
   public void triggerZ05(VAFacility sendingFacility, Person updatedPerson) throws ServiceException {
	   Map updatedKeysAndSitesMap = new HashMap();
	   
	   if (sendingFacility != null) {
		   updatedKeysAndSitesMap.put(updatedPerson, sendingFacility);
   		}
   
	   FilterSitesPersonTriggerEvent evt = new FilterSitesPersonTriggerEvent(
	            PersonTrigger.DestinationType.MESSAGING, PersonTrigger.TargetType.VISTA,
	            PersonTrigger.DispatchType.NOTIFY, PersonTrigger.DataType.ADDRESS,
	            updatedKeysAndSitesMap);
	   
	   if (updatedPerson != null) {
		   evt.setPersonId(updatedPerson.getPersonEntityKey());
	       evt.setIdentityTraits(updatedPerson.getIdentityTraits());
	       evt.setEntityKeysAndSites(updatedKeysAndSitesMap);
    	   evt.setPayload(updatedPerson);
    	   evt.setName(TRIGGER_Z05);
       }

       boolean hasAddressTriggerEvent = false;
       if (triggerEventCacheManager.getTriggerEvents() != null)
       {
           for (Iterator iter = triggerEventCacheManager.getTriggerEvents()
                   .iterator(); iter.hasNext();)
           {
               TriggerEvent currentEvent = (TriggerEvent) iter.next();
               TriggerIdentity identity = currentEvent.getTriggerIdentity();

               if (identity instanceof PersonTriggerIdentity)
               {
                   if(((PersonTriggerIdentity)identity).matchTriggerCriteria((PersonTriggerIdentity)evt.getTriggerIdentity()))
                   {
                       hasAddressTriggerEvent = true;
                       break;
                   }
               }
           }
         }

       if (!hasAddressTriggerEvent)
       {
           // Create the trigger event
           Set triggerEvents = new HashSet();
           triggerEvents.add(evt);

           triggerEventCacheManager.storeTriggerEvents(triggerEvents);

       }	   
   }
   public void processAssociations(Set incoming, Person onFile, VAFacility sendingFacility) throws ServiceException {
	   //Invoke as caller is non VistA
	   processAssociations(false, incoming, onFile, sendingFacility);
   }
   
   public String getAssociationRuleParameters() {
      return associationRuleParameters;
   }

   public void setAssociationRuleParameters( String associationRuleParameters) {
      this.associationRuleParameters = associationRuleParameters;
   }

   private void doProcessAssociation(Association incoming, Person onFile, String caller, VAFacility sendingFacility)
         throws ServiceException {

      Validate.notNull(incoming, "An association must not be null");
        if(incoming.getRepresentativeName() != null && incoming.getRepresentativeName().getFamilyName() != null) {
            if(incoming.getRepresentativeName().getFamilyName().equals(DELETE_FLAG)) {
            	return;
            }
        }
      Person pristine = this.getPristinePerson(onFile);

      // Validate an incoming address first
      ValidationMessages messages = this.getRuleValidationService().validateAssociation(
            incoming, onFile, pristine, UI.equals(caller));

      if( messages != null && !messages.isEmpty() ) {
         throw new ValidationServiceException(messages);
      }

      //CCR12710
      if (logger.isDebugEnabled()) {
          logger.debug("AssociationRuleServiceImpl processAssociationRuleFlow : incoming" + incoming + " onfile: " + onFile);
		}

      // Invoke process association rule flow
      invokeRuleFlow( this.getRuleParameters( this.getAssociationRuleParameters() ),
                      new AssociationInputData( incoming, onFile, pristine, UI.equals(caller), sendingFacility ) );
   }

   private void doProcessAssociations(Set incoming, Person onFile, String caller, VAFacility sendingFacility)
         throws ServiceException {

      // Process the incoming associations
      for( Iterator iter = incoming.iterator(); iter.hasNext(); ) {
         this.doProcessAssociation((Association)iter.next(), onFile, caller, sendingFacility);
      }
   }
   /**
    * @see gov.va.med.esr.common.rule.service.AssociationRuleService#removeAssociations(java.util.Set,
    *      gov.va.med.esr.common.model.person.Person)
    */
   public void removeAssociations(Set incoming, Person onFile, VAFacility sendingFacility) throws ServiceException {
      Validate.notNull(onFile, "A veteran on file must not be null");


      //    If incoming message has delete signal for association,
      // then remove the association
      Association incomingNok1 = Association.getAssociationOfType(incoming, AssociationType.CODE_PRIMARY_NEXT_OF_KIN.getCode());
      Association incomingNok2 = Association.getAssociationOfType(incoming, AssociationType.CODE_OTHER_NEXT_OF_KIN.getCode());
      Association incomingDesig = Association.getAssociationOfType(incoming, AssociationType.CODE_DESIGNEE.getCode());
      Association incomingEmer1 = Association.getAssociationOfType(incoming, AssociationType.CODE_EMERGENCY_CONTACT.getCode());
      Association incomingEmer2 = Association.getAssociationOfType(incoming, AssociationType.CODE_OTHER_EMERGENCY_CONTACT.getCode());

      Association onFileNok1 = Association.getAssociationOfType(onFile.getAllAssociations(), AssociationType.CODE_PRIMARY_NEXT_OF_KIN.getCode());
      Association onFileNok2 = Association.getAssociationOfType(onFile.getAllAssociations(), AssociationType.CODE_OTHER_NEXT_OF_KIN.getCode());
      Association onFileDesig = Association.getAssociationOfType(onFile.getAllAssociations(), AssociationType.CODE_DESIGNEE.getCode());
      Association onFileEmer1 = Association.getAssociationOfType(onFile.getAllAssociations(), AssociationType.CODE_EMERGENCY_CONTACT.getCode());
      Association onFileEmer2 = Association.getAssociationOfType(onFile.getAllAssociations(), AssociationType.CODE_OTHER_EMERGENCY_CONTACT.getCode());
      // Added check for LastupdateDate in both onfile and incoming
      if(incomingNok1 != null && incomingNok1.getRepresentativeName() != null && incomingNok1.getRepresentativeName().getFamilyName() != null
              && incomingNok1.getRepresentativeName().getFamilyName().equals(DELETE_FLAG)){
          if(onFileNok1 != null  && onFileNok1.getLastUpdateDate() != null && incomingNok1.getLastUpdateDate()!= null &&
                  onFileNok1.getLastUpdateDate().getTime() < incomingNok1.getLastUpdateDate().getTime()){
              onFile.removeAssociation(onFileNok1);
          }
      }
      if(incomingNok2 != null && incomingNok2.getRepresentativeName() != null && incomingNok2.getRepresentativeName().getFamilyName() != null
              && incomingNok2.getRepresentativeName().getFamilyName().equals(DELETE_FLAG)){
          if(onFileNok2 != null && onFileNok2.getLastUpdateDate() != null && incomingNok2.getLastUpdateDate() != null &&
                  onFileNok2.getLastUpdateDate().getTime() < incomingNok2.getLastUpdateDate().getTime()){
              onFile.removeAssociation(onFileNok2);
          }
      }
      if(incomingEmer1 != null && incomingEmer1.getRepresentativeName() != null && incomingEmer1.getRepresentativeName().getFamilyName() != null
              && incomingEmer1.getRepresentativeName().getFamilyName().equals(DELETE_FLAG)){
          if(onFileEmer1 != null && onFileEmer1.getLastUpdateDate() != null && incomingEmer1.getLastUpdateDate() != null &&
                  onFileEmer1.getLastUpdateDate().getTime() < incomingEmer1.getLastUpdateDate().getTime()){
              onFile.removeAssociation(onFileEmer1);
          }
      }
      if(incomingEmer2 != null && incomingEmer2.getRepresentativeName() != null && incomingEmer2.getRepresentativeName().getFamilyName() != null
              && incomingEmer2.getRepresentativeName().getFamilyName().equals(DELETE_FLAG)){
          if(onFileEmer2 != null && onFileEmer2.getLastUpdateDate()!= null && incomingEmer2.getLastUpdateDate()!= null &&
                  onFileEmer2.getLastUpdateDate().getTime() < incomingEmer2.getLastUpdateDate().getTime()){
              onFile.removeAssociation(onFileEmer2);
          }
      }
      if(incomingDesig != null && incomingDesig.getRepresentativeName() != null && incomingDesig.getRepresentativeName().getFamilyName() != null
              && incomingDesig.getRepresentativeName().getFamilyName().equals(DELETE_FLAG)){
          if(onFileDesig != null && onFileDesig.getLastUpdateDate() != null && incomingDesig.getLastUpdateDate() != null &&
                  onFileDesig.getLastUpdateDate().getTime() < incomingDesig.getLastUpdateDate().getTime()){
              onFile.removeAssociation(onFileDesig);
          }
      }
}

   public boolean upddateAssociations(Set incoming, Person onFile, VAFacility sendingFacility) throws ServiceException {
	      Validate.notNull(onFile, "A veteran on file must not be null");

	      boolean isUpdated = false;

	      //    If incoming message has delete signal for association,
	      // then remove the association
	      Association incomingNok1 = Association.getAssociationOfType(incoming, AssociationType.CODE_PRIMARY_NEXT_OF_KIN.getCode());
	      Association incomingNok2 = Association.getAssociationOfType(incoming, AssociationType.CODE_OTHER_NEXT_OF_KIN.getCode());
	      Association incomingDesig = Association.getAssociationOfType(incoming, AssociationType.CODE_DESIGNEE.getCode());
	      Association incomingEmer1 = Association.getAssociationOfType(incoming, AssociationType.CODE_EMERGENCY_CONTACT.getCode());
	      Association incomingEmer2 = Association.getAssociationOfType(incoming, AssociationType.CODE_OTHER_EMERGENCY_CONTACT.getCode());

	      Association onFileNok1 = Association.getAssociationOfType(onFile.getAllAssociations(), AssociationType.CODE_PRIMARY_NEXT_OF_KIN.getCode());
	      Association onFileNok2 = Association.getAssociationOfType(onFile.getAllAssociations(), AssociationType.CODE_OTHER_NEXT_OF_KIN.getCode());
	      Association onFileDesig = Association.getAssociationOfType(onFile.getAllAssociations(), AssociationType.CODE_DESIGNEE.getCode());
	      Association onFileEmer1 = Association.getAssociationOfType(onFile.getAllAssociations(), AssociationType.CODE_EMERGENCY_CONTACT.getCode());
	      Association onFileEmer2 = Association.getAssociationOfType(onFile.getAllAssociations(), AssociationType.CODE_OTHER_EMERGENCY_CONTACT.getCode());
	      // Added check for LastupdateDate in both onfile and incoming
	      if(incomingNok1 != null && incomingNok1.getRepresentativeName() != null && incomingNok1.getRepresentativeName().getFamilyName() != null
	              && incomingNok1.getRepresentativeName().getFamilyName().equals(DELETE_FLAG)){
	          if(onFileNok1 != null  && onFileNok1.getLastUpdateDate() != null && incomingNok1.getLastUpdateDate()!= null &&
	                  onFileNok1.getLastUpdateDate().getTime() < incomingNok1.getLastUpdateDate().getTime()){
	        	  if (onFileNok1.getRepresentativeName() == null) {
	        		  onFileNok1.setRepresentativeName(new Name());
	        	  }
	        	  onFileNok1.getRepresentativeName().setFamilyName(DELETE_FLAG);
	        	  isUpdated = true;
	          }
	      }
	      if(incomingNok2 != null && incomingNok2.getRepresentativeName() != null && incomingNok2.getRepresentativeName().getFamilyName() != null
	              && incomingNok2.getRepresentativeName().getFamilyName().equals(DELETE_FLAG)){
	          if(onFileNok2 != null && onFileNok2.getLastUpdateDate() != null && incomingNok2.getLastUpdateDate() != null &&
	                  onFileNok2.getLastUpdateDate().getTime() < incomingNok2.getLastUpdateDate().getTime()){
	        	  if (onFileNok2.getRepresentativeName() == null) {
	        		  onFileNok2.setRepresentativeName(new Name());
	        	  }
	              onFileNok2.getRepresentativeName().setFamilyName(DELETE_FLAG);
	              isUpdated = true;
	          }
	      }
	      if(incomingEmer1 != null && incomingEmer1.getRepresentativeName() != null && incomingEmer1.getRepresentativeName().getFamilyName() != null
	              && incomingEmer1.getRepresentativeName().getFamilyName().equals(DELETE_FLAG)){
	          if(onFileEmer1 != null && onFileEmer1.getLastUpdateDate() != null && incomingEmer1.getLastUpdateDate() != null &&
	                  onFileEmer1.getLastUpdateDate().getTime() < incomingEmer1.getLastUpdateDate().getTime()){
	        	  if (onFileEmer1.getRepresentativeName() == null) {
	        		  onFileEmer1.setRepresentativeName(new Name());
	        	  }
	              onFileEmer1.getRepresentativeName().setFamilyName(DELETE_FLAG);
	              isUpdated = true;
	          }
	      }
	      if(incomingEmer2 != null && incomingEmer2.getRepresentativeName() != null && incomingEmer2.getRepresentativeName().getFamilyName() != null
	              && incomingEmer2.getRepresentativeName().getFamilyName().equals(DELETE_FLAG)){
	          if(onFileEmer2 != null && onFileEmer2.getLastUpdateDate()!= null && incomingEmer2.getLastUpdateDate()!= null &&
	                  onFileEmer2.getLastUpdateDate().getTime() < incomingEmer2.getLastUpdateDate().getTime()){
	        	  if (onFileEmer2.getRepresentativeName() == null) {
	        		  onFileEmer2.setRepresentativeName(new Name());
	        	  }
	              onFileEmer2.getRepresentativeName().setFamilyName(DELETE_FLAG);
	              isUpdated = true;
	          }
	      }
	      if(incomingDesig != null && incomingDesig.getRepresentativeName() != null && incomingDesig.getRepresentativeName().getFamilyName() != null
	              && incomingDesig.getRepresentativeName().getFamilyName().equals(DELETE_FLAG)){
	          if(onFileDesig != null && onFileDesig.getLastUpdateDate() != null && incomingDesig.getLastUpdateDate() != null &&
	                  onFileDesig.getLastUpdateDate().getTime() < incomingDesig.getLastUpdateDate().getTime()){
	        	  if (onFileDesig.getRepresentativeName() == null) {
	        		  onFileDesig.setRepresentativeName(new Name());
	        	  }
	              onFileDesig.getRepresentativeName().setFamilyName(DELETE_FLAG);
	              isUpdated = true;
	          }
	      }
	      return isUpdated;
	}   
}