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

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

// Library classes

// Framework classes
import gov.va.med.fw.rule.RuleDataAware;
import gov.va.med.fw.rule.RuleException;
import gov.va.med.fw.service.ServiceException;

// ESR classes
import gov.va.med.esr.common.infra.ImpreciseDateUtils;
import gov.va.med.esr.common.model.person.Person;
import gov.va.med.esr.common.model.person.SignatureImage;
import gov.va.med.esr.common.model.ee.MilitaryService;
import gov.va.med.esr.common.model.ee.MilitaryServiceEpisode;
import gov.va.med.esr.common.model.ee.MilitaryServiceSiteRecord;
import gov.va.med.esr.common.model.financials.IncomeTest;
import gov.va.med.esr.common.model.lookup.MessageType;
import gov.va.med.esr.common.model.lookup.SignatureIndicator;
import gov.va.med.esr.common.rule.PersonInput;
import gov.va.med.esr.common.rule.data.MilitaryServiceInputData;
import gov.va.med.esr.common.rule.data.PersonInputData;
import gov.va.med.esr.common.rule.data.PersonSignatureInputData;
import gov.va.med.esr.common.util.MilitaryServiceEpisodeComparator;
import gov.va.med.esr.common.model.lookup.VAFacility;

/**
 * @author Carlos Ruiz
 * @version 1.0
 */
public class PersonInputParameter extends BaseParameter implements PersonInput {

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

	/**
	 * Default constructor
	 */
	public PersonInputParameter() {
		super();
	}

    
	/**
	 * @see gov.va.med.esr.common.rule.PersonInput#getClaimFolderLocation()
	 */
	public String getClaimFolderLocation() {
		return this.getClaimFolderLocation(this.getIncomingPerson());
	}

	/**
	 * @see gov.va.med.esr.common.rule.PersonInput#getClaimFolderNumber()
	 */
	public String getClaimFolderNumber() {
		return this.getClaimFolderNumber(this.getIncomingPerson());
	}

	/**
	 * @see gov.va.med.esr.common.rule.PersonInput#isVeteran()
	 */
	public boolean isVeteran() {
		return this.isVeteran( this.getIncomingPerson() );
	}
	
	/**
	 * @see gov.va.med.esr.common.rule.PersonInput#setClaimFolderLocation(java.lang.String)
	 */
	public void setClaimFolderLocation(String code) {
		Person result = this.getResultPerson();
		if( result != null ) {
			try {
				result.setClaimFolderLocation( this.getLookupService().getVaFacilityByCode( code ) );
			}
			catch( Exception e ) {
				if( this.logger.isWarnEnabled() ) {
					logger.warn( "Failed to set a va facility location for a code " + code );
				}
			}
		}
	}

	/**
	 * @see gov.va.med.esr.common.rule.PersonInput#setClaimFolderNumber(java.lang.Integer)
	 */
	public void setClaimFolderNumber(String number) {
		Person result = this.getResultPerson();
		if( result != null ) {
			result.setClaimFolderNumber( number );
		}
	}

	/**
	 * @see gov.va.med.esr.common.rule.parameter.PersonInput#setVeteranIndicator(java.lang.Boolean)
	 */
	public void setVeteranIndicator(Boolean setting) {
		Person result = this.getResultPerson();
		if (result != null) {
			result.setVeteran(setting);
		}
	}

	/**
	 * This method looks into the enrollment determination record of a veteran on
	 * file to see if a veteran has ever been enrolled into ESR. If this is not a
	 * veteran, the eligibility records are used to determine if this is the fist
	 * time ESR processes this person.
	 * 
	 * @see gov.va.med.esr.common.rule.parameter.PersonInput#isExistingPerson()
	 */
	public boolean isExistingPerson() {

		boolean enrolled = false;
		Person pristine = this.getPristinePerson();
		if (pristine != null) {
			// Pristine should never be null
			enrolled = (pristine.getEnrollmentDetermination() != null);
		}
		return enrolled;
	}

	/**
	 * @see gov.va.med.esr.common.rule.PersonInput#getUserEnrolleeValidThrough()
	 */
	public Integer getUserEnrolleeValidThrough() {
		Person incoming = this.getIncomingPerson();
		return incoming != null ? incoming.getUserEnrolleeValidThrough() : null;
	}

	/**
	 * @see gov.va.med.esr.common.rule.PersonInput#getSensitiveRecordIndicator()
	 */
	public Boolean getSensitiveRecordIndicator() {
		return this.getSensitiveRecordIndicator( this.getIncomingPerson() );
	}
    
