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

// Java classes
import java.util.Collection;
import java.util.Date;

// Library classes
import org.apache.commons.lang.Validate;
import org.springframework.beans.factory.NoSuchBeanDefinitionException;

// Framework classes
import gov.va.med.fw.rule.AbstractRuleFlowAwareService;
import gov.va.med.fw.rule.RuleParameters;
import gov.va.med.fw.service.ServiceException;
import gov.va.med.fw.model.AbstractEntity;
import gov.va.med.fw.service.transaction.TransactionTimestampManager;
import gov.va.med.fw.service.trigger.TriggerRouter;

// EDB classes
import gov.va.med.esr.common.model.person.Person;
import gov.va.med.esr.common.rule.service.RuleValidationService;
import gov.va.med.esr.service.PersonService;
import gov.va.med.esr.service.impl.VOAApplicationServiceImpl;
import gov.va.med.esr.common.rule.service.MergeRuleService;

/**
 * Project: Common
 * 
 * @author DNS   LEV
 * @version 1.0
 */
public abstract class AbstractRuleValidationServiceAwareImpl extends AbstractRuleFlowAwareService { 

	/**
	 * A private constants to indicate the caller 
	 */
	public static final String UI = "UI";


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

	/**
	 * An instance of personService
	 */
	private PersonService personService = null;
	
	/**
	 * An instance of personService
	 */
	private PersonService cachedPersonService = null;
	
	/**
	 * An instance of mergeRuleService
	 */
	private MergeRuleService mergeRuleService = null;

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

	/**
	 * A default constructor
	 */
	protected AbstractRuleValidationServiceAwareImpl() {
		super();
	}

	/**
	 * Method to get a pristine person. If the incoming person has an entity key,
	 * it will get person using getPerson() (returns clone). This person reflects
	 * unchanged person from cache. Otherwise, a clone is created from the
	 * incoming person.
	 * 
	 * @param person
	 * @return pristine person
	 * @throws ServiceException
	 */
	public Person getPristinePerson(Person person) throws ServiceException {
		Validate.notNull(person, "A person must not be null");
        // CCR13437 if explicit add, just get the person traits from the incoming person without issue two extra 1305 calls
        if (person.isJustAdded()) {
    		return this.getCachedPersonService().getPerson( person.getPersonEntityKey(), person.getIdentityTraits() );
        }
        
		return this.getCachedPersonService().getPerson( person.getPersonEntityKey() );
	}

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

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

	/**
	 * @return Returns the personService.
	 */
	public PersonService getPersonService() {
		return personService;
	}

	/**
	 * @param personService The personService to set.
	 */
	public void setPersonService(PersonService personService) {
		this.personService = personService;
	}

	/**
	 * @see org.springframework.beans.factory.InitializingBean#afterPropertiesSet()
	 */
	public void afterPropertiesSet() throws Exception {
		super.afterPropertiesSet();
		Validate.notNull(this.ruleValidationService,"Rule validation service is required");
		Validate.notNull(this.mergeRuleService, "A merge rule service must not be null");
		Validate.notNull(this.personService, "A person service must not be null");
		Validate.notNull(this.eventRounterService, "An event router service must not be null");
        Validate.notNull(this.timestampManager, "A Timestamp Manager service must not be null");
	}
    
    /**
     * Gets the transaction date/timestamp. For more information see TransactionTimestampManager.getTransactionTimestamp().
     */
    protected Date getTransactionDate() {
        return new Date(timestampManager.getTransactionTimestamp().getTime());
    }

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

	/**
	 * Sets a merge rule service
	 * 
	 * @param mergeRuleService
	 *           A merge rule service to be injected
	 */
	public void setMergeRuleService(MergeRuleService mergeRuleService) {
		this.mergeRuleService = mergeRuleService;
	}

	/**
	 * @return Returns the eventRounterService.
	 */
	public TriggerRouter getEventRounterService() {
		return eventRounterService;
	}

	/**
	 * @param eventRounterService
	 *           The eventRounterService to set.
	 */
	public void setEventRounterService(TriggerRouter eventRounterService) {
		this.eventRounterService = eventRounterService;
	}
	
	/**
	 * @return Returns the cachedPersonService.
	 */
	public PersonService getCachedPersonService() {
		return cachedPersonService;
	}

	/**
	 * @param cachedPersonService The cachedPersonService to set.
	 */
	public void setCachedPersonService(PersonService cachedPersonService) {
		this.cachedPersonService = cachedPersonService;
	}

    
	public TransactionTimestampManager getTimestampManager() {
        return timestampManager;
    }

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

    /** Returns a matching entity in a collection using logics defined in a 
	 * MatchRuleService class.
	 * 
	 * @param entity An entity to search for a match
	 * @param target A collection of entities to search for a match
	 * @return AbstractEntity A match entity
	 */
	protected AbstractEntity findMatchingElement(AbstractEntity entity, Collection target) {
		return this.getMergeRuleService().getMatchRuleService().findMatchingElement(entity, target);
	}
   
   protected RuleParameters getRuleParameters( String name ) throws NoSuchBeanDefinitionException {
      try {
         return (RuleParameters)this.getComponent( name, RuleParameters.class );
      }
      catch( ServiceException e ) {
         throw new NoSuchBeanDefinitionException( "Failed to obtain rule parameters: " + name );
      }
   }
}