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

// Java classes
import java.util.Date;
import java.util.HashSet;
import java.util.Set;
import java.util.Iterator;

// Library classes
import org.apache.commons.lang.Validate;
import org.apache.commons.lang.builder.ToStringBuilder;
import org.apache.commons.lang.builder.ToStringStyle;

// Framework classes
import gov.va.med.fw.model.AbstractEntity;
import gov.va.med.fw.service.ServiceException;
import gov.va.med.fw.util.DateUtils;
import gov.va.med.fw.util.StringUtils;
import gov.va.med.fw.validation.ValidationMessages;
import gov.va.med.fw.validation.ValidationServiceException;

// EDB classes
import gov.va.med.esr.UseCaseName;
import gov.va.med.esr.common.batchprocess.LoadVSSCDataResult;
import gov.va.med.esr.common.infra.ImpreciseDate;
import gov.va.med.esr.common.infra.ImpreciseDateUtils;
import gov.va.med.esr.common.model.ee.CombatEpisode;
import gov.va.med.esr.common.model.ee.ConflictExperience;
import gov.va.med.esr.common.model.ee.MilitaryService;
import gov.va.med.esr.common.model.ee.MilitaryServiceSiteRecord;
import gov.va.med.esr.common.model.ee.MilitaryServiceEpisode;
import gov.va.med.esr.common.model.ee.VerificationInfo;
import gov.va.med.esr.common.model.lookup.DischargeType;
import gov.va.med.esr.common.model.lookup.MilitaryServiceComponent;
import gov.va.med.esr.common.model.lookup.ServiceBranch;
import gov.va.med.esr.common.model.lookup.VAFacility;
import gov.va.med.esr.common.model.person.Person;
import gov.va.med.esr.common.rule.data.MilitaryServiceInputData;
import gov.va.med.esr.common.rule.service.MilitaryRuleService;
import gov.va.med.esr.common.rule.service.RuleValidationService;
import gov.va.med.esr.service.MsdsResponseInfo;

/** Implements business rules to process military service information.
 * 
 * Project: Common
 * @author DNS   LEV
 * @version 1.0
 */
public class MilitaryRuleServiceImpl extends AbstractRuleValidationServiceAwareImpl implements MilitaryRuleService {

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

	/**
	 * An instance of periodOfServiceRuleParameters
	 */
	private String periodOfServiceRuleParameters = null;
	
	/**
	 * An instance of processMilitaryRuleParameters
	 */
	private String processMilitaryRuleParameters = null;
	
	/**
	 * An instance of processMSDSDataRuleParameters
	 */
	private String processMSDSDataRuleParameters = null;	
	
	/**
    * A default constructor
    */
   public MilitaryRuleServiceImpl() {
      super();
   }
   
   /** Invokes business rules to validate then to update military service info coming from
    * a message.
    * 
    * @see gov.va.med.esr.common.rule.service.MilitaryRuleService#processMilitaryService(gov.va.med.esr.common.model.ee.MilitaryService, gov.va.med.esr.common.model.person.Person)
    */
   public Person processMilitaryService(VAFacility sendingFacility, String dfn, MilitaryService ms, Person onFile,String caller ) throws ServiceException {
		return this.doProcessMilitaryService(sendingFacility, dfn, ms, onFile, caller);
   }
   
   /** Invokes business rules to validate and to update military service episodes
    * and conflict experiences.
    * 
    * @see gov.va.med.esr.common.rule.service.MilitaryRuleService#processMilitaryService(gov.va.med.esr.common.model.ee.MilitaryService, gov.va.med.esr.common.model.person.Person)
    */
   public Person manageMilitaryService( MilitaryService ms, Person onFile ) throws ServiceException {
   	return this.manageMilitaryService( ms, onFile, false);
   }


	/**
	 * @see gov.va.med.esr.common.rule.service.MilitaryRuleService#manageMilitaryService(gov.va.med.esr.common.model.ee.MilitaryService, gov.va.med.esr.common.model.person.Person, boolean)
	 */
   public Person manageMilitaryService(MilitaryService ms, Person onFile, boolean isFromEE) throws ServiceException {
        return this.doManageMilitaryService( ms, onFile, isFromEE);
   }

    /** 
	 * Invokes business rules to upload vssc data (oef/oif episodes)
	 * @see gov.va.med.esr.common.rule.service.MilitaryRuleService#processVSSCData(gov.va.med.esr.common.model.ee.MilitaryService, gov.va.med.esr.common.model.person.Person, gov.va.med.esr.common.batchprocess.LoadVSSCDataResult)
	 */
	public Person processVSSCData(MilitaryService ms, Person onFile, LoadVSSCDataResult loadVSSCDataResult) throws ServiceException {
		return this.doProcessVSSCData(ms, onFile,loadVSSCDataResult);
	}

	/**
	 * @see gov.va.med.esr.common.rule.service.MilitaryRuleService#processMSDSData(gov.va.med.esr.common.model.person.Person, gov.va.med.esr.common.model.person.Person, gov.va.med.esr.service.MsdsResponseInfo)
	 */
//	public boolean processMSDSData(Person incoming, Person onFile, MsdsResponseInfo info) throws ServiceException {
//	   	return this.doProcessMSDSData(incoming, onFile, UseCaseName.MSDSPROC, info);
//	}

	
	
	/**
	 * @see gov.va.med.esr.common.rule.service.MilitaryRuleService#processMSDSData(gov.va.med.esr.common.model.person.Person, gov.va.med.esr.common.model.person.Person, gov.va.med.esr.service.MsdsResponseInfo)
	 */
	public boolean processMSDSData(Person incoming, Person onFile, MsdsResponseInfo info) throws ServiceException {
		return this.doProcessMSDSData(incoming, onFile, UseCaseName.MSDSPROC, info);
	}

