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

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

import gov.va.med.esr.common.infra.ImpreciseDate;
import gov.va.med.esr.common.infra.ImpreciseDateUtils;
import gov.va.med.esr.common.model.CommonEntityKeyFactory;
import gov.va.med.esr.common.model.comms.CommsLogEntry;
import gov.va.med.esr.common.model.ee.VerificationInfo;
import gov.va.med.esr.common.model.lookup.AddressChangeSource;
import gov.va.med.esr.common.model.lookup.AddressType;
import gov.va.med.esr.common.model.lookup.BadAddressReason;
import gov.va.med.esr.common.model.lookup.Country;
import gov.va.med.esr.common.model.lookup.State;
import gov.va.med.esr.common.model.lookup.VAFacility;
import gov.va.med.esr.common.model.party.Address;
import gov.va.med.esr.common.model.person.Association;
import gov.va.med.esr.common.model.person.Person;
import gov.va.med.esr.common.rule.ContactInformationInput;
import gov.va.med.esr.common.rule.data.AddressInputData;
import gov.va.med.esr.common.rule.data.CommsInputData;
import gov.va.med.esr.common.rule.data.ExternalSystemsInputData;
import gov.va.med.esr.common.rule.data.accessor.DemographicAccessor;
import gov.va.med.esr.common.rule.data.accessor.PropertyAccessor;
import gov.va.med.esr.service.external.demographics.DemographicsChangeEvent;
import gov.va.med.esr.service.external.demographics.DemographicsChangeType;
import gov.va.med.fw.model.AbstractKeyedEntity;
import gov.va.med.fw.model.EntityKey;
import gov.va.med.fw.model.EntityKeyFactory;
import gov.va.med.fw.rule.RuleDataAware;
import gov.va.med.fw.rule.RuleException;
import gov.va.med.fw.service.ServiceException;
import gov.va.med.fw.util.StringUtils;


/**
 * This class is used by Process Address and Validate Address to provide
 * access to BOM objects dealing with contact information.
 * 
 * @author Carlos Ruiz (refactored Van's code)
 * @version 1.0
 */
