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

import gov.va.med.esr.common.model.CommonEntityKeyFactory;
import gov.va.med.esr.common.model.lookup.VAFacility;
import gov.va.med.esr.common.model.person.id.PersonEntityKey;
import gov.va.med.esr.common.model.person.id.PersonIdEntityKeyImpl;
import gov.va.med.esr.common.model.person.id.VPIDEntityKey;
import gov.va.med.esr.service.PersonIdentityTraits;
import gov.va.med.esr.service.trigger.BulletinTriggerEvent.PersonIdentityFields;
import gov.va.med.fw.model.AbstractKeyedEntity;
import gov.va.med.fw.util.Reflector;
import gov.va.med.person.idmgmt.VPID;

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

import org.apache.commons.lang.Validate;

public class PreferredFacilityPerson extends AbstractKeyedEntity {

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


   private String VPIDValue;
   
   private Set internalPreferredFacilities = null;

   private Set internalProviderAssignments = null;

   /* This field stores the calculated result of the most recent preferred facility site */
   private VAFacility mostRecentPreferredFacility = null;

   // CCR11593 eliminate multiple 1305, traits is needed in order to be passed to the person event trigger to avoid duplicate 1305 search calls
   PersonIdentityTraits identityTraits = null;
   
   // TODO to be deleted
   public VPIDEntityKey getVPIDEntityKey() {
      return ( VPIDValue != null ) ? CommonEntityKeyFactory
            .createVPIDEntityKey(VPIDValue) : null;
   }
   private String getVPIDValue() {
      return VPIDValue;
   }

   private void setVPIDValue(String VPIDValue) {
      this.VPIDValue = VPIDValue;
   }
   
   public String getShortVPID() {
      return VPID.getShortVPID(getVPIDValue());
   }

   protected Class getEntityKeyClass() {
	      return PersonIdEntityKeyImpl.class;
   }

   public PersonEntityKey getPersonEntityKey() {
	  return (PersonEntityKey)getEntityKey();
   }
   
   public PersonIdentityTraits getIdentityTraits() {
		return identityTraits;
	}
   
   public void setIdentityTraits(PersonIdentityTraits identityTraits) {
		this.identityTraits = identityTraits;
   }
   
   /**
    * @see gov.va.med.fw.model.AbstractKeyedEntity#finalize()
    */
   protected void finalize() throws Throwable {
      super.finalize();
      setVPIDValue( null );
   }
   
   
   public VAFacility getMostRecentPreferredFacility() {
		return mostRecentPreferredFacility;
   }
   
   public void setMostRecentPreferredFacility(
			VAFacility mostRecentPreferredFacility) {
	   this.mostRecentPreferredFacility = mostRecentPreferredFacility;
   }

   /* Returns a collection of PreferredFacility Objects. If no preferredFacilities exists for this person,
    * this method will return empty Set.
    * 
    * @return Set - Set of PreferredFacilities
    */
   public Set getPreferredFacilities() {
      return Collections.unmodifiableSet(getInternalPreferredFacilities());
   }
   
	/**
	* Getter - for hibernate use only.
	* 
	* @return
	*/
	private Set getInternalPreferredFacilities() {
     if( this.internalPreferredFacilities == null ) {
        this.internalPreferredFacilities = new HashSet();
     }
     return this.internalPreferredFacilities;
	}
	
	/**
	* Setter - for hibernate use only.
	* 
	* @param preferredFacilities
	*/
	private void setInternalPreferredFacilities(Set preferredFacilities) {
     this.internalPreferredFacilities = preferredFacilities;
	}
	
	/**
	* Add a PreferredFacility for this person. This method takes care of
	* Bidirectional setting of PreferredFacility person.
	* 
	* @param preferredFacility
	*/
	public void addPreferredFacility(
			PreferredFacilityLite preferredFacility) {
     Validate.notNull(preferredFacility,
           "Null preferredFacility specified.");
     getInternalPreferredFacilities().add(preferredFacility);
     preferredFacility.setPersonLite(this);
	}
	
	/**
	* Add a set of preferredFacility for this person. This method takes care of
	* Bidirectional setting of PreferredFacility person.
	* 
	* @param preferredFacilities
	*/
	public void addAllPreferredFacilities(Set preferredFacilities) {
     Validate.notNull(preferredFacilities,
           "Empty set of preferredFacilities specified.");

     this.setOwner(preferredFacilities);
     Set internal = this.getInternalPreferredFacilities();
     internal.addAll(preferredFacilities);
	}
	