	public boolean hasQualifyingMse(Person person, VerificationInfo info) throws ServiceException {
    	Validate.notNull( person, "A veteran must not be null " );

		Person pristine = this.getPristinePerson(person);
    	
    	if (person.getMilitaryService() != null) { 
    		MilitaryServiceInputData militaryData = 
    			new MilitaryServiceInputData(person.getMilitaryService(), person,pristine,false);
    		militaryData.setUseCaseName(UseCaseName.VBAQMSE);
    		militaryData.setMilitaryService(person.getMilitaryService());      

    		this.invokeRuleFlow(this.getRuleParameters( this.getProcessMSDSDataRuleParameters()),militaryData);
    		info.setMsdsMessageDate(militaryData.getMsdsMessageDate());
    		return militaryData.hasQmse();
    	}
     	return false; 
	}

	/**
	 * @see gov.va.med.esr.common.rule.service.MilitaryRuleService#evaluateMSDSImprovement(gov.va.med.esr.common.model.person.Person, gov.va.med.esr.common.model.person.Person)
	 */
	public boolean evaluateMSDSImprovement(Person incoming, Person onFile, MsdsResponseInfo info) throws ServiceException {
    	return this.doProcessMSDSData(incoming, onFile, UseCaseName.MSDSEVAL, info);
	}

	private boolean doProcessMSDSData(Person incoming, Person onFile, String useCase, MsdsResponseInfo info) 
		throws ServiceException {
		
    	Validate.notNull( onFile, "A current veteran must not be null " );
    	Validate.notNull( incoming, "An incoming veteran must not be null " );
    	Validate.notNull( useCase, "Use case must be specified" );    	

		Person pristine = this.getPristinePerson(onFile);
    	
    	if (UseCaseName.MSDSEVAL.equals(useCase) && onFile.getMilitaryService() != null) { 
    		// Invoke a rule flow using the military service object that was accepted earlier
    		MilitaryServiceInputData militaryData = 
    			new MilitaryServiceInputData(onFile.getMilitaryService(), onFile,pristine,true);
    		militaryData.setUseCaseName(useCase);
    		militaryData.setMilitaryService(onFile.getMilitaryService());      
    		militaryData.setImpreciseDateDetected(info.isConvertedImpreciseData());
    		militaryData.setImprovedByMSDS(info.isSatisfiesUpgradeCriteria());

    		this.invokeRuleFlow(this.getRuleParameters( this.getProcessMSDSDataRuleParameters()),militaryData);
    		return militaryData.isImprovedByMSDS();
    	}
    	else
    		if (UseCaseName.MSDSPROC.equals(useCase) && info != null) {
    			this.applyCollisionRules(info);
    			
    			if (info.getEsrPerson()!= null && info.getVadirMilitaryService() == null) {
    				// This is test scenario... not real message
    				// Leave the incoming person intact
    			}
    			else {
    				// setup the person with VADIR data first. It may be null.
        			incoming.setMilitaryService(info.getVadirMilitaryService());
    			}
    			
    			if (info.getMsdsReceivedStatus() != null && MsdsResponseInfo.PERSON_NOT_FOUND.equals(info.getMsdsReceivedStatus())) {
    				incoming.setMilitaryService(new MilitaryService());
        			MilitaryServiceInputData terminateEarly = 
        				new MilitaryServiceInputData(incoming.getMilitaryService(), onFile,pristine,true);
        			terminateEarly.setUseCaseName(useCase);
        			terminateEarly.setMsdsReceiveStatus(info.getMsdsReceivedStatus());
        			this.invokeRuleFlow(this.getRuleParameters( this.getProcessMSDSDataRuleParameters()),terminateEarly);
        			return true;
    			}
       			if (this.checkIfNoData(info)){
    				incoming.setMilitaryService(new MilitaryService());
        			MilitaryServiceInputData terminateEarly = 
        				new MilitaryServiceInputData(incoming.getMilitaryService(), onFile,pristine,true);
        			terminateEarly.setUseCaseName(useCase);
        			terminateEarly.setMsdsReceiveStatus(info.getMsdsReceivedStatus());
        			terminateEarly.setNoMSDSdata(true);        			
        			this.invokeRuleFlow(this.getRuleParameters( this.getProcessMSDSDataRuleParameters()),terminateEarly);
        			return true;
    			}    			
    			
    			if (incoming.getMilitaryService() != null) {
        			MilitaryServiceInputData militaryDataVadir = 
        				new MilitaryServiceInputData(incoming.getMilitaryService(), onFile,pristine,true);
        			militaryDataVadir.setProcessQMSEflow(info.isProcessQMSEflow());
        			militaryDataVadir.setHasQmse(info.isHasQmse());        			
        			militaryDataVadir.setMsdsWorkItem(info.getMsdsWorkItem());
        			militaryDataVadir.setMsdsDataSource(UseCaseName.MSDSVADIR);
        			militaryDataVadir.setMsdsReceiveStatus(info.getMsdsReceivedStatus());
        			militaryDataVadir.setMsdsVadirStatus(info.getVadirError());        			
        			militaryDataVadir.setMsdsOefOifIndicator(info.getOefoifInd());        			
        			militaryDataVadir.setUseCaseName(useCase);
        			militaryDataVadir.setMilitaryService(incoming.getMilitaryService());        	        				
        			this.invokeRuleFlow(this.getRuleParameters( this.getProcessMSDSDataRuleParameters()),militaryDataVadir);
        			if (militaryDataVadir.isTerminateRuleFlow()) {
        				info.setMsdsWorkItem(militaryDataVadir.getMsdsWorkItem());
        				return militaryDataVadir.isTerminateRuleFlow();    				
        			}
        			if (militaryDataVadir.isImprovedByMSDS() || militaryDataVadir.hasQmse()) {
        				// Handle upgrades that are determined by source of data, etc.
        				info.setSatisfiesUpgradeCriteria(true);
        			}        			
        			if (!info.isHasQmse()) {
        				info.setHasQmse(militaryDataVadir.hasQmse());        			
        			}
        			// Flow Control: If we found a QMSE in first pass, don't neet to process QMSE flow
        			// again in subsequent passes with session person
        			if (info.isProcessQMSEflow() && info.isHasQmse()) {
            			info.setProcessQMSEflow(false);        				
        			}

        			info.setConvertedImpreciseData(militaryDataVadir.isImpreciseData());
    			}
    			if (info.getEsrPerson()!= null && info.getBirlsMilitaryService() == null) {
    				// This is test scenario... not real message
    				// Leave the incoming person intact
    			}
    			else {
    				// setup the person with BIRLS data first. It may be null.
        			incoming.setMilitaryService(info.getBirlsMilitaryService());
    			}    			

    			if (incoming.getMilitaryService() != null) {
        			incoming.setPurpleHeart(info.getPurpleHeart());
        			incoming.setMedalOfHonor(info.getMedalOfHonor());
        			MilitaryServiceInputData militaryDataBirls = 
        				new MilitaryServiceInputData(incoming.getMilitaryService(), onFile,pristine,true);
        			militaryDataBirls.setProcessQMSEflow(info.isProcessQMSEflow());
        			militaryDataBirls.setHasQmse(info.isHasQmse());        			
        			militaryDataBirls.setMsdsWorkItem(info.getMsdsWorkItem());
        			militaryDataBirls.setMsdsDataSource(UseCaseName.MSDSVBA);
        			militaryDataBirls.setMsdsReceiveStatus(info.getMsdsReceivedStatus());
        			militaryDataBirls.setMsdsBirlsStatus(info.getBirlsError());        			
        			militaryDataBirls.setUseCaseName(useCase);
        			militaryDataBirls.setMilitaryService(incoming.getMilitaryService());        	        				
        			this.invokeRuleFlow(this.getRuleParameters( this.getProcessMSDSDataRuleParameters()),militaryDataBirls);
        			info.setMsdsWorkItem(militaryDataBirls.getMsdsWorkItem());
        			if(militaryDataBirls.isImpreciseData()){
        				info.setConvertedImpreciseData(militaryDataBirls.isImpreciseData());
        			}
        			if (militaryDataBirls.isImprovedByMSDS() || militaryDataBirls.hasQmse() ) {
        				// Handle upgrades that are determined by source of data, etc. 
            			info.setSatisfiesUpgradeCriteria(true);
        			}
        			if (!info.isHasQmse()) {
        				info.setHasQmse(militaryDataBirls.hasQmse());        			
        			}
        			// Flow Control: If we found a QMSE in first pass, don't neet to process QMSE flow
        			// again in subsequent passes with session person
        			if (info.isProcessQMSEflow() && info.isHasQmse()) {
            			info.setProcessQMSEflow(false);        				
        			}

        			return militaryDataBirls.isTerminateRuleFlow();    				    				
    			}
    		}
     	return false; 
	}
	