public class ContactInformationInputParameter extends BaseParameter
        implements ContactInformationInput {

    private static final long serialVersionUID = 6875166494032709168L;

    private Set updatedVeteranAddresses = null;

    private Address incomingAddress = null;

    private Address pristineAddress = null;

    private Address resultAddress = null;

    private AddressInputData addressInputData = null;
    
    /**
     * Default constructor
     */
    public ContactInformationInputParameter() {
        super();
    }

    public EntityKey getEntityKey() 
    {
        // Handle for external systems context first
        ExternalSystemsInputData esi = this.getExternalSystemsInputData();
        if (esi != null) {
            // This is for external systems context
            DemographicsChangeEvent demographicsChangeEvent = esi.getDemographicsChangeEvent(); 
            gov.va.med.esr.service.external.person.EntityKey key = (demographicsChangeEvent != null) ?  demographicsChangeEvent.getEntityKey() : null;
            if (key != null && demographicsChangeEvent != null && demographicsChangeEvent.getChangeType() != null) {
                if (demographicsChangeEvent.getChangeType().equals(DemographicsChangeType.ADDRESS_CHANGE)) {
                    return CommonEntityKeyFactory.createAddressEntityKey(key.getKey());                
                }
                if (demographicsChangeEvent.getChangeType().equals(DemographicsChangeType.EMAIL_CHANGE)) {
                    return CommonEntityKeyFactory.createEmailEntityKey(key.getKey());                
                }
                if (demographicsChangeEvent.getChangeType().equals(DemographicsChangeType.PHONE_CHANGE)) {
                    return CommonEntityKeyFactory.createPhoneEntityKey(key.getKey());                
                }                          
            }
        }
        // Check if comms context
        if (this.getCommsInputData() != null) {
            Address address = this.getCommsInputData().getMailingAddress();
            return address != null ? address.getEntityKey() : null;
        }
        
        // If the contact information is for Association then return EntityKey of Association
        if(this.getAssociation() != null)
        {
            return this.getAssociation() != null ? this.getAssociation().getEntityKey() : null;
        }
        EntityKey entityKey = super.getEntityKey();
        
        if(entityKey == null)
        {
        	if (this.getPristineAddress() != null) {
        		entityKey = this.getPristineAddress().getEntityKey();
        	} else if (this.getIncomingAddress() != null) {
        		entityKey = EntityKeyFactory.createEntityKey(123, 1, Address.class);
        	}
        }
        return entityKey;
    }
    
    /**
     * @see gov.va.med.esr.common.rule.parameter.ContactInformationInput#isAddressTypeExisted()
     */
    public boolean isAddressTypeExisted() {
        boolean found = false;
        Set addresses = this.getUpdatedVeteranAddresses();
        for (Iterator iter = addresses.iterator(); iter.hasNext();) {
            Address addressFromDb = (Address) iter.next();
            // CCR10307 -- prevent null pointer exception caused by address type being null
            if (addressFromDb.getType() != null) {
	            String addressType = addressFromDb.getType().getCode();
	            if (this.getAddressType(this.getIncomingAddress()).equals(
	                    addressType)) {
	                found = true;
	                break;
	            }
	            }
        }
        return found;
    }

    /**
     * @see gov.va.med.esr.common.rule.parameter.ContactInformationInput#isAddressDeleted()
     */
    public boolean isAddressDeleted() {
        Address incoming = this.getIncomingAddress();
        if (incoming != null && incoming.getType().getCode().equals(AddressType.CODE_PERMANENT_ADDRESS.getName()) &&
                incomingAddress.getEndDate() != null) {
            return true;
        }
        return false;
    }

    /**
     * 
     * @see gov.va.med.esr.common.rule.parameter.ContactInformationInput#getPerson()
     */
    public Person getPerson() {
        return this.getIncomingPerson();
    }
    
    /**
     * @see gov.va.med.esr.common.rule.parameter.ContactInformationInput#getAddressStartDate()
     */
    public Date getAddressStartDate(){
        return this.getIncomingAddress() != null ? getPreciseDate(this.getIncomingAddress().getStartDate()) : null;
    }
    
    /**
     * @see gov.va.med.esr.common.rule.parameter.ContactInformationInput#getAddressEndDate()
     */
    public Date getAddressEndDate(){
        return this.getIncomingAddress() != null ? getPreciseDate(this.getIncomingAddress().getEndDate()) : null;
    }
    
    /**
     * @see gov.va.med.esr.common.rule.parameter.ContactInformationInput#getPrsitineAddressStartDate()
     */
    public Date getPristineAddressStartDate(){
    	return this.getPristineAddress() != null ? getPreciseDate(this.getPristineAddress().getStartDate()) : null;
    }
    
    /**
     * @see gov.va.med.esr.common.rule.parameter.ContactInformationInput#getPrsitineAddressEndDate()
     */
    public Date getPristineAddressEndDate(){
        return this.getPristineAddress() != null ? getPreciseDate(this.getPristineAddress().getEndDate()) : null;
    }

    /**
     * @see gov.va.med.esr.common.rule.parameter.ContactInformationInput#setAddressEnd(java.util.Date)
     */
    public void setAddressEndDate(Date d){
        this.getResultAddress().setEndDate(this.getImpreciseDate(d));
    }
    
    /**
     * @see gov.va.med.esr.common.rule.parameter.ContactInformationInput#getAssociation()
     */
    public Association getAssociation() {
        return this.getIncomingAddress() != null ? this.getIncomingAddress().getAssociation() : null;
    }
    
    /**
     * @see gov.va.med.esr.common.rule.parameter.ContactInformationInput#getAssociationType()
     */
    public String getAssociationType() {
        return this.getAssociation() != null ? this.getAssociation().getType().getCode() : null;
    }

    /**
     * @see gov.va.med.esr.common.rule.parameter.ContactInformationInput#getAddressLineOne()
     */
    public String getAddressLineOne() 
    {
        Address address = this.getIncomingAddress();
        return (address != null && StringUtils.isNotEmpty(address.getLine1())) ? address.getLine1() : null;
    }

    /**
     * @see gov.va.med.esr.common.rule.parameter.ContactInformationInput#getPristineAddressLineOne()
     */
    public String getPristineAddressLineOne() 
    {
        Address address = this.getPristineAddress();
        return (address != null && StringUtils.isNotEmpty(address.getLine1())) ? address.getLine1() : null;
    }

    /**
     * @see gov.va.med.esr.common.rule.parameter.ContactInformationInput#getAddressLineTwo()
     */
    public String getAddressLineTwo() 
    {
        Address address = this.getIncomingAddress();
        return (address != null && StringUtils.isNotEmpty(address.getLine2())) ? address.getLine2() : null;
    }

    /**
     * @see gov.va.med.esr.common.rule.parameter.ContactInformationInput#getPristineAddressLineTwo()
     */
    public String getPristineAddressLineTwo() 
    {
        Address address = this.getPristineAddress();
        return (address != null && StringUtils.isNotEmpty(address.getLine2())) ? address.getLine2() : null;
    }

    /**
     * @see gov.va.med.esr.common.rule.parameter.ContactInformationInput#getAddressLineThree()
     */
    public String getAddressLineThree() 
    {
        Address address = this.getIncomingAddress();
        return (address != null && StringUtils.isNotEmpty(address.getLine3())) ? address.getLine3() : null;
    }

    /**
     * @see gov.va.med.esr.common.rule.parameter.ContactInformationInput#getPristineAddressLineThree()
     */
    public String getPristineAddressLineThree() 
    {
        Address address = this.getPristineAddress();
        
        return (address != null && StringUtils.isNotEmpty(address.getLine3())) ? address.getLine3() : null;
    }

    /**
     * @see gov.va.med.esr.common.rule.parameter.ContactInformationInput#getCity()
     */
    public String getCity() 
    {
        Address address = this.getIncomingAddress();
        return (address != null && StringUtils.isNotEmpty(address.getCity())) ? address.getCity() : null;
    }

    /**
     * @see gov.va.med.esr.common.rule.parameter.ContactInformationInput#getPristineCity()
     */
    public String getPristineCity() 
    {
        Address address = this.getPristineAddress();
        return (address != null && StringUtils.isNotEmpty(address.getCity())) ? address.getCity() : null;
    }

    /**
     * @see gov.va.med.esr.common.rule.parameter.ContactInformationInput#getCounty()
     */
    public String getCounty() 
    {
        Address address = this.getIncomingAddress();
        return (address != null && StringUtils.isNotEmpty(address.getCounty())) ? address.getCounty() : null;
    }

    /**
     * @see gov.va.med.esr.common.rule.parameter.ContactInformationInput#getPristineCounty()
     */
    public String getPristineCounty() 
    {
        Address address = this.getPristineAddress();
        return (address != null && StringUtils.isNotEmpty(address.getCounty())) ? address.getCounty() : null;
    }

    /**
     * @see gov.va.med.esr.common.rule.parameter.ContactInformationInput#getPristineAddressStateCode()
     */
    public String getPristineAddressStateCode() 
    {
        Address address = this.getPristineAddress();
        return (address != null && StringUtils.isNotEmpty(address.getState())) ? address.getState() : null;
    }

    /**
     * @see gov.va.med.esr.common.rule.parameter.ContactInformationInput#getProvince()
     */
    public String getProvince() 
    {
        Address address = this.getIncomingAddress();
        return (address != null && StringUtils.isNotEmpty(address.getProvince())) ? address.getProvince() : null;
    }

    /**
     * @see gov.va.med.esr.common.rule.parameter.ContactInformationInput#getPristineProvince()
     */
    public String getPristineProvince() 
    {
        Address address = this.getPristineAddress();
        return (address != null && StringUtils.isNotEmpty(address.getProvince())) ? address.getProvince() : null;
    }

    /**
     * @see gov.va.med.esr.common.rule.parameter.ContactInformationInput#getCountry()
     */
    public String getCountry() 
    {
        Address address = this.getIncomingAddress();
        return (address != null && StringUtils.isNotEmpty(address.getCountry())) ? address.getCountry() : null;
    }

    /**
     * @see gov.va.med.esr.common.rule.parameter.ContactInformationInput#getPristineCountry()
     */
    public String getPristineCountry() 
    {
        Address address = this.getPristineAddress();
        return (address != null && StringUtils.isNotEmpty(address.getCountry())) ? address.getCountry() : null;
    }

    /**
     * @see gov.va.med.esr.common.rule.parameter.ContactInformationInput#getBadAddressReasonText()
     */
    public BadAddressReason getBadAddressReasonText() 
    {
         return this.getIncomingAddress() != null ? this.getIncomingAddress().getBadAddressReason() : null;
    }

    /**
     * @see gov.va.med.esr.common.rule.parameter.ContactInformationInput#getPristineBadAddressReasonText()
     */
    public BadAddressReason getPristineBadAddressReasonText() {
        return this.getPristineAddress() != null ? this.getPristineAddress().getBadAddressReason() : null;
    }
    
    public boolean  isBadAddressReasonTextRemoved() 
    {
    	BadAddressReason incoming=getBadAddressReasonText();
    	BadAddressReason pristine =getPristineBadAddressReasonText();
    	if(incoming ==null && pristine !=null){
    		return true;
    	 }
    	return false;
    }
    
    /**
     * @see gov.va.med.esr.common.rule.ContactInformationInput#getResultBadAddressReasonText()
     */
    public BadAddressReason getResultBadAddressReasonText() {
        return this.getResultAddress() != null ? this.getResultAddress().getBadAddressReason() : null;
    }

    /**
     * @see gov.va.med.esr.common.rule.parameter.ContactInformationInput#getSourceOfChange()
     */
    public AddressChangeSource getSourceOfChange() {
        return this.getIncomingAddress() != null ? this.getIncomingAddress().getChangeSource() : null;
    }

    /**
     * @see gov.va.med.esr.common.rule.parameter.ContactInformationInput#getPristineSourceOfChange()
     */
    public AddressChangeSource getPristineSourceOfChange() {
        return this.getPristineAddress() != null ? this.getPristineAddress().getChangeSource() : null;
    }

    /**
     * @see gov.va.med.esr.common.rule.parameter.ContactInformationInput#getPhoneNumber()
     */
    public String getPhoneNumber() {
        return this.getIncomingAddress() != null ? this.getIncomingAddress().getPhoneNumber() : null;
    }

    /** 
     * @see gov.va.med.esr.common.rule.parameter.ContactInformationInput#getPristinePhoneNumber()
     */
    public String getPristinePhoneNumber() {
        return this.getPristineAddress() != null ? this.getPristineAddress().getPhoneNumber() : null;
    }
    
    /** 
     * @see gov.va.med.esr.common.rule.parameter.ContactInformationInput#setAddressLineOne(java.lang.String)
     */
    public void setAddressLineOne(String line) {
        this.getResultAddress().setLine1(line);
    }

    /** 
     * @see gov.va.med.esr.common.rule.parameter.ContactInformationInput#setAddressLineTwo(java.lang.String)
     */
    public void setAddressLineTwo(String line) {
        this.getResultAddress().setLine2(line);
    }
    
    /** 
     * @see gov.va.med.esr.common.rule.parameter.ContactInformationInput#setAddressLineThree(java.lang.String)
     */
    public void setAddressLineThree(String line) {
        this.getResultAddress().setLine3(line);
    }

    /**
     * @see gov.va.med.esr.common.rule.parameter.ContactInformationInput#setCity(java.lang.String)
     */
    public void setCity(String city) {
        this.getResultAddress().setCity(city);
    }

    /** 
     * @see gov.va.med.esr.common.rule.parameter.ContactInformationInput#setCounty(java.lang.String)
     */
    public void setCounty(String county) {
        this.getResultAddress().setCounty(county);
    }

    /** 
     * @see gov.va.med.esr.common.rule.parameter.ContactInformationInput#setChangeDate(java.util.Date)
     */
    public void setChangeDate(Date date) {
        this.getResultAddress().setChangeDate(date);
    }

    /** 
     * @see gov.va.med.esr.common.rule.parameter.ContactInformationInput#setProvince(java.lang.String)
     */
    public void setProvince(String province) {
        this.getResultAddress().setProvince(province);
    }

    /** 
     * @see gov.va.med.esr.common.rule.parameter.ContactInformationInput#setCountry(java.lang.String)
     */
    public void setCountry(String country) {
        this.getResultAddress().setCountry(country);
    }

    /** 
     * @see gov.va.med.esr.common.rule.parameter.ContactInformationInput#setBadAddressReasonText(java.lang.String)
     */
    public void setBadAddressReasonText(BadAddressReason text) {
        this.getResultAddress().setBadAddressReason(text);
    }

    /** 
     * @see gov.va.med.esr.common.rule.parameter.ContactInformationInput#setSourceOfChange(java.lang.String)
     */
    public void setSourceOfChange(AddressChangeSource value) {
        this.getResultAddress().setChangeSource(value);
    }

    /** 
     * @see gov.va.med.esr.common.rule.parameter.ContactInformationInput#isVeteranAddressMatched()
     */
    public void setPhoneNumber(String text) {
        this.getResultAddress().setPhoneNumber(text);
    }
    
    /** 
     * @see gov.va.med.esr.common.rule.parameter.ContactInformationInput#isVeteranAddressMatched()
     */
    public boolean isVeteranAddressMatched() {
        String incomingAddresType = this.getAddressType(this
                .getIncomingAddress());
        String currentAddresType = this.getAddressType(this
                .getPristineAddress());
        return isEqual(incomingAddresType, currentAddresType);
    }

    /** 
     * @see gov.va.med.esr.common.rule.parameter.ContactInformationInput#isAddressDateValid()
     */
    public boolean isAddressDateValid() {
        // TODO this is the way Van implemented this???
        return true;
    }
    
    public boolean isAddressActive() {
        Address incoming = this.getIncomingAddress();
        return (incoming != null) ? incoming.isActive() : true;
    }

    /** 
     * @see gov.va.med.esr.common.rule.parameter.ContactInformationInput#isAddressTypeEqual(java.lang.String)
     */
    public boolean isAddressTypeEqual(String type) {
        String incoming = null;
        // Special when called by external system
        ExternalSystemsInputData esi = this.getExternalSystemsInputData();
        if (esi != null) {
            if (esi.getDemographicsChangeEvent().getEntityKey() != null
                    && esi.getDemographicsChangeEvent().getEntityKey().getKey() != null
                    && esi.getDemographicsChangeEvent().getChangeType().equals(
                            DemographicsChangeType.ADDRESS_CHANGE)) {
                Person person = this.getIncomingPerson();
                EntityKey key = CommonEntityKeyFactory
                        .createAddressEntityKey(getExternalSystemsInputData()
                                .getDemographicsChangeEvent().getEntityKey()
                                .getKey().toString());
                if (key != null) {
                    Address address = person.getAddressByEntityKey(key);
                    incoming = address != null ? this.getAddressType(address)
                            : null;
                }
            }
        } else {
            // For normal use
            incoming = this.getAddressType(this.getIncomingAddress());
        }

        return incoming != null ? isEqual(incoming, type) : false;
    }

    /** 
     * @see gov.va.med.esr.common.rule.parameter.ContactInformationInput#isAddressTypeValid()
     */
    public boolean isAddressTypeValid() {
        String incomingAddressType = this.getAddressType(this
                .getIncomingAddress());
        if (isEqual(incomingAddressType, AddressType.CODE_PERMANENT_ADDRESS.getName())
                || isEqual(incomingAddressType, AddressType.CODE_CONFIDENTIAL_ADDRESS.getName())
                        || isEqual(incomingAddressType, AddressType.CODE_TEMPORARY_CORRESPONDENCE_ADDRESS.getName()) ) {
            return true;
        } else {
            return false;
        }
    }

    /** 
     * @see gov.va.med.esr.common.rule.parameter.ContactInformationInput#isAddressAfter()
     */
    public boolean isAddressAfter() {
        Date incoming = this.getUpdatedDate(this.getIncomingAddress());
        Date current = this.getUpdatedDate(this.getPristineAddress());
        return isAfter(incoming, current);
    }

    public boolean isAddressUpdateDateEqual() {
        Date incoming = this.getUpdatedDate(this.getIncomingAddress());
        Date current = this.getUpdatedDate(this.getPristineAddress());
        return isEqual(incoming, current);
    }
    /** 
     * @see gov.va.med.esr.common.rule.parameter.ContactInformationInput#isAddressStateValid()
     */
    public boolean isAddressStateValid() {
       
        // TODO remove this method and associated BAL rule.
        // The builder already handles USA and non-USA addresses.
        return true;
    }

    /** 
     * @see gov.va.med.esr.common.rule.parameter.ContactInformationInput#setVeteranAddressStateCode(gov.va.med.esr.common.model.lookup.State)
     */
    public void setVeteranAddressStateCode(String value) {
        this.getResultAddress().setState(value);
    }

    /** 
     * @see gov.va.med.esr.common.rule.parameter.ContactInformationInput#setVeteranAddressZipCode(gov.va.med.esr.common.model.lookup.ZipCode)
     */
    public void setVeteranAddressZipCode(String value) {
        this.getResultAddress().setZipCode(value);
    }

    /** 
     * @see gov.va.med.esr.common.rule.parameter.ContactInformationInput#setVeteranAddressProvinceCode(java.lang.String)
     */
    public void setVeteranAddressProvinceCode(String value) {
        this.getResultAddress().setProvince(value);
    }

    /** 
     * @see gov.va.med.esr.common.rule.parameter.ContactInformationInput#setVeteranAddressPostalCode(java.lang.String)
     */
    public void setVeteranAddressPostalCode(String value) {
        this.getResultAddress().setPostalCode(value);
    }

    /** 
     * @see gov.va.med.esr.common.rule.parameter.ContactInformationInput#createVeteranAddress()
     */
    public void createVeteranAddress() {
        updateSetOfAddressWithNewAddressInfo(this.getIncomingAddress(),
                this.updatedVeteranAddresses);
    }

    /** 
     * @see gov.va.med.esr.common.rule.parameter.ContactInformationInput#expireVeteranAddress()
     */
    public void expireVeteranAddress() {
        retireCurrentAddress(this.getIncomingAddress(),
                this.getUpdatedVeteranAddresses());

    }

    /** 
     * @see gov.va.med.esr.common.rule.parameter.ContactInformationInput#updateVeteranAddress()
     */
    public void updateVeteranAddress() {
        updateSetOfCurrentAddressWithUpdatedAddressInfo(this
                .getIncomingAddress(), this.getUpdatedVeteranAddresses());

    }

    /** 
     * @see gov.va.med.esr.common.rule.parameter.ContactInformationInput#getVeteranAddressUpdatedDate()
     */
    public Date getVeteranAddressUpdatedDate() {
        return this.getPristineAddress() != null ? 
                this.getPristineAddress().getChangeDate() : null;
    }

    /** 
     * @see gov.va.med.esr.common.rule.parameter.ContactInformationInput#getAddressUpdatedDate()
     */
    public Date getAddressUpdatedDate() {
        return this.getIncomingAddress() != null ?
                this.getIncomingAddress().getChangeDate() : null;
    }

    public void setAddressUpdatedDate(Date date) {
        Address address = this.getResultAddress();
        if(address != null) {
            address.setChangeDate(date);
        }
    }
    
    /** 
     * @see gov.va.med.esr.common.rule.parameter.ContactInformationInput#getAddressStateCode()
     */
    public String getAddressStateCode() 
    {
        Address address = this.getIncomingAddress();
        return (address != null && StringUtils.isNotEmpty(address.getState())) ? address.getState() : null;
    }

    /** 
     * @see gov.va.med.esr.common.rule.parameter.ContactInformationInput#getAddressZipCode()
     */
    public String getAddressZipCode() {
        return this.getIncomingAddress() != null ? this.getIncomingAddress().getZipCode() : null;
    }

    public String getPostalCode()
    {
        return this.getIncomingAddress() != null ? this.getIncomingAddress().getPostalCode() : null;
    }
    
    public String getPristinePostalCode()
    {
        return this.getPristineAddress() != null ? this.getPristineAddress().getPostalCode() : null;
    }
    
    /**
     * @see gov.va.med.esr.common.rule.ContactInformationInput#getAddressTypeCode()
     */
    public AddressType getAddressTypeCode() {
        return this.getIncomingAddress() != null ? this.getIncomingAddress().getType() : null;
    }

    /**
     * @see gov.va.med.esr.common.rule.ContactInformationInput#getChangeDate(java.util.Date)
     */
    public Date getChangeDate() {
        return this.getIncomingAddress() != null ? this.getIncomingAddress().getChangeDate() : null;
    }

    /**
     * @see gov.va.med.esr.common.rule.ContactInformationInput#getChangeSite()
     */
    public VAFacility getChangeSite() {
        return this.getIncomingAddress() != null ? this.getIncomingAddress().getChangeSite() : null;
    }

    /**
     * @see gov.va.med.esr.common.rule.ContactInformationInput#getZipPlus4()
     */
    public String getZipPlus4() {
        return this.getIncomingAddress() != null ? this.getIncomingAddress().getZipPlus4() : null;
    }
    
    public String getPristineZipPlus4() {
        return this.getPristineAddress() != null ? this.getPristineAddress().getZipPlus4() : null;
    }

    /**
     * @see gov.va.med.esr.common.rule.parameter.ContactInformationInput#getPristineAddressZipCode()
     */
    public String getPristineAddressZipCode(){
    	return this.getPristineAddress() != null ? this.getPristineAddress().getZipCode() : null;
    }

    /**
     * @see gov.va.med.esr.common.rule.ContactInformationInput#setAddressTypeCode(gov.va.med.esr.common.model.lookup.AddressType)
     */
    public void setAddressTypeCode(AddressType addressType) {
        this.getResultAddress().setType(addressType);
    }

    /**
     * @see gov.va.med.esr.common.rule.ContactInformationInput#setZipzipPlus4(java.lang.String)
     */
    public void setZipPlus4(String value) {
        this.getResultAddress().setZipPlus4(value);
    }
    
    /**
     * @see gov.va.med.esr.common.rule.ContactInformationInput#setBadAddressReasonText(null)
     */
    public void setBadAddressReasonText(BadAddressReason.Code text) throws RuleException {
        try {
            if (text != null) {
                this.getResultAddress().setBadAddressReason(this.getLookupService().getBadAddressReasonByCode(text.getName()));                
            }
        }

        catch (ServiceException e) {
            throw new RuleException("Failed to set enrollment priority", e);
        }        

    }
    
    /**
     * @see gov.va.med.esr.common.rule.ContactInformationInput#getPOAAddressLineOne()
     */
    public String getPOAAddressLineOne() {
        Address address = this.getLetterMailingAddress();
        return address != null ? address.getLine1() : null;
    }
    
    /**
     * @see gov.va.med.esr.common.rule.ContactInformationInput#getPOAAddressStateCode()
     */
    public String getPOAAddressStateCode() {
        Address address = this.getLetterMailingAddress();
        return address != null ? address.getState() : null;
    }
    
    /**
     * @see gov.va.med.esr.common.rule.ContactInformationInput#getPOAAddressZipCode()
     */
    public String getPOAAddressZipCode() {
        Address address = this.getLetterMailingAddress();
        return address != null ? address.getZipCode() : null;
    }
    
    /**
     * @see gov.va.med.esr.common.rule.ContactInformationInput#getPOAAddressProvince()
     */
    public String getPOAAddressProvince() {
        Address address = this.getLetterMailingAddress();
        return address != null ? address.getProvince() : null;
    }
    
    /**
     * @see gov.va.med.esr.common.rule.ContactInformationInput#getPOAAddressPostalCode()
     */
    public String getPOAAddressPostalCode() {
        Address address = this.getLetterMailingAddress();
        return address != null ? address.getPostalCode() : null;
    }
    
    /**
     * @see gov.va.med.esr.common.rule.ContactInformationInput#getPOACity()
     */
    public String getPOACity() {
        Address address = this.getLetterMailingAddress();
        return address != null ? address.getCity() : null;
    }
    
    /**
     * @see gov.va.med.esr.common.rule.ContactInformationInput#getPOACountry()
     */
    public String getPOAAddressCountry() {
        Address address = this.getLetterMailingAddress();
        return address != null ? address.getCounty() : null;
    }
    
    /**
     * @see gov.va.med.esr.common.rule.ContactInformationInput#getUpdatedLetterAddress()
     */
    public Address getUpdatedLetterAddress() {
        return this.getCommsInputData() != null ? this.getCommsInputData().getUpdatedMailingAddress() : null;
    }

    /**
     * @see gov.va.med.esr.common.rule.ContactInformationInput#isCurrentConfidentialAddressEqualOriginalLetterAddress()
     */
    public boolean isCurrentConfidentialAddressEqualOriginalLetterAddress() {
        // Compares current Confidential address on file field by field to the address stored
        // in the comms log entry. Returns true if equal else false. This method only gets called by
        // rules if the address is confidential
        return this.isCurrentMailingAddressSameAsOriginal();
    }

    /**
     * @see gov.va.med.esr.common.rule.ContactInformationInput#isCurrentTemporaryAddressEqualOriginalLetterAddress()
     */
    public boolean isCurrentTemporaryAddressEqualOriginalLetterAddress() {
        // Compares current temporary address on file field by field to the address stored in
        // the comms log entry. Returns true if equal else false. This method only gets called by
        // rules if the address is temporary
        return this.isCurrentMailingAddressSameAsOriginal();
    }

    // Public methods not used by ILOG
    public Date getUpdatedDate(Address address) {
        return (address != null) ? address.getChangeDate() : null;
    }
   
    
    // Private Methods
    
    private void updateSetOfAddressWithNewAddressInfo(Address newAddress,Set addresses) 
   {
        if (addresses != null) 
        {
            try
            {
                Address address = this.getMergeRuleService().mergeAddress(newAddress,new Address());
                this.getResultPerson().addAddress(address);
            }
            catch(ServiceException ex)
            {
                this.logger.debug( "Failed to add/merge a new Address", ex);
				throw new RuntimeException("Failed to add/merge a new Address", ex);
            }
        }
    }

    private void updateSetOfCurrentAddressWithUpdatedAddressInfo(Address updatedAddress, Set addresses) 
    {
        if (addresses != null) 
        {
            boolean isUpdated = false;
            for (Iterator iter = addresses.iterator(); iter.hasNext();) 
            {
                try
                {	Address currentAddress = (Address) iter.next();
                	
                	// CCR10307 -- prevent null pointer exception caused by address type being null

                    if (currentAddress.getType() != null &&
                    	isEqual(currentAddress.getType().getCode(),updatedAddress.getType().getCode())) 
                    {	
                        this.getMergeRuleService().mergeAddress(updatedAddress,currentAddress);
                        isUpdated = true;
                        
                        //  VOA 3.1 CodeCR7423
                        if (isMessageFromVOA()){
                        	if ( updatedAddress!=null && updatedAddress.getCountry() == null && ( AddressType.CODE_PERMANENT_ADDRESS.equals(updatedAddress.getType()) ||
                    			AddressType.CODE_CONFIDENTIAL_ADDRESS.equals(updatedAddress.getType()) ||
                    			AddressType.CODE_TEMPORARY_CORRESPONDENCE_ADDRESS.equals(updatedAddress.getType()) 
                    			) )
                    			// updatedAddress.setCountry(Country.CODE_USA.getCode());
                    			currentAddress.setCountry(Country.CODE_USA.getCode());
                    	}
                    	// end of CodeCR7423 
                    }   
                }
                catch(ServiceException ex)
                {
                    this.logger.debug( "Failed to merge an Address", ex);
    				throw new RuntimeException("Failed to merge an Address", ex);
                }
            }     
            // If no address of incoming type is found in the database, create a new one and add.
            if(!isUpdated)
            {
                try
                {
                    Address currentAddress = new Address();  
                    this.getMergeRuleService().mergeAddress(updatedAddress,currentAddress);
                    
                    //  VOA 3.1 CodeCR7423
                    if (isMessageFromVOA()){
                    	if ( updatedAddress!=null && updatedAddress.getCountry() == null && ( AddressType.CODE_PERMANENT_ADDRESS.equals(updatedAddress.getType()) ||
                			AddressType.CODE_CONFIDENTIAL_ADDRESS.equals(updatedAddress.getType()) ||
                			AddressType.CODE_TEMPORARY_CORRESPONDENCE_ADDRESS.equals(updatedAddress.getType()) 
                			) )
                			// updatedAddress.setCountry(Country.CODE_USA.getCode());
                			currentAddress.setCountry(Country.CODE_USA.getCode());
                	}
                	// end of CodeCR7423
                	
                    this.getResultPerson().addAddress(currentAddress);
                }
                catch(ServiceException ex)
                {
                    this.logger.debug( "Failed to add/merge an Address", ex);
    				throw new RuntimeException("Failed to add/merge an Address", ex);
                }
            }
        }
    }

    private void retireCurrentAddress(Address newAddress, Set addresses) {
        if (addresses != null) {
    		for (Iterator iter = addresses.iterator(); iter.hasNext();) {
                Object obj = iter.next();
                if (obj instanceof Address) {
                    Address currentAddress = (Address) obj;
                    if ((currentAddress.getType() != null) && (newAddress != null)
                            && (newAddress.getType() != null)) {
                        if (isEqual(currentAddress.getType().getCode(), newAddress
                                .getType().getCode())) {
                            // Retire the address by setting end date
                            currentAddress.setEndDate(this.getImpreciseDate(Calendar.getInstance().getTime()));
                            // Set the update date of the address to the update
                            // date of new address
                            currentAddress.setChangeDate(newAddress.getChangeDate());
                        }
                    }
                }
    		}
        }
    }

    private String getAddressType(Address address) {
        return (address != null && address.getType() != null) ?
                address.getType().getCode() : null;

    }

    /**
     * @see gov.va.med.esr.common.rule.ContactInformationInput#isAddressChanged()
     */
    public boolean isAddressChanged() {
    	if (this.getPristineAddress() == null && this.getIncomingAddress() != null) {
    		return true;
    	}
        if( !this.isEqual(this.getAddressLineOne(),this.getPristineAddressLineOne()) ||
            !this.isEqual(this.getCity(),this.getPristineCity()) ||
            !this.isEqual(this.getAddressStateCode(),this.getPristineAddressStateCode()) ||
            !this.isEqual(this.getAddressZipCode(),this.getPristineAddressZipCode()) ||
            !this.isEqual(this.getProvince (),this.getPristineProvince()) ||
            !this.isEqual(this.getPostalCode(),this.getPristinePostalCode()) ||
            !this.isEqual(this.getCountry(),this.getPristineCountry()) ) {
            return true;
        }
        return false;
    }
    
    private Address getIncomingAddress() {
        if ( this.incomingAddress == null ) {
        	DemographicAccessor accessor = getDemographicAccessor();
        	this.incomingAddress = accessor != null ? accessor.getAddress(PropertyAccessor.DataType.INCOMING) : null;
        	
            /*// For comms context
            if (this.getCommsInputData() != null) {
                this.incomingAddress = this.getCommsInputData().getMailingAddress();            
            }
            else {
                // For normal address context
                this.incomingAddress = (this.getAddressInputData() != null) ? this
                        .getAddressInputData().getIncomingAddress() : null;                
            }*/
        }
        return this.incomingAddress;
    }

    private Address getPristineAddress() {
        if ( this.pristineAddress == null ) {
        	DemographicAccessor accessor = getDemographicAccessor();
        	this.pristineAddress = accessor != null ? accessor.getAddress(PropertyAccessor.DataType.PRISTINE) : null;
        	
            /*this.pristineAddress = (this.getAddressInputData() != null) ? this
                    .getAddressInputData().getPristineAddress() : null;*/
        }
        return this.pristineAddress;
    }

    
    public boolean isValidUSAState() {
    	return isValidUSAState(getAddressStateCode());
    }
    
	private boolean isValidUSAState(String stateCode) {
		boolean valid = false;

		try {
			State state = this.getLookupService().getStateByCode(stateCode);
			if(state != null)
				valid = true;
		} catch(ServiceException e) {  /* do nothing */ }
		return valid;
	}
    

    /**
     * @return Returns the updatedVeteranAddresses.
     */
    private Set getUpdatedVeteranAddresses() {
        if ( this.updatedVeteranAddresses == null ){
            this.updatedVeteranAddresses = (this.getAddressInputData() != null) ?
                    this.getAddressInputData().getUpdatedVeteranAddresses() : null;
        }
        return updatedVeteranAddresses;
    }    
    
    private AddressInputData getAddressInputData() {
        if ( this.addressInputData == null ) {
            RuleDataAware data = this.getRuleDataAware();
            if (data instanceof AddressInputData) {
                this.addressInputData = (AddressInputData) data;
            }
        }
        return this.addressInputData;
    }

    private Association getOnFileAssociation() {
        return this.getAddressInputData().getOnFileAssociation();
    }
    
    /**
     * @return Returns the resultAddress.
     */
    private Address getResultAddress() {
        // This method does not get called unless an address is needed for
        // updating or inserting. Must create one if null.
        if ( this.resultAddress == null ) {
            // Handle address from comms context
            if (this.getCommsInputData() != null) {
                // If we get here it means that address is being updated in comms
                // context
                this.resultAddress = this.getCommsInputData().getMailingAddress();
                this.getCommsInputData().setAddressChanged(true);
            }
            else {
                this.resultAddress = this.getAddressInputData() != null ? this
                        .getAddressInputData().getResultAddress() : null;
                // Handle result address from an association
                if (resultAddress == null && getAssociation() != null) {
                    Address newAddress = new Address();
                    this.getOnFileAssociation().setAddress(newAddress);
                    return newAddress;
                }
                // Handle result address from a person
                if (resultAddress == null && getIncomingAddress() != null
                        && getIncomingAddress().getType() != null) {
                    resultAddress = Address.getAddressOfType(this.getResultPerson()
                            .getAddresses(), getIncomingAddress().getType()
                            .getCode());
                    if (resultAddress == null) {
                        resultAddress = new Address();
                        // Link to person
                        this.getResultPerson().addAddress(resultAddress);
                    }
                }                
            }
        }

        return this.resultAddress;
    }

    // TODO move to base class
    private Date getPreciseDate(ImpreciseDate date) {
        return date != null ? ImpreciseDateUtils.getDateWithDefault(date)
                : null;
    }

    // TODO move to base class
    private ImpreciseDate getImpreciseDate(Date date) {
        return date != null ? new ImpreciseDate(date) : null;
    }

    
	/**
	 * @see gov.va.med.esr.common.rule.parameter.BaseParameter#getKeyedEntity()
	 */
	protected AbstractKeyedEntity getKeyedEntity() {
		return this.getResultAddress();
	}
    
    private boolean isCurrentMailingAddressSameAsOriginal() {
        CommsInputData cid = this.getCommsInputData();
        if (cid != null) {
            Address current = cid.getMailingAddress();
            Address original = cid.getUpdatedMailingAddress();
            if (original == null && current != null) {
                return true;
            }
        }
        return false;       
    }
    
    private Address getLetterMailingAddress() {
        CommsInputData commsInputData = this.getCommsInputData();
        CommsLogEntry commsLogEntry = (commsInputData != null)?
                commsInputData.getCommsLogEntry() : null;
        return commsLogEntry != null ? commsLogEntry.getAddress() : null;       
    }
    
    public boolean isIncomingAddressFieldsAllNull(){	
    	if ( StringUtils.isEmpty(this.getAddressLineOne()) &&
    		 StringUtils.isEmpty(this.getAddressLineTwo()) &&
    		 StringUtils.isEmpty(this.getAddressLineThree()) &&
    		 StringUtils.isEmpty(this.getAddressStateCode()) &&
    		 StringUtils.isEmpty(this.getAddressZipCode()) &&
    		 StringUtils.isEmpty(this.getCity()) &&
    		 StringUtils.isEmpty(this.getCountry()) &&
    		 StringUtils.isEmpty(this.getCounty()) &&
    		 StringUtils.isEmpty(this.getProvince()) &&
    		 StringUtils.isEmpty(this.getPostalCode()) &&
    		 StringUtils.isEmpty(this.getZipPlus4()) )
    		return true;
    	else
    		return false;
    }
        
    
    //CCR11898
    public boolean isVerificationInfoNotNull(){
    	if(this.getAddressInputData().getVerificationInfo() != null)
    		return true;
    	else
    		return false;
    }
    
    //CCR11898
    public void setTempAddressFlaginVerificationInfo(boolean tempAddressUpdateMoreRecent){
    	this.getAddressInputData().getVerificationInfo().setTempAddressUpdateMoreRecent(tempAddressUpdateMoreRecent);
    }

}