	/**
	 * Remove an PreferredFacilityLite for this person. This method takes care of
	 * Bidirectional setting of PreferredFacility's person.
	 * 
	 * @param preferredFacility
	 */
	public void removePreferredFacility(PreferredFacilityLite preferredFacility) {
		Validate
				.notNull(preferredFacility, "Null PreferredFacility specified.");
		preferredFacility.setPersonLite(null);
		getInternalPreferredFacilities().remove(preferredFacility);
	}

	public void removeAllPreferredFacilities() {
		this.getInternalPreferredFacilities().clear();
	}
	
	/*
	 * Returns a collection of PatientProviderAssignment Objects. If no
	 * patientProviderAssignment exists for this person, this method will return
	 * empty Set.
	 * 
	 * @return Set - Set of PatientProviderAssignment
	 */
	public Set getProviderAssignments() {
		return Collections.unmodifiableSet(getInternalProviderAssignments());
	}

	/**
	* Getter - for hibernate use only.
	* 
	* @return
	*/
	private Set getInternalProviderAssignments() {
     if( this.internalProviderAssignments == null ) {
        this.internalProviderAssignments = new HashSet();
     }
     return this.internalProviderAssignments;
	}
	
	/**
	* Setter - for hibernate use only.
	* 
	* @param pAssignments
	*/
	private void setInternalProviderAssignments(Set providerAssignments) {
     this.internalProviderAssignments = providerAssignments;
	}
	
	/**
	* Add a PatientProviderAssignment for this person. This method takes care of
	* Bidirectional setting of PatientProviderAssignment person.
	* 
	* @param preferredFacility
	*/
	public void addProviderAssignment(
			PatientProviderAssignmentLite providerAssignment) {
     Validate.notNull(providerAssignment,
           "Null ProviderAssignment specified.");
     getInternalProviderAssignments().add(providerAssignment);
     providerAssignment.setPersonLite(this);
	}
	
	/**
	* Add a set of PatientProviderAssignments for this person. This method takes care of
	* Bidirectional setting of PatientProviderAssignment person.
	* 
	* @param providerAssignments
	*/
	public void addAllProviderAssignments(Set providerAssignments) {
     Validate.notNull(providerAssignments,
           "Empty set of providerAssignments specified.");

     this.setOwner(providerAssignments);
     Set internal = this.getInternalProviderAssignments();
     internal.addAll(providerAssignments);
	}
	
	/**
	 * Remove an PatientProviderAssignment for this person. This method takes care of
	 * Bidirectional setting of PatientProviderAssignment person.
	 * 
	 * @param providerAssignment
	 */
	public void removeProviderAssignment(PatientProviderAssignmentLite providerAssignment) {
		Validate
				.notNull(providerAssignment, "Null PatientProviderAssignments specified.");
		providerAssignment.setPersonLite(null);
		getInternalProviderAssignments().remove(providerAssignment);
	}

	public void removeAllProviderAssignments() {
		this.getInternalProviderAssignments().clear();
	}
   
   private void setOwner(AbstractKeyedEntity entity) {
      Validate.notNull(entity, "Null entity specified");
      try {
         // Invoke a setPerson method on an entity to set
         // this (Person) into it. VL
         Reflector.invoke(entity, "setPerson", new Object[] { this });
      }
      catch( Exception e ) {
         // Catch all exception here since there is reason to handle each
         // exception separately - VL
         if( getLogger().isDebugEnabled() ) {
            getLogger().debug("Failed to set person as an owner for entity " + entity, e);
         }
      }
   }

   private void setOwner(Collection entities) {

	      // JDK 5.0, we can pass in a collection of the same type
	      // For now, we enfore that a collection must contain
	      // only elements of type AbstractKeyedEntity - VL

	      Validate.notNull(entities, "Null entity specified");
	      for( Iterator i = entities.iterator(); i.hasNext(); ) {
	         Object entity = i.next();
	         // This will throw a ClassCastException to
	         // let a caller handle properly since a collection
	         // must contain only AbstractKeyedEntity - VL
	         this.setOwner((AbstractKeyedEntity)entity);
	      }
	   }


}