	/**
	 * @see gov.va.med.esr.common.rule.service.impl.AbstractRuleValidationServiceAwareImpl#afterPropertiesSet()
	 */
	public void afterPropertiesSet() throws Exception {
		super.afterPropertiesSet();
		Validate.notNull( this.periodOfServiceRuleParameters, "periodOfServiceRuleParameters property is required");
		Validate.notNull( this.processMilitaryRuleParameters, "processMilitaryRuleParameters property is required");
	}
	
	/**
	 * @see gov.va.med.esr.common.rule.service.MilitaryRuleService#calculatePeriodOfService(gov.va.med.esr.common.model.person.Person, gov.va.med.esr.common.model.person.Person)
	 */
	public Person calculatePeriodOfServiceForUI(Person incoming, Person onFile,String useCaseName) throws ServiceException {
	    return calculatePeriodOfService(incoming,onFile,UI,useCaseName, null);
	}
    
    /**
     * @see gov.va.med.esr.common.rule.service.MilitaryRuleService#calculatePeriodOfService(gov.va.med.esr.common.model.person.Person, gov.va.med.esr.common.model.person.Person)
     */
    public Person calculatePeriodOfService(Person incoming, Person onFile,String useCaseName,VAFacility sendingFacility) throws ServiceException {
        return calculatePeriodOfService(incoming,onFile,null,useCaseName, sendingFacility);
    }
    
    private Person calculatePeriodOfService(Person incoming, Person onFile, String caller, String useCaseName, VAFacility sendingFacility) throws ServiceException {

        Validate.notNull( incoming, "An incoming veteran must not be null " );
        Validate.notNull( onFile, "A current veteran must not be null " );
        
        // Validate incoming veteran's military service episodes and conflicts
        MilitaryService militaryService = incoming.getMilitaryService();
         
        // Get a pristine person
        Person pristine = this.getPristinePerson(onFile);
        
        boolean isUpdateFromUI = StringUtils.equals(caller,UI) ? true : false;
    
        // Validate incoming veteran's HEC military service episodes and conflict experiences
        if(isUpdateFromUI) {
            this.doValidation(militaryService, onFile, pristine, UI);
        }
        
        // Invoke a rule flow to calculate period of service
        this.invokeRuleFlow(this.getRuleParameters(getPeriodOfServiceRuleParameters()),new MilitaryServiceInputData(militaryService,onFile,pristine,isUpdateFromUI,useCaseName, sendingFacility));
        
        return onFile;
    }
	
