// Package
package gov.va.med.esr.common.model.party;

// Java Classes
import java.util.Date;
import java.util.Iterator;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

// Library Classes
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.insurance.InsurancePolicy;
import gov.va.med.esr.common.model.lookup.PhoneSourceOfChange;
import gov.va.med.esr.common.model.lookup.PhoneType;
import gov.va.med.esr.common.model.lookup.VAFacility;
import gov.va.med.esr.common.model.person.Person;
import gov.va.med.esr.common.model.person.Spouse;

public class Phone extends AbstractKeyedEntity
{
    private static final long serialVersionUID = 6489355045450738752L;
    
    // Pattern Matching constants for parsing phone numbers
    private static final String PATTERN_PHONE = "[\\(]?(\\d{3})??[\\)]?[\\-\\.]?(\\d{3})?[\\-\\.]?(\\d{4})?(?:x[\\-]?(\\d{1,4}))?"; // (999)999-9999x-9999
    private static final int PATTERN_GROUP_AREA_CODE = 1;
    private static final int PATTERN_GROUP_NPA = 2;
    private static final int PATTERN_GROUP_NXX = 3;
    private static final int PATTERN_GROUP_EXT = 4;

    private PhoneType type;
    private String phoneNumber;
    private PhoneSourceOfChange sourceOfChange;
    private VAFacility siteOfChange;
    private Date changeDate;
    private Person person;
    private Spouse spouse;
    private InsurancePolicy insurancePolicy;

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

    public PhoneType getType()
    {
        return this.type;
    }

    public void setType(PhoneType type)
    {
        this.type = type;
    }

    public String getPhoneNumber()
    {
        return this.phoneNumber;
    }

    public void setPhoneNumber(String phoneNumber)
    {
        this.phoneNumber = phoneNumber;
    }

    public PhoneSourceOfChange getSourceOfChange() {
        return this.sourceOfChange;
    }
    
    public void setSourceOfChange(PhoneSourceOfChange sourceOfChange) {
        this.sourceOfChange = sourceOfChange;
    }
    
    public VAFacility getSiteOfChange() {
        return this.siteOfChange;
    }
    
    public void setSiteOfChange(VAFacility site) {
        this.siteOfChange = site;
    }
    
    public Date getChangeDate() {
        return this.changeDate;
    }

    public void setChangeDate(Date changeDate) {
        this.changeDate = changeDate;
    }

    /**
     * Returns a formatted version of this Phone
     * @return The formatted Phone
     */
    public String getFormattedPhone()
    {
        return getFormattedPhone(getPhoneNumber());
    }

