package gov.va.med.fw.rule;

// Java classes
import java.util.Collection;
import java.util.Iterator;
import java.util.Map;

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

import gov.va.med.fw.service.AbstractComponent;
import gov.va.med.fw.service.ServiceException;
import gov.va.med.fw.validation.Validatable;
import gov.va.med.fw.validation.ValidationMessages;

// EDB classes

/** Provides access to a rule flow and methods to invoke a rule set using
 * the injected rule flow.
 * 
 * @author DNS   LEV
 * @version 1.0
 */
public abstract class AbstractRuleFlowAwareService extends AbstractComponent {

	private RuleFlow ruleFlow = null;

	/**
	 * A default constructor for a base service that knows about an instance of a
	 * Rule Flow.
	 */
	protected AbstractRuleFlowAwareService() {
		super();
	}

	/** Returns a rule flow that is used to invoke a rule set
	 * @return Returns the ruleFlow.
	 */
	public RuleFlow getRuleFlow() {
		return ruleFlow;
	}

	/** Sets a rule flow to be used for invoking a rule set
	 * @param ruleFlow The ruleFlow to set.
	 */
	public void setRuleFlow(RuleFlow ruleFlow) {
		this.ruleFlow = ruleFlow;
	}

	/**
	 * @see org.springframework.beans.factory.InitializingBean#afterPropertiesSet()
	 */
	public void afterPropertiesSet() throws Exception {
		Validate.notNull(ruleFlow, "A rule flow service must not be null");
	}

	/**
	 * Invokes a rule flow to process a rule set
	 * @param parameters A collection of rule parameters
	 * @param ruleData A data class encapsulates insurance data
	 * @throws ServiceException In case of errors in processing insurance data
	 */
	protected ValidationMessages invokeRuleFlow(RuleParameters parameters,
            RuleDataAware ruleData) throws ServiceException
    {
        return this.invokeRuleFlow(parameters, ruleData,
                Validatable.ELIMINATION);
    }
	
	/**
	 * Invokes a rule flow to process a rule set
	 * @param parameters A collection of rule parameters
	 * @param ruleData A data class encapsulates insurance data
	 * @param validationMode Validatable.EXHAUSTIVE or Validatable.ELIMINATION
	 * @throws ServiceException In case of errors in processing insurance data
	 */
	protected ValidationMessages invokeRuleFlow(RuleParameters parameters,
            RuleDataAware ruleData, String validationMode)
            throws ServiceException
    {

        ValidationMessages messages = null;

        try
        {
            setExhaustiveMode(parameters, Validatable.EXHAUSTIVE
                    .equals(validationMode));

            this.getRuleFlow().execute(parameters, ruleData);

            RuleState ruleState = parameters.getState();

            // If a validation mode is set exhaustive mode, check for validation
            // messages here
            if (ruleState != null && ruleState.isExhaustiveMode())
                messages = ruleState.getValidationMessages();

        } catch (RuleException e)
        {
            throw new ServiceException("Failed to invoke rule flow "
                    + parameters.getRuleFlowName(), e);
        }

        return messages;
    }	
	
	/**
	 * @param parameters
	 * @param ruleData
	 * @param validationMode
	 * @throws ServiceException
	 */
	protected void invokeRuleFlow( RuleParameters parameters, Map ruleData) throws ServiceException {

		try {
			this.getRuleFlow().execute(parameters, ruleData);
		}
		catch (RuleException e) {
			throw new ServiceException("Failed to invoke rule flow " + parameters.getRuleFlowName(), e);
		}
	}	
	
	  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 );
	      }
	   }	
	  
	  protected void setExhaustiveMode( RuleParameters parameters, boolean flag ) {
	       
	       Collection params = parameters.getRuleParameters().values();
	       for (Iterator i = params.iterator(); i.hasNext();) {
	          RuleParameter param = (RuleParameter) i.next();
	          param.getState().setExhaustiveMode( flag );
	       }
	       parameters.getState().setExhaustiveMode( flag );
	  }	  
}