    /** Invokes the specific business rule flow according to whether an update is triggered from a GUI screen
     * or a messaging component.  All military service episodes and conflict experiences are validated first
     * before any update process takes place.
     * 
     * @param militaryInfo A military service to process
     * @param onFile The veteran on file
     * @param caller A string indicating who the caller is
     * @return A veteran with udpated military service info and special factor
     * @throws ServiceException Thrown in case of validation errors and un-expected errors
     */
    private Person doManageMilitaryService(MilitaryService militaryService, Person onFile, boolean isFromEE) throws ServiceException {
        
        Validate.notNull( onFile, "A current veteran must not be null " );
        
        if( militaryService != null ) { 

            // Get a pristine person
            Person pristine = this.getPristinePerson(onFile);
           
            // For UI and military service use caseneed to be validated.
            if(!isFromEE) {
                // Validate military episodes and conflicts
                this.doValidation( militaryService, onFile, pristine, UI );
            }

            MilitaryServiceInputData militaryData = new MilitaryServiceInputData(militaryService, onFile,pristine,true);
            
            // Invoke a rule flow to calculate period of service
            this.invokeRuleFlow(this.getRuleParameters( getProcessMilitaryRuleParameters()),militaryData);
        }
        else {
            // If incoming veteran doesn't any military info
            // remove military from a veteran on file
            onFile.setMilitaryService( militaryService );
        }
        return onFile;
    }
    
	/** Invokes the specific business rule flow according to whether an update is triggered from a GUI screen
	 * or a messaging component.  All military service episodes and conflict experiences are validated first
	 * before any update process takes place.
	 * 
	 * @param militaryInfo A military service to process
	 * @param onFile The veteran on file
	 * @param caller A string indicating who the caller is
	 * @return A veteran with udpated military service info and special factor
	 * @throws ServiceException Thrown in case of validation errors and un-expected errors
	 */
	private Person doProcessMilitaryService(VAFacility sendingFacility, String dfn, MilitaryService militaryInfo, Person onFile,String caller) throws ServiceException {
		
		Validate.notNull( onFile, "A current veteran must not be null " );
		
		if( militaryInfo != null ) { 

			// Get a pristine person
			Person pristine = this.getPristinePerson(onFile);
			 if( UI.equals(caller) ) {
//				 For UI and military service use caseneed to be validated.
		                       // Validate military episodes and conflicts
		                this.doValidation( militaryInfo, onFile, pristine, UI );
		          
			 }
            MilitaryServiceInputData militaryData = new MilitaryServiceInputData(militaryInfo, onFile,pristine,false);
            militaryData.setSendingFacility(sendingFacility);
            militaryData.setDfn(dfn);
            
			// Invoke a rule flow to calculate period of service
			this.invokeRuleFlow(this.getRuleParameters( getProcessMilitaryRuleParameters()),militaryData);
		}
		else {
			// If incoming veteran doesn't any military info
			// remove military from a veteran on file
			onFile.setMilitaryService( militaryInfo );
		}
		return onFile;
	}

	/**
	 * 
	 * @param ms
	 * @param onFile
	 * @param loadVSSCDataResult
	 * @return
	 * @throws ServiceException
	 */
	public Person doProcessVSSCData(MilitaryService ms, Person onFile, LoadVSSCDataResult loadVSSCDataResult) throws ServiceException {
		Validate.notNull( onFile, "A current veteran must not be null " );
		
		if( ms != null ) { 

			// Get a pristine person
			Person pristine = this.getPristinePerson(onFile);

            MilitaryServiceInputData militaryData = new MilitaryServiceInputData(ms, onFile,pristine,false);
            militaryData.setDataFromVSSC(true);
            militaryData.setLoadVSSCDataResult(loadVSSCDataResult);
            
			// Invoke a rule flow to calculate period of service
			this.invokeRuleFlow(this.getRuleParameters( getProcessMilitaryRuleParameters()),militaryData);
		}
		else {
			// If incoming veteran doesn't any military info
			// remove military from a veteran on file
			onFile.setMilitaryService( ms );
		}
		return onFile;
	}

    /*
     * @see gov.va.med.esr.common.rule.service.MilitaryRuleService#validateMilitaryService(gov.va.med.esr.common.model.ee.MilitaryServiceSiteRecord, gov.va.med.esr.common.model.person.Person, gov.va.med.esr.common.model.person.Person, java.lang.String)
     */
    public void validateMilitaryService(Person incoming, Person onFile) throws ServiceException {
        Validate.notNull( incoming, "A veteran incoming must not be null for validation");
        Validate.notNull( onFile, "A veteran on file must not be null for validation");
        
        //Get a pristine person
        Person pristine = this.getPristinePerson(onFile);
        
        RuleValidationService ruleService = this.getRuleValidationService();
        ValidationMessages messages = new ValidationMessages();
        
        // Validate military service
        MilitaryService militaryService = incoming.getMilitaryService();
        if(militaryService != null) {
            ValidationMessages errors = ruleService.validateMS(militaryService,onFile,pristine,true,UseCaseName.EE);
            
            if( errors != null && !errors.isEmpty() ) {
                  messages.add(errors); 
                  throw new ValidationServiceException( messages ); 
            }
        }
    }

    
    
