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

// Java Classes
import java.util.HashSet;
import java.util.Set;

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

// Framework Classes
import gov.va.med.fw.model.AbstractKeyedEntity;
import gov.va.med.fw.util.StringUtils;

// ESR Classes
import gov.va.med.esr.common.model.lookup.InsuranceReportSource;
import gov.va.med.esr.common.model.lookup.InsuranceSiteUploadStatus;
import gov.va.med.esr.common.model.lookup.PhoneType;
import gov.va.med.esr.common.model.lookup.ReasonInsuranceNotUploaded;
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.party.Phone;
import gov.va.med.esr.common.model.person.Person;

/**
 * @author Yi He
 * @author DNS   MANSOG
 * @version 1.0
 */
public abstract class InsurancePolicy extends AbstractKeyedEntity implements Comparable
{
    
    private static final long serialVersionUID = -1370346746640850300L;
    private Person person = null;

    private InsuranceSubscriber subscriber = null;

    private InsurancePlan insurancePlan = null;

    private Set internalAddresses = null;

    private Set internalPhones = null;

    private InsuranceReportSource reportSource;

    private VAFacility reportSite;

    private InsuranceSiteUploadStatus insuranceSiteUploadStatus;

    private ReasonInsuranceNotUploaded reasonInsuranceNotUploaded;

    /**
     * Insurance Company name is defined, as the name of the insurance company
     */
    private String companyName = null;

    /**
     * Group Name is defined as the name that the insurance company uses to identify this plan.
     */
    protected String groupName = null;

    /**
     * Group Number is defined as the number or code which the insurance company uses to identify this plan.
     */
    protected String groupNumber = null;

    /**
     * Creates a new InsurancePolicy object.
     */
    public InsurancePolicy()
    {
        super();
    }

    public Person getPerson()
    {
        return this.person;
    }

    public void setPerson(Person person)
    {
        this.person = person;
    }

    public InsuranceSubscriber getSubscriber()
    {
        return this.subscriber;
    }

    public void setSubscriber(InsuranceSubscriber subscriber)
    {
        this.subscriber = subscriber;
    }

    /**
     * @return A InsurancePlan object
     */
    public InsurancePlan getInsurancePlan()
    {
        return this.insurancePlan;
    }

    public void setInsurancePlan(InsurancePlan insurancePlan)
    {
        this.insurancePlan = insurancePlan;
    }

    /**
     * Get the Address associated with the InsurancePolicy
     *
     * @return Address or null if no address found.
     */
    public Address getAddress()
    {
        Set addresses = getInternalAddresses();
        if (addresses.size() > 0)
        {
            return (Address)addresses.iterator().next();
        }
        return null;
    }

    /**
     * Set the address to this InsurancePolicy.
     *
     * @param address
     */
    public void setAddress(Address address)
    {
        Set addresses = getInternalAddresses();
        addresses.clear();
        // setting null mean remove the existing but not add it.
        if (address != null)
        {
            addAddress(address);
        }
    }

    /**
     * Return the Pre-Certification Phone associated with this InsurancePolicy.
     *
     * @return Phone of type Pre-Certification or null if no Pre-Certification phone found.
     */
    public Phone getPreCertificationPhone()
    {
        return Phone.getPhoneOfType(getInternalPhones(),
            PhoneType.CODE_PRE_CERTIFICATION_PHONE.getName());
    }

    /**
     * Return the Business Phone associated with this InsurancePolicy.
     *
     * @return Phone of type Business or null if no business address found.
     */
    public Phone getBusinessPhone()
    {
        return Phone.getPhoneOfType(getInternalPhones(),
            PhoneType.CODE_BUSINESS.getName());
    }

    /**
     * Return the Fax associated with this InsurancePolicy.
     *
     * @return Phone of type Fax or null if no fax found.
     */
    public Phone getFax()
    {
        return Phone.getPhoneOfType(getInternalPhones(),
            PhoneType.CODE_FAX.getName());
    }

    /**
     * Set the phone of type Pre-Certification. If the phone type is not Pre-Certification,
     * this method will throw IllegalArgumentException.
     *
     * @param phone the phone to set.
     *
     * @throws IllegalArgumentException if the Phone type is not Pre-Certification.
     */
    public void setPreCertificationPhone(Phone phone)
    {
        setPhone(getInternalPhones(), phone, PhoneType.CODE_PRE_CERTIFICATION_PHONE.getName());
    }

    /**
     * Set the phone of type business. If the phone type is not business, this method will throw
     * IllegalArgumentException.
     *
     * @param phone
     *
     * @throws IllegalArgumentException if the Phone type is not business.
     */
    public void setBusinessPhone(Phone phone)
    {
        setPhone(getInternalPhones(), phone, PhoneType.CODE_BUSINESS.getName());
    }

    /**
     * Set the phone of type fax. If the phone type is not fax, this method will throw IllegalArgumentException.
     *
     * @param fax
     *
     * @throws IllegalArgumentException if the Phone type is not fax.
     */
    public void setFax(Phone fax)
    {
        setPhone(getInternalPhones(), fax, PhoneType.CODE_FAX.getName());
    }

    /**
     * @param phones
     * @param phone
     * @param typeCode
     */
    private void setPhone(Set phones, Phone phone, String typeCode)
    {
        if (phone != null && !phone.getType().getCode().equals(typeCode))
        {
            throw new IllegalArgumentException(
                "Invalid Phone type: Phone type code must be " + typeCode);
        }
        Phone currentPhone = Phone.getPhoneOfType(phones, typeCode);
        if (currentPhone != null)
        {
            removePhone(currentPhone);
        }

        // Set this phone as a new phone. We have already removed existing phone
        // if there is any.
        if (phone != null)
        {
            addPhone(phone);
        }
    }

