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

import java.util.Collections;
import java.util.Date;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;

import org.apache.commons.lang.Validate;
import org.apache.commons.lang.builder.ToStringBuilder;

import gov.va.med.fw.model.AbstractKeyedEntity;
import gov.va.med.fw.util.DateUtils;

import gov.va.med.esr.common.infra.ImpreciseDate;
import gov.va.med.esr.common.model.lookup.AssociationType;
import gov.va.med.esr.common.model.lookup.Gender;
import gov.va.med.esr.common.model.lookup.Relationship;
import gov.va.med.esr.common.model.lookup.SSNType;

/**
 * Abstract relation Object to hold common relation information.
 * Possible subclasses of Relation could be Dependent and Spouse.
 * 
 * @author DNS   MANSOG
 */
public class Relation
    extends AbstractKeyedEntity {
    
    private static final long serialVersionUID = -1370346746640850300L;
    private Person        person       = null;
    private Relationship  relationship = null;
    private Name          name         = null;
    private Gender        gender       = null;
    private ImpreciseDate dob          = null;

    private ImpreciseDate startDate    = null;
    private Date          endDate      = null;

    private Set           internalSsns = null;

    /**
     * Default Constructor.
     */
    public Relation() {
        super();
    }

    public Person getPerson() {
        return person;
    }

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

    public ImpreciseDate getDob() {
        return dob;
    }

    public void setDob(ImpreciseDate dob) {
        this.dob = dob;
    }

    public Gender getGender() {
        return gender;
    }

    public void setGender(Gender gender) {
        this.gender = gender;
    }

    public Name getName() {
        return name;
    }

    public void setName(Name name) {
        this.name = name;
    }

    public ImpreciseDate getStartDate() {
        return this.startDate;
    }

    public void setStartDate(ImpreciseDate startDate) {
        this.startDate = startDate;
    }

    public Date getEndDate() {
        return this.endDate;
    }

    public void setEndDate(Date endDate) {
        this.endDate = endDate;
    }    

    /**
     * Getter - for hibernate use only.
     * @return Set
     */
    private Set getInternalSsns() {
        if (this.internalSsns == null) {
            this.internalSsns = new HashSet();
        }
        return internalSsns;
    }

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

    /**
     * Returns a collection of SSN.
     * 
     * @return Set - Set of SSN Objects
     */
    public Set getSsns() {
        return Collections.unmodifiableSet(getInternalSsns());
    }

    /**
     * Add a SSN for this relation.
     * This method takes care of Bidirectional setting of SSN's relation.
     * @param ssn
     */
    public void addSsn(SSN ssn) {
        Validate.notNull(ssn, "SSN cannot be null");
        getInternalSsns().add(ssn);
        ssn.setRelation(this);
    }

    /**
     * Remove an SSN from this person's SSN collection.
     * This method takes care of Bidirectional setting of SSN's person.
     * @param ssn
     */
    public void removeSsn(SSN ssn) {
        Validate.notNull(ssn, "SSN cannot be null");
        getInternalSsns().remove(ssn);
    }

    public void removeAllSsns() {
        getInternalSsns().clear();
    }

    /**
     * Get the pseudo SSN.
     * @return
     */
    public SSN getPseudoSsn() {
        return SSN.getSSNOfType(getInternalSsns(),
                SSNType.CODE_PSEUDO.getName());
    }

    /**
     * Get the first official SSN from the SSN collection.
     * @return
     */
    public SSN getOfficialSsn() {
        return SSN.getSSNOfType(getInternalSsns(),
                SSNType.CODE_ACTIVE.getName());
    }

    /**
     * Return a collection of SSN of type Official.
     * @return
     */
    public Set getOfficialSsns() {
        Set otherSsns = SSN.getSSNsOfType(getInternalSsns(),
                SSNType.CODE_ACTIVE.getName());
        return Collections.unmodifiableSet(otherSsns);
    }

    /**
     * Return a collection of SSN of type Other.
     * @return
     */
    public Set getOtherSsns() {
        Set otherSsns = SSN.getSSNsOfType(getInternalSsns(),
                SSNType.CODE_ALIAS.getName());
        return Collections.unmodifiableSet(otherSsns);
    }

    public Relationship getRelationship() {
        return this.relationship;
    }

    public void setRelationship(Relationship relationship) {
        this.relationship = relationship;
    }

    protected void buildToString(ToStringBuilder builder) {
        super.buildToString(builder);
        builder.append("name", this.name);
        builder.append("gender", this.gender);
        builder.append("dob", this.dob);
        builder.append("relationship", this.relationship);
        builder.append("internalSsns", this.internalSsns);
        builder.append("startDate", this.startDate);
        builder.append("endDate", this.endDate);
    }
}

