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

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

// Framework classes

// EDB classes
import gov.va.med.esr.common.rule.service.ClinicalDeterminationRuleService;
import gov.va.med.esr.common.rule.service.CommunicationRuleService;
import gov.va.med.esr.common.rule.service.EligibilityEnrollmentRuleService;
import gov.va.med.esr.common.rule.service.ContactInfoRuleService;
import gov.va.med.esr.common.rule.service.DemographicRuleService;
import gov.va.med.esr.common.rule.service.EGTRuleService;
import gov.va.med.esr.common.rule.service.EligibilityFactorRuleService;
import gov.va.med.esr.common.rule.service.EventRuleService;
import gov.va.med.esr.common.rule.service.FinancialInfoRuleService;
import gov.va.med.esr.common.rule.service.InsuranceRuleService;
import gov.va.med.esr.common.rule.service.MilitaryRuleService;
import gov.va.med.esr.common.rule.service.AssociationRuleService;
import gov.va.med.esr.common.rule.service.POWRuleService;
import gov.va.med.esr.common.rule.service.ProcessVBARuleService;
import gov.va.med.esr.common.rule.service.PurpleHeartRuleService;
import gov.va.med.esr.common.rule.service.MergeRuleService;
import gov.va.med.esr.common.rule.service.RegistryRuleService;
import gov.va.med.esr.common.rule.service.RuleValidationService;
import gov.va.med.esr.common.model.person.Person;
import gov.va.med.esr.service.CommsEmailBulletinService;
import gov.va.med.esr.service.PSDelegateService;
import gov.va.med.fw.validation.ValidationMessages;
import gov.va.med.fw.validation.ValidationMessage;
import gov.va.med.fw.validation.ValidationServiceException;
import gov.va.med.fw.model.AbstractEntity;
import gov.va.med.fw.service.transaction.TransactionTimestampManager;
import gov.va.med.fw.service.ServiceException;
import gov.va.med.fw.service.EntityNotChangedException;

import java.util.Date;
import java.util.Collection;

/**
 * A base service class that provides accesses to all internal ESR rule services.
 *
 * @author DNS   LEV
 */
public abstract class AbstractRuleAwareServiceImpl extends AbstractHelperServiceImpl {

    public static final String ENROLLMENT_DETERMINATION_NOT_PRESENT_KEY = "ENROLLMENT_DETERMINATION_NOT_PRESENT";

   /**
    * An instance of clinicalDeterminationRuleService
    */
   private ClinicalDeterminationRuleService clinicalDeterminationRuleService = null;

   /**
    * An instance of CommunicationRuleService
    */
   private CommunicationRuleService communicationRuleService = null;

   /**
    * An instance of contactInfoRuleService
    */
   private ContactInfoRuleService contactInfoRuleService = null;

   /**
    * An instance of demographicRuleService
    */
   private DemographicRuleService demographicRuleService = null;

   /**
    * An instance of egtRuleService
    */
   private EGTRuleService egtRuleService = null;

   /**
    * An instance of eligibilityFactorRuleService
    */
   private EligibilityFactorRuleService eligibilityFactorRuleService= null;

   /**
    * An instance of eventRuleService
    */
   private EventRuleService   eventRuleService = null;

   /**
    * An instance of financialInfoRuleService
    */
   private FinancialInfoRuleService financialInfoRuleService = null;

   /**
    * An instance of insuranceRuleService
    */
   private InsuranceRuleService insuranceRuleService = null;



   /**
    * An instance of militaryRuleService
    */
   private MilitaryRuleService militaryRuleService = null;

   /**
    * An instance of personRuleService
    */
   private AssociationRuleService associationRuleService = null;

   /**
    * An instance of powRuleService
    */
   private POWRuleService powRuleService = null;

   /**
    * An instance of processVBARuleService
    */
   private ProcessVBARuleService processVBARuleService = null;

   /**
    * An instance of purpleHeartRuleService
    */
   private PurpleHeartRuleService purpleHeartRuleService = null;
   /**
    * An instance of enrollmentRuleService
    */
   private EligibilityEnrollmentRuleService enrollmentRuleService = null;

    /**
     * An instance of mergeRuleService
     */
    private MergeRuleService mergeRuleService = null;

    /**
     * An instance of registryRuleService
     */
    private RegistryRuleService registryRuleService = null;

    /**
     * An instance of RuleValidationService
     */
    private RuleValidationService ruleValidationService = null;