	private void doValidation( MilitaryService militaryService, 
										Person onFile, 
										Person pristine,
										String caller ) throws ServiceException {
        Validate.notNull(militaryService, "Military Service for an incoming must not be null for validation.");
		Validate.notNull(onFile, "A veteran on file must not be null for validation.");
		
		RuleValidationService ruleService = this.getRuleValidationService();
		ValidationMessages messages = new ValidationMessages();
		
		// Validate military service
		ValidationMessages msErrors = ruleService.validateMS(militaryService,onFile,pristine,UI.equals( caller ),UseCaseName.MS);
		if( msErrors != null && !msErrors.isEmpty() ) {
		    messages.add(msErrors); 
        }

        
        // Validate OEF/OIF Combat episodes
        Set combatEpisodes = militaryService.getCombatEpisodes();
        if( combatEpisodes != null ) {
            for( Iterator i=combatEpisodes.iterator(); i.hasNext(); ) {
                
                CombatEpisode ce = (CombatEpisode)i.next();
                // Validate conflict experience 
                ValidationMessages errors = ruleService.validateCombatEpisode( ce, onFile, pristine, UI.equals( caller ) );
              
              if( errors != null && !errors.isEmpty() ) {
                  messages.add( errors,String.valueOf(ce.hashCode())); 
              }
            }
        }
        
        MilitaryServiceSiteRecord record = militaryService.getHECMilitaryServiceSiteRecord();
		// Validate episodes
		Set episodes = (record != null) ? record.getMilitaryServiceEpisodes() : null;
		if( episodes != null ) {
			for( Iterator i=episodes.iterator(); i.hasNext(); ) {
				
			    MilitaryServiceEpisode mse = (MilitaryServiceEpisode)i.next();
				// Validate conflict experience 
				ValidationMessages errors = ruleService.validateMSE( mse, onFile, pristine, UI.equals( caller ) );
		      
		      if( errors != null && !errors.isEmpty() ) {
		          messages.add( errors,String.valueOf(mse.hashCode()) ); 
		      }
			}
		}
		
		// Validate conflicts
		Set conflicts = (record != null) ? record.getConflictExperiences() : null;
		if( conflicts != null ) {
			for( Iterator i=conflicts.iterator(); i.hasNext(); ) {
				
			    ConflictExperience ce = (ConflictExperience)i.next();
				// Validate conflict experience 
				ValidationMessages errors = ruleService.validateConflictExperience( ce, onFile, pristine, UI.equals( caller ) );
		      
		      if( errors != null && !errors.isEmpty() ) {
		          messages.add( errors,String.valueOf(ce.hashCode())); 
		      }
			}
		}
	
      if( messages != null && !messages.isEmpty() ) {
         throw new ValidationServiceException( messages ); 
      }
	}
	
	/**
	 * @return Returns the periodOfServiceRuleParameters.
	 */
	public String getPeriodOfServiceRuleParameters() {
		return periodOfServiceRuleParameters;
	}
	/**
	 * @param periodOfServiceRuleParameters The periodOfServiceRuleParameters to set.
	 */
	public void setPeriodOfServiceRuleParameters(
         String periodOfServiceRuleParameters) {
		this.periodOfServiceRuleParameters = periodOfServiceRuleParameters;
	}
	/**
	 * @return Returns the processMilitaryRuleParameters.
	 */
	public String getProcessMilitaryRuleParameters() {
		return processMilitaryRuleParameters;
	}
	/**
	 * @param processMilitaryRuleParameters The processMilitaryRuleParameters to set.
	 */
	public void setProcessMilitaryRuleParameters(
         String processMilitaryRuleParameters) {
		this.processMilitaryRuleParameters = processMilitaryRuleParameters;
	}
	
	/**
	 * @return the processMSDSDataRuleParameters
	 */
	public String getProcessMSDSDataRuleParameters() {
		return processMSDSDataRuleParameters;
	}

	/**
	 * @param processMSDSDataRuleParameters the processMSDSDataRuleParameters to set
	 */
	public void setProcessMSDSDataRuleParameters(
			String processMSDSDataRuleParameters) {
		this.processMSDSDataRuleParameters = processMSDSDataRuleParameters;
	}	
	
	//MSDS Seeding Changes
	
	/** Invokes business rules to validate then to update military service info coming from
	 * a message, this is an extension for Seeding purposes.
	 * 
	 * @see gov.va.med.esr.common.rule.service.MilitaryRuleService#processMilitaryService(gov.va.med.esr.common.model.ee.MilitaryService, gov.va.med.esr.common.model.person.Person)
	 */
	public Person processSeedingMilitaryService(MilitaryService ms, Person onFile ) throws ServiceException {
	     return this.doProcessSeedingMilitaryService(ms, onFile );
	}
	
	/** Invokes the specific business rule flow according to whether an update is triggered from a GUI screen
     * or a messaging component.  All military service episodes and conflict experiences are validated first
     * before any update process takes place. Created for Seeding purposes
     * 
     * @param militaryInfo A military service to process
     * @param onFile The veteran on file
     * @param caller A string indicating who the caller is
     * @return A veteran with udpated military service info and special factor
     * @throws ServiceException Thrown in case of validation errors and un-expected errors
     */
    private Person doProcessSeedingMilitaryService(MilitaryService militaryInfo, Person onFile) throws ServiceException {
        
        Validate.notNull( onFile, "A current veteran must not be null " );
        
        if( militaryInfo != null ) { 

            // Get a pristine person
            Person pristine = this.getPristinePerson(onFile);

            MilitaryServiceInputData militaryData = new MilitaryServiceInputData(militaryInfo, onFile,pristine,false);            
            militaryData.setIsSeeding(true);
            
            // Invoke a rule flow to calculate period of service
            this.invokeRuleFlow(this.getRuleParameters( getProcessMilitaryRuleParameters()),militaryData);
        }        
        return onFile;
    }
    