	/**
	 * @see gov.va.med.esr.common.rule.PersonInput#setSensitiveRecordIndicator(java.lang.Boolean)
	 */
	public void setSensitiveRecordIndicator(Boolean flag) {
		Person result = this.getResultPerson();
		if( result != null ) {
			result.setSensitiveRecord( flag );
		}
	}
    
   public void setSensitiveChangeSource( String source ) throws RuleException {
      Person result = this.getResultPerson();
      if( result != null ) {
         try {
            result.setSensitivityChangeSource( getLookupService().getSensitivityChangeSourceByCode( source ) );
         }
         catch( ServiceException e ) {
            throw new RuleException( "Failed to set a sensitivity change source ", e );
         }
      }
   }
   public void setSensitiveChangeDate( Date date ) {
      
      Person result = this.getResultPerson();
      if( result != null ) {
         result.setSensitivityChangeDate( date );
      }
   }
   public void setSensitiveChangeSite( VAFacility site ) {
      Person result = this.getResultPerson();
      if( result != null ) {
         result.setSensitivityChangeSite( site );
      }
   }
   
   public Date getSensitiveChangeDate() {
      return this.getResultPerson().getSensitivityChangeDate();
   }
   
   public VAFacility getSensitiveChangeSite() {
      return this.getResultPerson().getSensitivityChangeSite();
   }
   
    /**
     * @see gov.va.med.esr.common.rule.PersonInput#getPristineSignatureIndicator()
     */
    public String getPristineSignatureIndicator() {
        SignatureImage pristine = this.getMatchingSignatureImage(this.getIncomingSignatureImage(), this.getPristinePerson());
        String result = (pristine != null && pristine.getSignatureIndicator() != null) ?
                pristine.getSignatureIndicator().getCode() : SignatureIndicator.CODE_NO.getName();
                
        if (logger.isDebugEnabled()) {
           if (result != null)
              logger.debug("Pristine signature indicator ="+result);
           else
             logger.debug("Pristine signature indicator is null");                
        }    
                         
        return result;
    }
    
    /**
     * @see gov.va.med.esr.common.rule.PersonInput#getPristineSignatureList()
     */
    public ArrayList getPristineSignatureList() {
        Set signatures = this.getPristinePerson().getSignatureImages();
        return new ArrayList(signatures);
    }
    
    /**
     * @see gov.va.med.esr.common.rule.PersonInput#getSignatureIndicator()
     */
    public String getSignatureIndicator() {
        SignatureImage si = this.getIncomingSignatureImage();
        String result = (si != null && si.getSignatureIndicator() != null) ?
                si.getSignatureIndicator().getCode() : SignatureIndicator.CODE_NO.getName();
        if (logger.isDebugEnabled()) {
            if (result != null)
               logger.debug("Incoming Signature indicator: "+result);
            else
              logger.debug("Incoming Signature indicator is null");                
         }            
        return result;
    }
    
    public IncomeTest getPrimaryIncomeTestForSignatureImageIncomeYear() {
    	SignatureImage si = this.getIncomingSignatureImage();
    	IncomeTest forYear = this.getPristinePerson().getIncomeTest(si.getIncomeYear());
    	IncomeTest primaryForYear = null; 
    	if(forYear != null && Boolean.TRUE.equals(forYear.isPrimaryIncomeTest()))
    			primaryForYear = forYear;
    	return primaryForYear;
    }
    
    /**
     * @see gov.va.med.esr.common.rule.PersonInput#hasPrimaryIncomeTest()
     */
    public boolean hasPrimaryIncomeTest() {
        return this.getPrimaryTest() != null;
    }
    
    /**
     * @see gov.va.med.esr.common.rule.PersonInput#hasSignatureIndicatorOfNo()
     */
    public boolean hasSignatureIndicatorOfNo() {
        return hasSignatureIndicator(SignatureIndicator.CODE_NO.getName());
    }
    
    /**
     * @see gov.va.med.esr.common.rule.PersonInput#hasSignatureIndicatorOfYes()
     */
    public boolean hasSignatureIndicatorOfYes() {
        return hasSignatureIndicator(SignatureIndicator.CODE_YES.getName());
    }

    private boolean hasSignatureIndicator(String code) {
        Set images = this.getIncomingPerson().getSignatureImages();
        if (images != null && images.size() > 0) {
            for(Iterator iter = images.iterator(); iter.hasNext();) {
                SignatureImage si = (SignatureImage)iter.next();
                if (si.getSignatureIndicator() != null &&
                        si.getSignatureIndicator().getCode().equals(code)) {
                    return true;
                }
            }
        }
        return false;
    }
    