    /**
     * An instance of the timestamp manager used to retrieve the timestamp
     * associated with a transaction
     */
    private TransactionTimestampManager timestampManager;

    private PSDelegateService psDelegateService;

    private CommsEmailBulletinService bulletinService;

   /**
    * A default constructor to configure an empty service
    */
   protected AbstractRuleAwareServiceImpl() {
      super();
   }

    /**
     * Validates that the on-file Person contains an enrollment determination record.
     * This method should only be called for UI Person update flows.
     *
     * @param onFile The on-file Person
     * @throws ValidationServiceException if the on-file Person doesn't contain an enrollment
     * determination record.
     */
    protected void validateEnrollmentDetermination(Person onFile) throws ValidationServiceException
    {
        Validate.notNull(onFile, "An on-file Person must not be null.");
        if (!onFile.isEnrolled())
        {
            ValidationMessages validationMessages = new ValidationMessages();
            validationMessages.add(new ValidationMessage(ENROLLMENT_DETERMINATION_NOT_PRESENT_KEY));
            throw new ValidationServiceException(validationMessages);
        }
    }

    /**
     * Verifies whether a PSD Demographic Update has occurred.  A PSD Demographic Update is when any of
     * the following entities get updated: Addresses, Emails, Phones, Associations, or the DeathRecord.
     * @param incoming The incoming person
     * @param onFile The on-file person
     * @return True if any updates were found or false if not.
     */
    protected boolean determinePsdDemographicUpdate(Person incoming, Person onFile)
    {
        // Default to "no update"
        boolean psdDemographicUpdate = false;

        // Only consider a demographic update when a Z07 was received (i.e. we have an enrollment determination)
    // if (onFile.getEnrollmentDetermination() != null)
     //  {
            // Determine if a PSD demographic update has occurred
            psdDemographicUpdate =
                ((!AbstractEntity.matchesDomainValues(incoming.getAddresses(), onFile.getAddresses())) ||
                    (!AbstractEntity.matchesDomainValues(incoming.getEmails(), onFile.getEmails())) ||
                    (!AbstractEntity.matchesDomainValues(incoming.getPhones(), onFile.getPhones())) ||
                    (!AbstractEntity.matchesDomainValues(incoming.getAssociations(), onFile.getAssociations())) ||
                    (!AbstractEntity.matchesDomainValues(incoming.getDeathRecord(), onFile.getDeathRecord())));
     //   }

        // If a PSD Demographic Update has occurred, update the timestamp of the demographic update date
        if (psdDemographicUpdate)
        {
            onFile.setDemographicUpdateDate(new Date(getTimestampManager().getTransactionTimestamp().getTime()));
        }

        return psdDemographicUpdate;
    }

    /**
     * Ensures that the incoming and on-file entities don't match (i.e. that some business value has changed).
     * If the entities match each other, an EntityNotChangedException will be thrown
     * @param incoming The incoming entity
     * @param onFile The onFile entity
     * @throws EntityNotChangedException if the entites match each other
     */
    protected void ensureEntityChanged(AbstractEntity incoming, AbstractEntity onFile) throws EntityNotChangedException
    {
        if (AbstractEntity.matchesDomainValues(incoming, onFile))
        {
            String entityClassName = "null";
            if ((incoming != null) || (onFile != null))
            {
                entityClassName = (incoming != null ? incoming.getClass().getName() : onFile.getClass().getName());
            }
            throw new EntityNotChangedException("The incoming and onFile " + entityClassName +
                " entities match each other and have not changed.");
        }
    }

    /**
     * Ensures that the incoming and on-file entities don't match (i.e. that some business value has changed).
     * If the entities match each other, an EntityNotChangedException will be thrown
     * @param incoming The incoming entity
     * @param onFile The onFile entity
     * @throws EntityNotChangedException if the entites match each other
     */
    protected void ensureEntityChanged(Collection incoming, Collection onFile) throws EntityNotChangedException
    {
        if (AbstractEntity.matchesDomainValues(incoming, onFile))
        {
            throw new EntityNotChangedException("The incoming and onFile " +
                " entity collections match each other and have not changed.");
        }
    }