    /**
     * Gets a formatted version of this phone number
     * @param phoneNumber The phone number to format
     * @return The formatted phone number
     */
    public static String getFormattedPhone(String phoneNumber)
    {
        // Create the result buffer
        StringBuffer resultBuffer = new StringBuffer();

        if (StringUtils.isNotEmpty(phoneNumber))
        {
            String areaCode = null;
            String npa = null;
            String nxx = null;
            String ext = null;

            // Remove the spaces
            String filteredPhoneNumber = phoneNumber.replaceAll(" ", "");

            Matcher matcher = Pattern.compile(PATTERN_PHONE).matcher(filteredPhoneNumber);
            if (matcher.matches())
            {
                areaCode = matcher.group(PATTERN_GROUP_AREA_CODE);
                npa = matcher.group(PATTERN_GROUP_NPA);
                nxx = matcher.group(PATTERN_GROUP_NXX);
                ext = matcher.group(PATTERN_GROUP_EXT);
            }
            else
            {
                // Filter out all phone number special characters: ( ) - X .
                filteredPhoneNumber = filteredPhoneNumber.toUpperCase();
                filteredPhoneNumber = filteredPhoneNumber.replaceAll("\\(", "");
                filteredPhoneNumber = filteredPhoneNumber.replaceAll("\\)", "");
                filteredPhoneNumber = filteredPhoneNumber.replaceAll("X", "");
                filteredPhoneNumber = filteredPhoneNumber.replaceAll("x", "");
                filteredPhoneNumber = filteredPhoneNumber.replaceAll("-", "");
                filteredPhoneNumber = filteredPhoneNumber.replaceAll("\\.", "");

                // Grab each piece of the phone number
                StringBuffer filterBuffer = new StringBuffer(filteredPhoneNumber);
                areaCode = getPhonePiece(filterBuffer, 3);
                npa = getPhonePiece(filterBuffer, 3);
                nxx = getPhonePiece(filterBuffer, 4);
                ext = filterBuffer.toString();
            }

            // Append the area code
            if (StringUtils.isNotEmpty(areaCode))
            {
                resultBuffer.append("(");
                resultBuffer.append(areaCode);
                resultBuffer.append(")");
            }

            // Append the NPA
            if (StringUtils.isNotEmpty(npa))
            {
                resultBuffer.append(npa);
                if (StringUtils.isNotEmpty(nxx))
                {
                    resultBuffer.append("-");
                }
            }

            // Append the NXX
            if (StringUtils.isNotEmpty(nxx))
            {
                resultBuffer.append(nxx);
            }

            // Append the Extention
            if (StringUtils.isNotEmpty(ext))
            {
                /*if (resultBuffer.length() > 0)
                {
                    resultBuffer.append(" ");
                }*/
                resultBuffer.append("X");
                resultBuffer.append(ext);
            }
        }

        // Return the formatted result
        return resultBuffer.toString();
    }

    /**
     * Gets a piece of the phone number while removing that piece from the buffer
     * @param phoneBuffer The phone buffer
     * @param numChars The number of characters to get
     * @return The piece of the phone number
     */
    protected static String getPhonePiece(StringBuffer phoneBuffer, int numChars)
    {
        String returnValue = null;
        if (phoneBuffer.length() > numChars)
        {
            returnValue = phoneBuffer.substring(0, numChars);
            phoneBuffer.delete(0, numChars);
        }
        else
        {
            returnValue = phoneBuffer.toString();
            phoneBuffer.delete(0, phoneBuffer.length());
        }
        return returnValue;
    }

    /**
     * @see gov.va.med.fw.model.AbstractKeyedEntity#buildToString(org.apache.commons.lang.builder.ToStringBuilder)
     */
    protected void buildToString(ToStringBuilder builder)
    {
        super.buildToString(builder);

        builder.append("type", this.type);
        builder.append("phoneNumber", this.phoneNumber);
        builder.append("changeDate", this.changeDate);
    }

    public Person getPerson() {
        return this.person;
    }
    
    public void setPerson(Person person) {
        validateOwner(this.person, person);
        this.person = person;
    }
    

    public Spouse getSpouse() {
        return spouse;
    }

    public void setSpouse(Spouse spouse) {
       validateOwner(this.spouse, spouse);
       this.spouse = spouse;
    }

    public InsurancePolicy getInsurancePolicy() {
        return insurancePolicy;
    }
    public void setInsurancePolicy(InsurancePolicy insurancePolicy) {
        this.insurancePolicy = insurancePolicy;
    }
    
    /**
     * Helper method to get the Phone of a given type code.
     * This method iterates the collection of phones and return 
     * the Phone of give type code or null if no phone for a given type code
     * exists.
     * 
     * @param phones
     * @param typeCode
     * @return Phone if found or null if no phone with a given type found.
     */
    public static Phone getPhoneOfType(Set phones, String typeCode) {
        for (Iterator iter = phones.iterator(); iter.hasNext();) {
            Phone phone = (Phone) iter.next();
            if (phone != null && phone.getType() !=null && phone.getType().getCode() != null && phone.getType().getCode().equals(typeCode)) {
                return phone;
            }
        }
        return null;
    }
}