    private void applyCollisionRules(MsdsResponseInfo info) {
    	if (info == null) return;

    	Set birls = new HashSet();
    	Set vadir = new HashSet();
    	Set vadirToRemove  = new HashSet();
    	Set birlsToRemove  = new HashSet();        

    	if (info.getBirlsMilitaryService() != null) {
    		MilitaryService ms =  info.getBirlsMilitaryService();
    		MilitaryServiceSiteRecord hec = ms.getHECMilitaryServiceSiteRecord();
    		if (hec != null) {
    			Set episodes = hec.getMilitaryServiceEpisodes();
    			for( Iterator ie=episodes.iterator(); ie.hasNext(); ) {
    				MilitaryServiceEpisode episode = (MilitaryServiceEpisode)ie.next();
    				birls.add(episode);	
    			}
    		}
    	}

    	if (info.getVadirMilitaryService() != null) {
    		MilitaryService ms =  info.getVadirMilitaryService();
    		MilitaryServiceSiteRecord hec = ms.getHECMilitaryServiceSiteRecord();
    		if (hec != null) {
    			Set episodes = hec.getMilitaryServiceEpisodes();
    			for( Iterator ie=episodes.iterator(); ie.hasNext(); ) {
    				MilitaryServiceEpisode episode = (MilitaryServiceEpisode)ie.next();
    				vadir.add(episode);	
    			}
    		}    		
    	}

    	if (!vadir.isEmpty() && !birls.isEmpty()) {
    		// Start with either one. If there is a match, then continue.
    		// If either set is empty, of course there are no collisions possible
    		// and we don't reach this point.
    		Set episodes = vadir;
    		for( Iterator ie=episodes.iterator(); ie.hasNext(); ) {
    			MilitaryServiceEpisode episode = (MilitaryServiceEpisode)ie.next();
    			Set birlEpisodes = birls;
    			// The rationale for checking collisions here is to simpify
    			// the ILOG flow. Matching records from both sources would be eliminated per this  
    			// criteria anyway.  And they should NOT match and be in same result collection,
    			// which appears to be a gap in requirements.
    			for( Iterator i=birlEpisodes.iterator(); i.hasNext(); ) {
    				MilitaryServiceEpisode birlsEpisode = (MilitaryServiceEpisode)i.next();
    				if (episode.getServiceBranch() != null && birlsEpisode.getServiceBranch() != null &&
    						episode.getStartDate() != null && birlsEpisode.getStartDate() != null) {

    					ServiceBranch vadirSb = episode.getServiceBranch();
    					ServiceBranch birlsSb = birlsEpisode.getServiceBranch();
    					ImpreciseDate vadirStart = episode.getStartDate();
    					ImpreciseDate birlsStart = birlsEpisode.getStartDate();			        	

    					if (vadirSb.getCode().equals(birlsSb.getCode()) &&
    							vadirStart.equals(birlsStart) ) {
    						DischargeType vadirChar = episode.getDischargeType();
    						DischargeType birlsChar = birlsEpisode.getDischargeType();
    						Date vadirEnd = ImpreciseDateUtils.getDateWithDefault(episode.getEndDate());
    						Date birlsEnd = ImpreciseDateUtils.getDateWithDefault(birlsEpisode.getEndDate());

    						if (vadirChar == null && birlsChar == null) {
    							// Handle blank case separately since blank is acceptable
    							if (DateUtils.isEqualOrAfter(vadirEnd, birlsEnd)) {
    								birlsToRemove.add(birlsEpisode);
    							}
    							else if (DateUtils.isEqualOrAfter(birlsEnd,vadirEnd)) {
    								vadirToRemove.add(episode);
    							}	
    						}
    						/*
    						 * rule c IF (ESR determines matching “service
    						 * episodes” from both VADIR & WEBHINQ) AND
    						 * (Character of Service from WEBHINQ IS Blank or
    						 * NULL) AND (Character of Service in VADIR IS NOT
    						 * Blank or NULL) 
    						 * THEN 
    						 * ESR will use data from VADIR
    						 * ENDIF
    						 *
    						 */
    						else if (birlsChar == null && vadirChar != null)  {
    							birlsToRemove.add(birlsEpisode);
    						}
    						/*
    						 * rule d IF (ESR determines matching “service
    						 * episodes” from both VADIR & WEBHINQ) AND
    						 * (Character of Service from VADIR IS Blank or
    						 * NULL) AND (Character of Service in WEBHINQ IS NOT
    						 * Blank or NULL) 
    						 * THEN 
    						 * ESR will use data from
    						 * WEBHINQ 
    						 * ENDIF
    						 *
    						 */
    						else if (birlsChar != null && vadirChar == null)  {
    							vadirToRemove.add(episode);
    						}
    						/*
    						 * rule a IF (ESR determines matching “service
    						 * episodes” from both VADIR & WEBHINQ) AND (RAD in
    						 * VADIR is AFTER OR EQUAL TO Ending Date of Service
    						 * from WEBHINQ) AND (Character of Service in VADIR
    						 * IS EQUAL TO Character of Service from WEBHINQ)
    						 * THEN 
    						 * ESR will use data from VADIR 
    						 * ENDIF
    						 * 
    						 */
    						else if (vadirChar.getCode().equals(birlsChar.getCode()) &&
    								DateUtils.isEqualOrAfter(vadirEnd, birlsEnd)) {
    							birlsToRemove.add(birlsEpisode);
    						}
    						/*
    						 * 
    						 * rule b IF (ESR determines matching “service
    						 * episodes” from both VADIR & WEBHINQ) AND (Ending
    						 * Date of Service from WEBHINQ is AFTER RAD in
    						 * VADIR) AND (Character of Service from WEBHINQ IS
    						 * EQUAL TO Character of Service in VADIR) 
    						 * THEN ESR
    						 * will use data from WEBHINQ 
    						 * ENDIF
    						 *
    						 */
    						else if (vadirChar.getCode().equals(birlsChar.getCode()) &&
    								DateUtils.isEqualOrAfter(birlsEnd,vadirEnd)) {
    							vadirToRemove.add(episode);
    						}
    						/*
							 * SUC1013.6.8.6 
							 * Upon determination of matching service episodes, from both 
							 * VADIR & WEBHINQ, for the veteran, the system (ESR) will generate an
							 * exception when ALL of the conditions are true, as follows: 
							 * Character of Service from WEBHINQ IS NOT Blank OR NULL. 
							 * Character of Service in VADIR IS NOT Blank OR NULL. 
							 * Character of Service from WEBHINQ IS NOT EQUAL TO Character of Service in
							 * VADIR. 
							 * SUC1013.6.8.7 
							 * Upon generation of an exception due to the matching service episodes,
							 * from both VADIR & WEBHINQ, for the veteran, the Character of Discharge in both systems 
							 * are not the same, the system (ESR) will create an E&E
							 * Work Item, as follows: Work Item: Discrepancy
							 * between VBA and DOD Service Information Work Item
							 * Matching service episodes were found in data from
							 * VADIR & WEBHINQ, however, Character of Discharge
							 * was not the same. Resolution is required.
							 * 
							 */
    						else if (!vadirChar.getCode().equals(birlsChar.getCode()) &&
    								DateUtils.isEqualOrAfter(birlsEnd,vadirEnd)) {
    							// Design decision: only set if more important work items already set
    							if (!MsdsResponseInfo.MULTIPLE_PERSON_FOUND.equals(info.getMsdsReceivedStatus()) &&
    									!MsdsResponseInfo.PERSON_NOT_FOUND.equals(info.getMsdsReceivedStatus())) {
    								info.setMsdsReceivedStatus(MsdsResponseInfo.CHARACTER_OF_SERVICE_NOT_SAME_IN_BOTH);
    							}
    						}    						
    					}
    				}
    			}    	
    		}

    		if (vadirToRemove.size() > 0) {
    			// Safe assumption there will be service records to remove
    			MilitaryService ms =  info.getVadirMilitaryService();
    			MilitaryServiceSiteRecord hec = ms.getHECMilitaryServiceSiteRecord();
    			if (hec != null) {
    				Set original = new HashSet(hec.getMilitaryServiceEpisodes());
    				Set toAddBack = new HashSet();
    				hec.removeAllMilitaryServiceEpisodes();
    				for( Iterator ie=original.iterator(); ie.hasNext(); ) {
    					MilitaryServiceEpisode episode = (MilitaryServiceEpisode)ie.next();
    					if (!vadirToRemove.contains(episode)) {
    						toAddBack.add(episode);
    					}
    				}
    				if (toAddBack.size() > 0) {
    					hec.addAllMilitaryServiceEpisodes(toAddBack);        		    	
    				}
    			}    		    			
    		}
    		if (birlsToRemove.size() > 0) {
    			// Safe assumption there will be service records to remove
    			MilitaryService ms =  info.getBirlsMilitaryService();
    			MilitaryServiceSiteRecord hec = ms.getHECMilitaryServiceSiteRecord();
    			if (hec != null) {
    				Set original = new HashSet(hec.getMilitaryServiceEpisodes());
    				Set toAddBack = new HashSet();
    				hec.removeAllMilitaryServiceEpisodes();
    				for( Iterator ie=original.iterator(); ie.hasNext(); ) {
    					MilitaryServiceEpisode episode = (MilitaryServiceEpisode)ie.next();
    					if (!birlsToRemove.contains(episode)) {
    						toAddBack.add(episode);
    					}
    				}
    				if (toAddBack.size() > 0) {
    					hec.addAllMilitaryServiceEpisodes(toAddBack);        		    	
    				}
    			}    		    			
    		}
    	} else {
    		// nothing to do since the other cases have no collisions
    	}

    }
    