    /**
     * @see org.springframework.beans.factory.InitializingBean#afterPropertiesSet()
     */
    public void afterPropertiesSet() throws Exception {
       super.afterPropertiesSet();
       Validate.notNull( this.associationRuleService, "Association rule serivice is required" );
       Validate.notNull( this.clinicalDeterminationRuleService, "Clinial Determination rule serivice is required" );
       Validate.notNull( this.contactInfoRuleService, "Contact Info rule serivice is required" );
       Validate.notNull( this.demographicRuleService, "Demographic rule serivice is required" );
       Validate.notNull( this.egtRuleService, "EGT rule serivice is required" );
       Validate.notNull( this.eligibilityFactorRuleService, "Eligibility Factor rule serivice is required" );
       Validate.notNull( this.enrollmentRuleService, "Enrollment rule serivice is required" );
       Validate.notNull( this.financialInfoRuleService, "Financial rule serivice is required" );
       Validate.notNull( this.insuranceRuleService, "Insurance rule serivice is required" );
       Validate.notNull( this.militaryRuleService, "Military rule serivice is required" );
       Validate.notNull( this.powRuleService, "POW rule serivice is required" );
       Validate.notNull( this.processVBARuleService, "Process VBA rule serivice is required" );
       Validate.notNull( this.purpleHeartRuleService, "Purple Heart rule serivice is required" );
       Validate.notNull( this.psDelegateService, "psDelegateService is required" );
    }

   /**
    * @return Returns the clinicalDeterminationRuleService.
    */
   public ClinicalDeterminationRuleService getClinicalDeterminationRuleService() {
      return clinicalDeterminationRuleService;
   }

   /**
    * @param clinicalDeterminationRuleService
    *           The clinicalDeterminationRuleService to set.
    */
   public void setClinicalDeterminationRuleService( ClinicalDeterminationRuleService clinicalDeterminationRuleService) {
      this.clinicalDeterminationRuleService = clinicalDeterminationRuleService;
   }

   /**
    * @return Returns the enrollmentRuleService.
    */
   public EligibilityEnrollmentRuleService getEnrollmentRuleService() {
      return enrollmentRuleService;
   }

   /**
    * @param enrollmentRuleService The enrollmentRuleService to set.
    */
   public void setEnrollmentRuleService( EligibilityEnrollmentRuleService enrollmentRuleService ) {
      this.enrollmentRuleService = enrollmentRuleService;
   }

   /**
    * @return Returns the contactInfoRuleService.
    */
   public ContactInfoRuleService getContactInfoRuleService() {
      return this.contactInfoRuleService;
   }
   /**
    * @param contactInfoRuleService The contactInfoRuleService to set.
    */
   public void setContactInfoRuleService(
            ContactInfoRuleService contactInfoRuleService) {
      this.contactInfoRuleService = contactInfoRuleService;
   }
   /**
    * @return Returns the demographicRuleService.
    */
   public DemographicRuleService getDemographicRuleService() {
      return this.demographicRuleService;
   }
   /**
    * @param demographicRuleService The demographicRuleService to set.
    */
   public void setDemographicRuleService(
            DemographicRuleService demographicRuleService) {
      this.demographicRuleService = demographicRuleService;
   }
   /**
    * @return Returns the egtRuleService.
    */
   public EGTRuleService getEgtRuleService() {
      return this.egtRuleService;
   }
   /**
    * @param egtRuleService The egtRuleService to set.
    */
   public void setEgtRuleService(EGTRuleService egtRuleService) {
      this.egtRuleService = egtRuleService;
   }
   /**
    * @return Returns the eligibilityFactorRuleService.
    */
   public EligibilityFactorRuleService getEligibilityFactorRuleService() {
      return this.eligibilityFactorRuleService;
   }
   /**
    * @param eligibilityFactorRuleService The eligibilityFactorRuleService to set.
    */
   public void setEligibilityFactorRuleService(
            EligibilityFactorRuleService eligibilityFactorRuleService) {
      this.eligibilityFactorRuleService = eligibilityFactorRuleService;
   }

   /**
    * @return Returns the eventRuleService.
    */
   public EventRuleService getEventRuleService() {
       return eventRuleService;
   }

   /**
    * @param eventRuleService The eventRuleService to set.
    */
   public void setEventRuleService(EventRuleService eventRuleService) {
       this.eventRuleService = eventRuleService;
   }