    /**
     * This method returns count of signature images of signature indicator value of yes.
     */
    public int getPristineSignatureIndicatorCountOfYes()
    {
    	return getPristineSignatureIndicatorCount(SignatureIndicator.CODE_YES.getName());
    }
    
    /**
     * This method returns count of signature images of specific signature indicator value.
     * @param code
     * @return
     */
    private int getPristineSignatureIndicatorCount(String code) {
    	int count=0;
        Set images = this.getPristinePerson().getSignatureImages();
        if (images != null && images.size() > 0) {        	
            for(Iterator iter = images.iterator(); iter.hasNext();) {
                SignatureImage si = (SignatureImage)iter.next();
                if (si.getSignatureIndicator() != null &&
                        si.getSignatureIndicator().getCode().equals(code)) {
                    count++;
                }
            }
        }
        return count;
    }

    /**
     * @see gov.va.med.esr.common.rule.PersonInput#updateSignatureInformation()
     */
    public void updateSignatureInformation() throws RuleException {
        try {
            this.getMergeRuleService().mergeSignatureImage(this.getIncomingSignatureImage(), this.getResultPerson());            
        }
        catch (ServiceException e) {
            throw new RuleException("Failed to merge prisoner of war record", e);
        }               
    }    
      
    /**
     * @see gov.va.med.esr.common.rule.PersonInput#getPriorUserEnrolleeValidThrough()
     */
    public Integer getPriorUserEnrolleeValidThrough() {
        Person pristine = this.getPristinePerson();
        return pristine != null ? pristine.getUserEnrolleeValidThrough() : null;
    }
    
    public Boolean getSensitiveRecordIndicator( Person person ) {
   	 return person != null ? person.getSensitiveRecord() : Boolean.FALSE;
    }
    
	/** Returns a facility code of a specific peson 
	 * @param person A person to query a facility's code
	 * @return A claim folder facility code
	 */
	public String getClaimFolderLocation(Person person) {
		VAFacility facility = person != null ? person.getClaimFolderLocation() : null;
		return facility != null ? facility.getCode() : null;
	}

	/** Returns a claim folder number of a specific person
	 * @param person A person to query claim folder numer
	 * @return A claim folder number
	 */
	public String getClaimFolderNumber(Person person) {
		return person != null ? person.getClaimFolderNumber() : null;
	}

	/** Determines if a person is a veteran
	 * 
	 * @param person A person to query for a flag
	 * @return A flag indicating a person is a veteran
	 */
	public boolean isVeteran( Person person ) {
		Boolean isVeteran = person != null ? person.isVeteran() : null; 
		return isVeteran != null ? isVeteran.booleanValue() : false;
	}
	
    public void setAppointmentRequestDate(Date requestDate) {
		Person result = this.getResultPerson();
		if (result != null) {
			result.setAppointmentRequestDate(requestDate);
		}
	}

	public void setAppointmentRequestResponse(Boolean response) {
		Person result = this.getResultPerson();		
		if (result != null) {
			result.setAppointmentRequestResponse(response);
		}
	}

	


	private PersonSignatureInputData getPersonSignatureInputData() {
        RuleDataAware ruleDataAware = this.getRuleDataAware();
        if (ruleDataAware instanceof PersonSignatureInputData) {
            return (PersonSignatureInputData)ruleDataAware;
        }
        return null;
    }
    
    private SignatureImage getIncomingSignatureImage() {
        return this.getPersonSignatureInputData() != null ?
                this.getPersonSignatureInputData().getSignatureImage() : null;
    }
    
    private SignatureImage getMatchingSignatureImage(SignatureImage signature, Person person) {
        SignatureImage match = null;
        if (signature != null && person != null) {
            match = (SignatureImage)this.getMergeRuleService().getMatchRuleService().findMatchingElement(signature, person.getSignatureImages());
        }
        return match;
    }

    
	//The following are enhancements to ESR 3.1 VOA, please see 
    //SUC_[676] Send Update Message (Z11)
    
	/** Determines if a person has VOA Only
	 * 
	 * @param person A person to query for a flag
	 * @return A flag indicating a person is a veteran
	 */
	//CCR7449
	public boolean isVOAOnly()  {
		
		return getPristinePerson().isVOAOnly();
	}
	