    public Person processCreateHECRecord(MilitaryService ms, Person onFile ) throws ServiceException{
    	return this.doProcessCreateHECRecord(ms, onFile);		
    }

    private Person doProcessCreateHECRecord(MilitaryService militaryInfo, Person onFile) throws ServiceException {

    	Validate.notNull( onFile, "A current veteran must not be null " );

    	if( militaryInfo != null ) { 

    		// Get a pristine person
    		Person pristine = this.getPristinePerson(onFile);

    		MilitaryServiceInputData militaryData = new MilitaryServiceInputData(militaryInfo, onFile,pristine,false);            
    		militaryData.setIsSeeding(true);
    		militaryData.setTransferToHec(true);

    		// Invoke a rule flow to calculate period of service
    		this.invokeRuleFlow(this.getRuleParameters( getProcessMilitaryRuleParameters()),militaryData);
    	}        
    	return onFile;
    }
    
    
    private boolean checkIfNoData(MsdsResponseInfo info) {

    	if (info == null) return true;

    	if (info.getBirlsMilitaryService() != null) {
    		MilitaryService ms =  info.getBirlsMilitaryService();
    		if (ms != null) {
    			MilitaryServiceSiteRecord hec = ms.getHECMilitaryServiceSiteRecord();
    			if (hec != null) {
    				Set episodes = hec.getMilitaryServiceEpisodes();
    				if (episodes != null && episodes.size() > 0) return false;
    			}
    		}
    	}

    	if (info.getVadirMilitaryService() != null) {
    		MilitaryService ms =  info.getVadirMilitaryService();
    		if (ms != null) {
        		MilitaryServiceSiteRecord hec = ms.getHECMilitaryServiceSiteRecord();
        		if (hec != null) {
        			Set episodes = hec.getMilitaryServiceEpisodes();
        			if (episodes != null && episodes.size() > 0) return false;
        		}
        		Set hecActivations = ms.getActivations();
        		if (hecActivations != null && hecActivations.size() > 0)  return false;

        		Set hecCombatPays = ms.getCombatServices();
        		if (hecCombatPays != null && hecCombatPays.size() > 0)  return false;
    		}
    	}
    	if (info.getPurpleHeart() != null && info.getPurpleHeart().getPhIndicator() != null) return false;
    	if (info.getMedalOfHonor() != null && info.getMedalOfHonor().getMhIndicator() != null) return false;    		
    	if (info.getOefoifInd() != null) return false;

    	return true;
    }

