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

// Java classes
import java.util.Date;

// Library classes
import java.util.Set;

// Library classes

// Framework classes
import gov.va.med.fw.model.AbstractKeyedEntity;
import gov.va.med.fw.rule.RuleDataAware;
import gov.va.med.fw.service.ServiceException;
import gov.va.med.fw.util.StringUtils;

// ESR Classes
import gov.va.med.esr.common.infra.ImpreciseDateUtils;
import gov.va.med.esr.common.rule.InsuranceInput;
import gov.va.med.esr.common.rule.data.InsuranceInputData;
import gov.va.med.esr.common.rule.service.MatchRuleService;
import gov.va.med.esr.common.model.insurance.PrivateInsurance;
import gov.va.med.esr.common.model.insurance.Medicare;
import gov.va.med.esr.common.model.insurance.InsurancePolicy;
import gov.va.med.esr.common.model.insurance.InsurancePlan;
import gov.va.med.esr.common.model.insurance.InsuranceSubscriber;
import gov.va.med.esr.common.model.lookup.InsuranceReportSource;
import gov.va.med.esr.common.model.lookup.VAFacility;

/**
 * This class is a facade that provides access to insurance data. Parameter
 * objects are loaded into ILOG working memory for use by rules.
 * 
 * @author Carlos Ruiz
 * @version 1.0
 */
public class InsuranceInputParameter extends BaseParameter implements InsuranceInput {

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

	private InsurancePolicy policy = null;
	
	private PrivateInsurance privateInsurance = null;

	private Medicare medicare = null;

	private String medicareClaimNumber = null;

	private String nameOnMedicareCard = null;
	
	private Boolean enrolledInPartA = null;

	private Boolean enrolledInPartB = null;
	
	private Date partAEffectiveDate = null;
	
	private Date partBEffectiveDate = null;
	
	private String relationshipToInsured = null;

	private String nameOfInsure = null;
	
	private String reportSource = null;
	
	private String insurancePolicyType = null;
	
	private AbstractKeyedEntity updatedEntity = null;
	