   /**
    * @return Returns the financialInfoRuleService.
    */
   public FinancialInfoRuleService getFinancialInfoRuleService() {
      return this.financialInfoRuleService;
   }
   /**
    * @param financialInfoRuleService The financialInfoRuleService to set.
    */
   public void setFinancialInfoRuleService(
            FinancialInfoRuleService financialInfoRuleService) {
      this.financialInfoRuleService = financialInfoRuleService;
   }
   /**
    * @return Returns the insuranceRuleService.
    */
   public InsuranceRuleService getInsuranceRuleService() {
      return this.insuranceRuleService;
   }
   /**
    * @param insuranceRuleService The insuranceRuleService to set.
    */
   public void setInsuranceRuleService(InsuranceRuleService insuranceRuleService) {
      this.insuranceRuleService = insuranceRuleService;
   }
   /**
    * @return Returns the militaryRuleService.
    */
   public MilitaryRuleService getMilitaryRuleService() {
      return this.militaryRuleService;
   }
   /**
    * @param militaryRuleService The militaryRuleService to set.
    */
   public void setMilitaryRuleService(MilitaryRuleService militaryRuleService) {
      this.militaryRuleService = militaryRuleService;
   }
   /**
    * @return Returns the associationRuleService.
    */
   public AssociationRuleService getAssociationRuleService() {
      return this.associationRuleService;
   }


/**
    * @param associationRuleService The associationRuleService to set.
    */
   public void setAssociationRuleService(AssociationRuleService associationRuleService) {
      this.associationRuleService = associationRuleService;
   }
   /**
    * @return Returns the powRuleService.
    */
   public POWRuleService getPowRuleService() {
      return this.powRuleService;
   }
   /**
    * @param powRuleService The powRuleService to set.
    */
   public void setPowRuleService(POWRuleService powRuleService) {
      this.powRuleService = powRuleService;
   }
   /**
    * @return Returns the processVBARuleService.
    */
   public ProcessVBARuleService getProcessVBARuleService() {
      return this.processVBARuleService;
   }
   /**
    * @param processVBARuleService The processVBARuleService to set.
    */
   public void setProcessVBARuleService(
            ProcessVBARuleService processVBARuleService) {
      this.processVBARuleService = processVBARuleService;
   }
   /**
    * @return Returns the purpleHeartRuleService.
    */
   public PurpleHeartRuleService getPurpleHeartRuleService() {
      return this.purpleHeartRuleService;
   }
   /**
    * @param purpleHeartRuleService The purpleHeartRuleService to set.
    */
   public void setPurpleHeartRuleService(
            PurpleHeartRuleService purpleHeartRuleService) {
      this.purpleHeartRuleService = purpleHeartRuleService;
   }

    /**
     * Gets the merge rule service
     * @return the merge rule service
     */
    public MergeRuleService getMergeRuleService()
    {
        return mergeRuleService;
    }

    /**
     * Sets the merge rule service
     * @param mergeRuleService The merge rule service to set.
     */
    public void setMergeRuleService(MergeRuleService mergeRuleService)
    {
        this.mergeRuleService = mergeRuleService;
    }

    /**
     * Gets the registry rule service
     * @return the registry rule service
     */
    public RegistryRuleService getRegistryRuleService() {
        return registryRuleService;
    }

    /**
     * Sets the registry rule service
     * @param registryRuleService The registry rule service to set.
     */
    public void setRegistryRuleService(RegistryRuleService registryRuleService) {
        this.registryRuleService = registryRuleService;
    }

    /**
     * @return Returns the communicationRuleService.
     */
    public CommunicationRuleService getCommunicationRuleService() {
        return communicationRuleService;
    }

    /**
     * @param communicationRuleService The communicationRuleService to set.
     */
    public void setCommunicationRuleService(
            CommunicationRuleService communicationRuleService) {
        this.communicationRuleService = communicationRuleService;
    }

    /**
     * @return Returns the ruleValidationService.
     */
    public RuleValidationService getRuleValidationService() {
        return ruleValidationService;
    }

    /**
     * @param ruleValidationService The ruleValidationService to set.
     */
    public void setRuleValidationService(RuleValidationService ruleValidationService) {
        this.ruleValidationService = ruleValidationService;
    }

    public TransactionTimestampManager getTimestampManager() {
        return timestampManager;
    }

    public void setTimestampManager(TransactionTimestampManager timestampManager) {
        this.timestampManager = timestampManager;
    }

    public PSDelegateService getPsDelegateService()
    {
        return psDelegateService;
    }

    public void setPsDelegateService(PSDelegateService psDelegateService)
    {
        this.psDelegateService = psDelegateService;
    }

	public CommsEmailBulletinService getBulletinService() {
		return bulletinService;
	}

	public void setBulletinService(CommsEmailBulletinService bulletinService) {
		this.bulletinService = bulletinService;
	}
}