	/**
	 * @see gov.va.med.esr.common.rule.service.MilitaryRuleService#processMilitaryServiceNumbers(gov.va.med.esr.common.model.person.Person)
	 */
	public Person processMilitaryServiceNumbers(Person onFile) throws ServiceException {
		
		MilitaryService currentMilitaryService = onFile.getMilitaryService();
		if (currentMilitaryService == null) {
			return onFile;
		}
		
		MilitaryServiceSiteRecord currentHECSiteRecord = currentMilitaryService.getHECMilitaryServiceSiteRecord();
		if (currentHECSiteRecord == null) {
			return onFile;
		}
		
		Set episodes = currentHECSiteRecord.getMilitaryServiceEpisodes();
		if (episodes == null) {
			return onFile;
		}
		
		for (Iterator i = episodes.iterator(); i.hasNext();) {
			MilitaryServiceEpisode hecMilitaryServiceEpisode = (MilitaryServiceEpisode) i.next();
			if (StringUtils.isNotEmpty(hecMilitaryServiceEpisode.getServiceNumber())) {
		        if (logger.isDebugEnabled()) {
		        	logger.debug("MilitaryRuleServiceImpl processMilitaryServiceNumbers : ServiceNumber: " + hecMilitaryServiceEpisode.getServiceNumber());
			    }
				continue;
			}
			//service number is not present in HEC episode so find it from associated site one
			MilitaryServiceEpisode matchSiteMilitaryServiceEpisode = findMatchingSiteEpisode(hecMilitaryServiceEpisode, onFile);
	        if (logger.isDebugEnabled()) {
	        	logger.debug("MilitaryRuleServiceImpl processMilitaryServiceNumbers : matchSiteMilitaryServiceEpisode: " + matchSiteMilitaryServiceEpisode);
		    }
			if (matchSiteMilitaryServiceEpisode != null && StringUtils.isNotBlank(matchSiteMilitaryServiceEpisode.getServiceNumber())) {
		        if (logger.isDebugEnabled()) {
		        	logger.debug("MilitaryRuleServiceImpl processMilitaryServiceNumbers : site ServiceNumber: " + matchSiteMilitaryServiceEpisode.getServiceNumber());
			    }				
				hecMilitaryServiceEpisode.setServiceNumber(matchSiteMilitaryServiceEpisode.getServiceNumber());
			}
		}		
		return onFile;
	}
	
	private MilitaryServiceEpisode findMatchingSiteEpisode(MilitaryServiceEpisode hecMilitaryServiceEpisode, Person onFile) {
		MilitaryService currentMilitaryService = onFile.getMilitaryService();
		if (currentMilitaryService == null) {
			return null;
		}
		Set siteRecords = currentMilitaryService.getMilitaryServiceSiteRecords();
		if (siteRecords == null) {
			return null;
		}

		for (Iterator i = siteRecords.iterator(); i.hasNext();) {
			MilitaryServiceSiteRecord siteRecord = (MilitaryServiceSiteRecord) i.next();
			
			if (siteRecord.getSite() != null && 
					siteRecord.getSite().getCode() != null && 
					siteRecord.getSite().getCode().equals(VAFacility.CODE_HEC.getCode())) {
		        if (logger.isDebugEnabled()) {
		        	logger.debug("MilitaryRuleServiceImpl findMatchingSiteEpisode : HEC site: " + siteRecord.getSite().getCode());
			    }				
				continue;
			}
	        if (logger.isDebugEnabled()) {
	        	logger.debug("MilitaryRuleServiceImpl findMatchingSiteEpisode : Not HEC site: " + ToStringBuilder.reflectionToString(siteRecord, ToStringStyle.MULTI_LINE_STYLE));
		    }
			Set episodes = siteRecord.getMilitaryServiceEpisodes();
			if (episodes == null) {
				return null;
			}
			
			for (Iterator j = episodes.iterator(); j.hasNext();) {
				MilitaryServiceEpisode siteMilitaryServiceEpisode = (MilitaryServiceEpisode) j.next();
				if (hecMilitaryServiceEpisode != null &&
						siteMilitaryServiceEpisode != null &&
						isMatchServiceBranch(hecMilitaryServiceEpisode.getServiceBranch(), siteMilitaryServiceEpisode.getServiceBranch()) &&
						isMatchDischargeType(hecMilitaryServiceEpisode.getDischargeType(), siteMilitaryServiceEpisode.getDischargeType()) &&
						isMatchMilitaryServiceComponent(hecMilitaryServiceEpisode.getMilitaryServiceComponent(), siteMilitaryServiceEpisode.getMilitaryServiceComponent()) &&
						ImpreciseDateUtils.impreciseDatesEqualWithoutTime(hecMilitaryServiceEpisode.getStartDate(), siteMilitaryServiceEpisode.getStartDate()) &&
						ImpreciseDateUtils.impreciseDatesEqualWithoutTime(hecMilitaryServiceEpisode.getEndDate(), siteMilitaryServiceEpisode.getEndDate())) {
					return siteMilitaryServiceEpisode;
				}
			}
		}		
		return null;
	}
	
	private boolean isMatchServiceBranch(ServiceBranch br1, ServiceBranch br2) {
		if (br1 == null && br2 == null) {
			return true;
		}
		if (br1 != null && br2 != null) {
			return br1.getCode().equals(br2.getCode());
		}
		return false;
	}
	private boolean isMatchDischargeType(DischargeType br1, DischargeType br2) {
		if (br1 == null && br2 == null) {
			return true;
		}
		if (br1 != null && br2 != null) {
			return br1.getCode().equals(br2.getCode());
		}
		return false;
	}
	private boolean isMatchMilitaryServiceComponent(MilitaryServiceComponent br1, MilitaryServiceComponent br2) {
		if (br1 == null && br2 == null) {
			return true;
		}
		if (br1 != null && br2 != null) {
			return br1.getCode().equals(br2.getCode());
		}
		return false;
	}	
}