    public InsuranceReportSource getReportSource()
    {
        return this.reportSource;
    }

    public void setReportSource(InsuranceReportSource reportSource)
    {
        this.reportSource = reportSource;
    }

    public VAFacility getReportSite()
    {
        return this.reportSite;
    }

    public void setReportSite(VAFacility reportSite)
    {
        this.reportSite = reportSite;
    }

    /**
     * Setter - for hibernate use only.
     *
     * @param addresses
     */
    private void setInternalAddresses(Set addresses)
    {
        this.internalAddresses = addresses;
    }

    /**
     * Getter - for hibernate use only.
     *
     * @return the internal addresses
     */
    private Set getInternalAddresses()
    {
        if (this.internalAddresses == null)
        {
            this.internalAddresses = new HashSet();
        }
        return this.internalAddresses;
    }

    /**
     * Add a Address for this InsurancePolicy. This method takes care of Bidirectional setting of Address's
     * InsurancePolicy.
     *
     * @param address
     */
    private void addAddress(Address address)
    {
        Validate.notNull(address, "Null address specified.");
        getInternalAddresses().add(address);
        address.setInsurancePolicy(this);
    }

    /**
     * Remove an Address from this InsurancePolicy's Address collection. This method takes care of Bidirectional setting
     * of Address's InsurancePolicy.
     *
     * @param address
     */
    private void removeAddress(Address address)
    {
        Validate.notNull(address, "Null address specified.");
        getInternalAddresses().remove(address);
    }

    /**
     * Getter - for hibernate use only.
     *
     * @return the internal phones
     */
    private Set getInternalPhones()
    {
        if (this.internalPhones == null)
        {
            this.internalPhones = new HashSet();
        }
        return this.internalPhones;
    }

    /**
     * Setter - for hibernate use only.
     *
     * @param phones
     */
    private void setInternalPhones(Set phones)
    {
        this.internalPhones = phones;
    }

    /**
     * Add a Phone for this InsurancePolicy. This method takes care of Bidirectional setting of Phone's
     * InsurancePolicy.
     *
     * @param phone
     */
    private void addPhone(Phone phone)
    {
        Validate.notNull(phone, "Null phone specified.");
        getInternalPhones().add(phone);
        phone.setInsurancePolicy(this);
    }

    /**
     * Remove an Phone for this InsurancePolicy. This method takes care of Bidirectional setting of Phone's
     * InsurancePolicy.
     *
     * @param phone
     */
    private void removePhone(Phone phone)
    {
        Validate.notNull(phone, "Null phone specified.");
        getInternalPhones().remove(phone);
    }

    public InsuranceSiteUploadStatus getInsuranceSiteUploadStatus()
    {
        return insuranceSiteUploadStatus;
    }

    public void setInsuranceSiteUploadStatus(
        InsuranceSiteUploadStatus siteUploadStatus)
    {
        this.insuranceSiteUploadStatus = siteUploadStatus;
    }

    public ReasonInsuranceNotUploaded getReasonInsuranceNotUploaded()
    {
        return reasonInsuranceNotUploaded;
    }

    public void setReasonInsuranceNotUploaded(
        ReasonInsuranceNotUploaded reasonInsuranceNotUploaded)
    {
        this.reasonInsuranceNotUploaded = reasonInsuranceNotUploaded;
    }

    public String getCompanyName()
    {
        return this.companyName;
    }

    public void setCompanyName(String companyName)
    {
        this.companyName = companyName;
    }

    public String getGroupName()
    {
        return this.groupName;
    }

    public void setGroupName(String groupName)
    {
        this.groupName = groupName;
    }

    public String getGroupNumber()
    {
        return this.groupNumber;
    }

    public void setGroupNumber(String groupNumber)
    {
        this.groupNumber = groupNumber;
    }

    public boolean isSiteRecord()
    {
        return ((getReportSite() != null) &&
            (getReportSite().getStationNumber().compareTo(VAFacility.CODE_HEC.getName()) != 0));
    }

    /**
     * Compares two insurance policies.  The comparrison is based on the company name followed by
     * the group name followed by the group number.
     * @param policyObject The other policy object to compare.
     * @return the comparrision value.
     */
    public int compareTo(Object policyObject)
    {
        InsurancePolicy otherPolicy = (InsurancePolicy)policyObject;
        int returnValue = StringUtils.compareTo(getCompanyName(), otherPolicy.getCompanyName());
        if (returnValue == 0)
        {
            returnValue = StringUtils.compareTo(getGroupName(), otherPolicy.getGroupName());
            if (returnValue == 0)
            {
                returnValue = StringUtils.compareTo(getGroupNumber(), otherPolicy.getGroupNumber());
            }
        }
        return returnValue;
    }

    /*
     * (non-Javadoc)
     * 
     * @see gov.va.med.fw.model.AbstractKeyedEntity#buildToString(org.apache.commons.lang.builder.ToStringBuilder)
     */
    protected void buildToString(ToStringBuilder builder)
    {
        super.buildToString(builder);
        builder.append("subscriber", this.subscriber);
        builder.append("companyName", this.companyName);
        builder.append("groupName", this.groupName);
        builder.append("groupNumber", this.groupNumber);
        builder.append("reportSite", this.reportSite);
        builder.append("reportSource", this.reportSource);
        builder.append("reasonInsuranceNotUploaded",
            this.reasonInsuranceNotUploaded);
        builder.append("insuranceSiteUploadStatus",
            this.insuranceSiteUploadStatus);
        builder.append("internalAddresses", this.internalAddresses);
        builder.append("internalPhones", this.internalPhones);
        builder.append("insurancePlan", this.insurancePlan);
    }
}