	/**
	 * Default constructor
	 */
	public InsuranceInputParameter() {
		super();
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see gov.va.med.esr.common.rule.parameter.InsuranceInput#getInsuranceEffectiveDate()
	 */
	public Date getInsuranceEffectiveDate() {
        return (getPrivateInsurance() != null && getPrivateInsurance()
                .getPolicyEffectiveDate() != null) ? ImpreciseDateUtils
                .getDateWithDefault(getPrivateInsurance()
                        .getPolicyEffectiveDate()) : null;
    }

	/*
	 * (non-Javadoc)
	 * 
	 * @see gov.va.med.esr.common.rule.parameter.InsuranceInput#getInsuranceExpirationDate()
	 */
	public Date getInsuranceExpirationDate() {
        return (getPrivateInsurance() != null && getPrivateInsurance()
                .getPolicyExpirationDate() != null) ? ImpreciseDateUtils
                .getDateWithDefault(getPrivateInsurance()
                        .getPolicyExpirationDate()) : null;
    }

	/**
	 * @see gov.va.med.esr.common.rule.InsuranceInput#getMedicareClaimNumber()
	 */
	public String getMedicareClaimNumber() {
		if(  this.medicareClaimNumber == null ) {
			Medicare medicare = this.getMedicare();
			this.medicareClaimNumber = ((medicare != null ) ? medicare.getMedicareClaimNumber() : null);
		}
		return this.medicareClaimNumber;
	}

	/**
	 * @see gov.va.med.esr.common.rule.InsuranceInput#getNameOnMedicareCard()
	 */
	public String getNameOnMedicareCard() {
		if(  this.nameOnMedicareCard == null ) {
			Medicare medicare = this.getMedicare();
			this.nameOnMedicareCard = ((medicare != null ) ? medicare.getNameOnMedicareCard() : null);
		}
		return this.nameOnMedicareCard;
	}

	/**
	 * @see gov.va.med.esr.common.rule.InsuranceInput#getEnrolledInPartA()
	 */
	public boolean getEnrolledInPartA() {
		if(  this.enrolledInPartA == null ) {
			Medicare medicare = this.getMedicare();
			this.enrolledInPartA = ((medicare != null ) ? medicare.isEnrolledInPartA() : Boolean.FALSE );
		}
		return this.enrolledInPartA.booleanValue();
	}

	/**
	 * @see gov.va.med.esr.common.rule.InsuranceInput#getEnrolledInPartB()
	 */
	public boolean getEnrolledInPartB() {
		if(  this.enrolledInPartB == null ) {
			Medicare medicare = this.getMedicare();
			this.enrolledInPartB = ((medicare != null ) ? medicare.isEnrolledInPartB() : Boolean.FALSE );
		}
		return this.enrolledInPartB != null ? 
                this.enrolledInPartB.booleanValue() : false;
	}

	/**
	 * @see gov.va.med.esr.common.rule.InsuranceInput#getPartAEffectiveDate()
	 */
	public Date getPartAEffectiveDate() {
		 return (getMedicare() != null && getMedicare().getPartAEffectiveDate()!=null) ? 
				 ImpreciseDateUtils.getDateWithDefault(getMedicare().getPartAEffectiveDate()) : null;             
	    /*            
		if(  this.partAEffectiveDate == null ) {
			Medicare medicare = this.getMedicare();
			this.partAEffectiveDate = ((medicare != null ) ? medicare.getPartAEffectiveDate() : null );
		}
		return this.partAEffectiveDate;
		*/
	}

	/**
	 * @see gov.va.med.esr.common.rule.InsuranceInput#getPartBEffectiveDate()
	 */
	public Date getPartBEffectiveDate() {
		 return (getMedicare() != null && getMedicare().getPartBEffectiveDate()!=null) ? 
				 ImpreciseDateUtils.getDateWithDefault(getMedicare().getPartBEffectiveDate()) : null;    
		/*		 
		if(  this.partBEffectiveDate == null ) {
			Medicare medicare = this.getMedicare();
			this.partBEffectiveDate = ((medicare != null ) ? medicare.getPartBEffectiveDate() : null );
		}
		return this.partBEffectiveDate;
		*/
	}

	/**
	 * @see gov.va.med.esr.common.rule.InsuranceInput#isInsurancePolicyTypeForMedicare()
	 */
	public boolean isInsurancePolicyTypeForMedicare() {
		return (this.getMedicare() != null);
	}

	/**
	 * @see gov.va.med.esr.common.rule.InsuranceInput#getRelationshipToInsured()
	 */
	public String getRelationshipToInsured() {
		if(  this.relationshipToInsured == null ) {
			InsurancePolicy policy = this.getInsuranceInputData().getIncomingInsurancePolicy();
			if( policy != null ) {
				InsuranceSubscriber subscriber = policy.getSubscriber();
				this.relationshipToInsured = ((subscriber != null && subscriber.getRelationship() != null) ? subscriber.getRelationship().getCode() : null );
			}
		}
		return this.relationshipToInsured;
	}

	/**
	 * @see gov.va.med.esr.common.rule.InsuranceInput#getNameOfInsured()
	 */
	public String getNameOfInsured() {
		if(  this.nameOfInsure == null ) {
			InsurancePolicy policy = this.getInsuranceInputData().getIncomingInsurancePolicy();
			if( policy != null ) {
				InsuranceSubscriber subscriber = policy.getSubscriber();
				this.nameOfInsure = ((subscriber != null ) ? subscriber.getName() : null );
			}
		}
		
		return StringUtils.isEmpty(this.nameOfInsure) ? null : this.nameOfInsure;
	}

	/**
	 * @see gov.va.med.esr.common.rule.InsuranceInput#setNameOfInsured(java.lang.String)
	 */
	public void setNameOfInsured(String name) {
		
		InsurancePolicy policy = this.getInsurancePolicy();
		if( policy != null && policy.getSubscriber() != null ) {
			InsuranceSubscriber subscriber = policy.getSubscriber();
			
			subscriber.setName( name );
			this.nameOfInsure = name;
		}
	}

	/**
	 * @see gov.va.med.esr.common.rule.InsuranceInput#setInsuranceCompanyName(java.lang.String)
	 */
	public void setInsuranceCompanyName(String name) {
		InsurancePolicy policy = this.getInsurancePolicy();
		if( policy != null ) {
			policy.setCompanyName( name );
		}
	}

	/**
	 * @see gov.va.med.esr.common.rule.InsuranceInput#setGroupNumber(java.lang.String)
	 */
	public void setGroupNumber(String groupNumber) {
		InsurancePolicy policy = this.getInsuranceInputData().getIncomingInsurancePolicy();
		if( policy != null ) {
			policy.setGroupNumber( groupNumber );
		}
	}

	/**
	 * @see gov.va.med.esr.common.rule.InsuranceInput#setGroupName(java.lang.String)
	 */
	public void setGroupName(String groupName) {
		InsurancePolicy policy = this.getInsuranceInputData().getIncomingInsurancePolicy();
		if( policy != null ) {
			policy.setGroupName( groupName );
		}
	}

	/**
	 * @see gov.va.med.esr.common.rule.InsuranceInput#setEnrolledInMedicarePartA(boolean)
	 */
	public void setEnrolledInMedicarePartA(boolean flag) {
		Medicare policy = this.getMedicare();
		if( policy != null ) {
			policy.setEnrolledInPartA( Boolean.valueOf( flag ) );
			this.enrolledInPartA = Boolean.valueOf( flag );
		}
	}

	/**
	 * @see gov.va.med.esr.common.rule.InsuranceInput#setEnrollendInMedicarePartB(boolean)
	 */
	public void setEnrolledInMedicarePartB(boolean flag) {
		Medicare policy = this.getMedicare();
		if( policy != null ) {
			policy.setEnrolledInPartB( Boolean.valueOf( flag ) );
			this.enrolledInPartB = Boolean.valueOf( flag );
		}
	}

	/**
	 * @see gov.va.med.esr.common.rule.InsuranceInput#getInsuranceReportSource()
	 */
	public String getInsuranceReportSource() {
		if( logger.isDebugEnabled() ) {
			logger.debug( "A report source's code is " + this.reportSource );
		}
		if(  this.reportSource == null ) {
			InsurancePolicy policy = this.getInsuranceInputData().getIncomingInsurancePolicy();
			if( policy != null ) {
				InsuranceReportSource source = policy.getReportSource();
				this.reportSource = ((source != null ) ? source.getCode() : null );
			}
		}
		return this.reportSource;
	}

	/**
	 * Compares incoming association type with the veteran's record on file. If a
	 * matching type is found returns true, false otherwise
	 *
	 * @see gov.va.med.esr.common.rule.InsuranceInput#isInsurancePolicyOnFile()
	 */
	public boolean isInsurancePolicyOnFile() {
        boolean newRecord = false;
        InsurancePolicy incoming = this.getInsurancePolicy();
        //CCR 12291: VOA calls the manageInsurancePolocies() in order to trigger Z04, 
        //which has caller as UI (isUpdateFromGUI() = true) 
        //but here, need to take the non-UI route so that insurances will not be duplicated
        if(this.isUpdateFromGUI() && 
        		!(getSendingFacility() != null && VAFacility.CODE_MHV.getCode().equals(getSendingFacility().getCode()))){ //not from VOA
            InsurancePolicy onFilePolicy = (incoming.getEntityKey() != null) ? this.getPristinePerson().getInsuranceByEntityKey(incoming.getEntityKey()) : null;
            newRecord = (onFilePolicy != null) ? true : false;
        } else {
            newRecord = (this.getResultInsurancePolicy() != null);
        }
		if( logger.isDebugEnabled() ) {
			logger.debug( "Is this a new reccord ? " + newRecord );
		}
		return newRecord;
	}

	/**
	 * @see gov.va.med.esr.common.rule.InsuranceInput#mergeInsurancePolicy()
	 */
	public void mergeInsurancePolicy() {
		
		try {
			InsurancePolicy incoming = this.getInsurancePolicy();
			this.updatedEntity = this.getMergeRuleService().mergeInsurancePolicy( incoming, this.getResultPerson() );
		}
		catch( ServiceException e ) {
			if( this.logger.isDebugEnabled() ) {
				this.logger.debug( "Failed to merge an insurance policy", e );
			}
			throw new RuntimeException("Failed to merge an insurance policy", e );
		}
	}

	public void addInsurancePolicy() 
	{	
        InsurancePolicy incoming = this.getInsurancePolicy();
        if(incoming != null)
        {
            try
            {
	            InsurancePolicy newPolicy = (InsurancePolicy)incoming.getClass().newInstance();
	            newPolicy = this.getMergeRuleService().mergeInsurancePolicy(incoming,newPolicy);
	            this.getResultPerson().addInsurance(newPolicy);
	        }
	    	catch(Exception ex)
	    	{
	    	    if( this.logger.isDebugEnabled() ) {
	    	        this.logger.debug( "Failed to merge new insurance policy", ex);
	    	    }
	    	    throw new RuntimeException("Failed to merge new insurance policy", ex);
	    	}
		}
	}

	public String getInsurancePolicyType() {
		if (this.insurancePolicyType == null ) {
			InsurancePolicy incoming = this.getInsurancePolicy();
			InsurancePlan plan = incoming.getInsurancePlan();
			
			this.insurancePolicyType = (plan != null) ? plan.getPlanType().getCode() : null; 
		}
		return this.insurancePolicyType;
	}
	
	 public String getAddressZipCode() 
	 {
		 InsurancePolicy incoming = this.getInsurancePolicy();
		 if (incoming != null && incoming.getAddress() != null) {
			 return incoming.getAddress().getZipCode();
		 }
         return null;
	 }
	
	 public String getPristineAddressZipCode()
	 {
		 InsurancePolicy incoming = this.getInsurancePolicy();
	     if(this.isUpdateFromGUI() && incoming.getEntityKey() != null) {
	         InsurancePolicy onFilePolicy = 
	        	 this.getPristinePerson().getInsuranceByEntityKey(incoming.getEntityKey());	            
	         if (onFilePolicy != null && onFilePolicy.getAddress() != null) {
	        	 return onFilePolicy.getAddress().getZipCode();
	         }
	     }
         return null;	 
    }
	 
	/**
	 * @return Returns the privateInsurance.
	 */
	private PrivateInsurance getPrivateInsurance() {
		if (this.privateInsurance == null ) {
			this.privateInsurance = (this.getInsuranceInputData() != null) ? this
					.getInsuranceInputData().getIncomingPrivateInsurance() : null;
		}
		return privateInsurance;
	}

	/**
	 * @return Returns the medicare
	 */
	private Medicare getMedicare() {
		if (this.medicare == null ) {
			this.medicare = (this.getInsuranceInputData() != null) ? this
					.getInsuranceInputData().getIncomingMedicare() : null;
		}
		return this.medicare;
	}

	private InsurancePolicy getInsurancePolicy() {
		if (this.policy == null ) {
			this.policy = (this.getInsuranceInputData() != null) ? this
					.getInsuranceInputData().getIncomingInsurancePolicy() : null;
		}
		return this.policy;
	}
    
    public InsurancePolicy getResultInsurancePolicy() 
    {
        // Get current set of InsurancePolicy and incoming InsurancePolicy
        InsurancePolicy incoming = this.getInsurancePolicy();
        
        //check null of incoming to avoid exception "A source to find a match must not be null"
        if (incoming == null)
        	return null;
        Set result = this.getResultPerson().getInsurances();
        MatchRuleService mrs = this.getMergeRuleService().getMatchRuleService();
            
        // Find a match association on file
        return (InsurancePolicy)mrs.findMatchingElement(incoming,result);
    }
    
   public boolean isInsuranceAddressFound()
   {
	 InsurancePolicy incoming = this.getInsurancePolicy();
	 
	 if (incoming != null && incoming.getAddress() != null) 
	     return true;
	 else 
	     return false;
   }
   
   public String getInsuranceCompanyName()
   {
	 InsurancePolicy incoming = this.getInsurancePolicy();
	 
	 if (incoming != null && incoming.getCompanyName() != null ) 
	     return incoming.getCompanyName();
	 
	 return null;
   }
   
	private InsuranceInputData getInsuranceInputData() {
		RuleDataAware ruleDataAware = this.getRuleDataAware();
		if (ruleDataAware instanceof InsuranceInputData) {
			return (InsuranceInputData) ruleDataAware;
		}
		return null;
	}

	/**
	 * @see gov.va.med.esr.common.rule.parameter.BaseParameter#getKeyedEntity()
	 */
	protected AbstractKeyedEntity getKeyedEntity() {
		return this.updatedEntity;
	}
}