    /**
     * CCR7423: BR43.2: Definition - ESR VistA Veteran Record: An ESR Enrollment
     * Record is defined as a ESR VistA Veteran Record if at any time the record
     * was updated from an unsolicited Z07 coming from any VistA
     * system. ESR VistA Veteran Record and Veteran's Online Application Only
     * Record are mutually exclusive statuses.
     * 
     * R43.1: Definition - ESR Veteran's Online Application Only Record: This is
     * an ESR record that was created from a Veteran's Online Application
     * submission and since that time no unsolicited Z07 has ever
     * been received from a VistA system i.e. Medical Center or any other VA
     * facility that would use VistA to communicate with ESR. Veteran's Online
     * Application Only Record and ESR VistA Veteran Record are mutually
     * exclusive statuses.

     */
    public void updateVOAIndicator() 
    {
        String messageType = null;
        
        // if message is ORU-Z07
        if (getAacInfo()!=null && (messageType=getAacInfo().getMessageType())!=null) {
            if (messageType.equals(MessageType.CODE_ORUZ07_TO_ESR.getCode())) {
                if (isMessageFromVista()) {
                    getResultPerson().setVOAIndicator(null);
                }
                else if (isMessageFromVOA() && getPristinePerson().getVOAIndicator() == null) {
                    getResultPerson().setVOAIndicator(null);
                }
            }
        }
    }
    
    //  CR5987 CCR10353 Dental Indicator
    public Boolean getDentalIndicator(){
    	Person person = this.getIncomingPerson();
    	return person != null ? person.getEligibleForClassIIDental() : Boolean.FALSE;
	}
    public void setDentalIndicator(Boolean dentalIndicator)
    {
    	Person result = this.getResultPerson();
		if( result != null ) {
			result.setEligibleForClassIIDental(dentalIndicator);
		}
	}
    public void deleteDentalApplicationDueBeforeDate(){
    	Person result = this.getResultPerson();
		if( result != null ) {
			result.setClassIIDentalApplicationDueBefore(null);
		}
    }
    public void calculateDentalApplicationDueBeforeDate(){
    	Date mostRecentServiceSD = getMostRecentReceivedSSD();
    	Person result = this.getResultPerson();
    	if (mostRecentServiceSD !=null )
    		mostRecentServiceSD = getDatePlus180Days(mostRecentServiceSD);
    	if (result !=null )
    		result.setClassIIDentalApplicationDueBefore(mostRecentServiceSD);
    }
    
//  handbookTrigger	
	public boolean isHandBookTrigger(){
    	return this.getPersonInputData().getHandBookTrigger();    	
    }
    private  Date getDatePlus180Days(Date date) {
        if( date != null ) {
            Calendar calendar = Calendar.getInstance();
            calendar.clear();
            calendar.setTime( date );
            // Correct mistake CCR10408
            // calendar.add(Calendar.YEAR,2);
            calendar.add(Calendar.DATE,+180);
            return calendar.getTime();
        }
	  return null;
    }
    
    public Date getMostRecentReceivedSSD() {
		Date mostRecentSSD = null;
	//	if(  this.mostRecentSSD == null ) {
			// need to loop thru all (updated)MSEs and return the Service Separation date which is most recent
			MilitaryService ms = this.getResultPerson().getMilitaryService();
			if( ms != null ) {

				// A temp collection to hold all MSEs
				HashSet episodes = new HashSet();

				// Get all site records
				Set records = ms.getMilitaryServiceSiteRecords();

				// Loop through all site records to put all MSEs in a temp collection
				// to search for the most recent MSE by service separation date.
				Iterator i = records != null ? records.iterator() : null;
				while( i != null && i.hasNext() ) {
					MilitaryServiceSiteRecord site_record = (MilitaryServiceSiteRecord)i.next();
					Set site_episodes = (site_record != null) ? site_record.getMilitaryServiceEpisodes() : null;
					if(site_episodes != null && !site_episodes.isEmpty()) {
						episodes.addAll( site_episodes );
					}
				}
				
				// Now use a collection comparator to search for an MSE
				// with the most recent service separation date 
				MilitaryServiceEpisodeComparator comparator = new MilitaryServiceEpisodeComparator();
				if( !episodes.isEmpty() ) {
					MilitaryServiceEpisode episode = (MilitaryServiceEpisode)Collections.max( episodes, comparator );
					mostRecentSSD = (episode != null) ? ImpreciseDateUtils.getDateWithDefault( episode.getEndDate() ) : null;
				}
			}
		return mostRecentSSD;
	}
    /**
	 * Use this when doing validations where you need to access objects in
	 * piece-meal fashion or to access special input parameters.
	 * 
	 * @return MilitaryServiceInputData
	 */
	private PersonInputData getPersonInputData() {
		RuleDataAware ruleDataAware = this.getRuleDataAware();
		if (ruleDataAware instanceof PersonInputData) {
			return (PersonInputData) ruleDataAware;
		}
		return null;
	}
	
	   public void setVoaFormProcessPendingTrue()
	   {
		   this.getIncomingPerson().setVoaFormProcessPendingStatusTrue();
	   }
}