/*******************************************************************************
 * This file is part of GECAMed.
 * 
 * GECAMed is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License (L-GPL) as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 * 
 * GECAMed is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License for more details.
 * 
 * You should have received a copy of the GNU Lesser General Public License (L-GPL)
 * along with GECAMed.  If not, see <http://www.gnu.org/licenses/>.
 * 
 * GECAMed is Copyrighted by the Centre de Recherche Public Henri Tudor (http://www.tudor.lu)
 * (c) CRP Henri Tudor, Luxembourg, 2008
 *******************************************************************************/
package lu.tudor.santec.gecamed.hl7import.ejb.session.beans;

import java.util.Collection;
import java.util.Date;
import java.util.HashSet;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import javax.ejb.EJB;
import javax.ejb.Local;
import javax.ejb.TransactionAttribute;
import javax.ejb.TransactionAttributeType;
import javax.persistence.EntityManager;
import javax.persistence.NoResultException;
import javax.persistence.PersistenceContext;

import lu.tudor.santec.gecamed.address.ejb.entity.beans.AddressType;
import lu.tudor.santec.gecamed.address.ejb.entity.beans.Country;
import lu.tudor.santec.gecamed.address.ejb.entity.beans.Locality;
import lu.tudor.santec.gecamed.address.ejb.entity.beans.Zip;
import lu.tudor.santec.gecamed.billing.ejb.entity.beans.Invoice;
import lu.tudor.santec.gecamed.billing.ejb.session.beans.RuleBean;
import lu.tudor.santec.gecamed.billing.ejb.session.interfaces.RuleInterface;
import lu.tudor.santec.gecamed.core.ejb.entity.beans.Gender;
import lu.tudor.santec.gecamed.core.ejb.entity.beans.MaritalStatus;
import lu.tudor.santec.gecamed.core.ejb.entity.beans.PhoneType;
import lu.tudor.santec.gecamed.core.ejb.entity.beans.Title;
import lu.tudor.santec.gecamed.core.ejb.entity.beans.log.Log;
import lu.tudor.santec.gecamed.core.ejb.entity.beans.log.LogType;
import lu.tudor.santec.gecamed.core.ejb.session.interfaces.LogManager;
import lu.tudor.santec.gecamed.core.utils.AccidentNumberFormatter;
import lu.tudor.santec.gecamed.core.utils.ManagerFactory;
import lu.tudor.santec.gecamed.core.utils.SettingPluginNames;
import lu.tudor.santec.gecamed.core.utils.StringUtilities;
import lu.tudor.santec.gecamed.hl7import.ejb.entity.beans.HL7Address;
import lu.tudor.santec.gecamed.hl7import.ejb.entity.beans.HL7Admission;
import lu.tudor.santec.gecamed.hl7import.ejb.entity.beans.HL7Bean;
import lu.tudor.santec.gecamed.hl7import.ejb.entity.beans.HL7Guarantor;
import lu.tudor.santec.gecamed.hl7import.ejb.entity.beans.HL7Insurance;
import lu.tudor.santec.gecamed.hl7import.ejb.entity.beans.HL7Phone;
import lu.tudor.santec.gecamed.hl7import.ejb.session.interfaces.AdmissionInterface;
import lu.tudor.santec.gecamed.patient.ejb.entity.beans.HospitalDepartment;
import lu.tudor.santec.gecamed.patient.ejb.entity.beans.Hospitalisation;
import lu.tudor.santec.gecamed.patient.ejb.entity.beans.HospitalisationClass;
import lu.tudor.santec.gecamed.patient.ejb.entity.beans.HospitalisationPeriod;
import lu.tudor.santec.gecamed.patient.ejb.entity.beans.Insurance;
import lu.tudor.santec.gecamed.patient.ejb.entity.beans.InsurancePlan;
import lu.tudor.santec.gecamed.patient.ejb.entity.beans.Patient;
import lu.tudor.santec.gecamed.patient.ejb.entity.beans.PatientAddress;
import lu.tudor.santec.gecamed.patient.ejb.entity.beans.PatientContact;
import lu.tudor.santec.gecamed.patient.ejb.entity.beans.PatientPhone;
import lu.tudor.santec.gecamed.patient.utils.SettingConstants;
import lu.tudor.santec.gecamed.usermanagement.ejb.session.interfaces.LoginInterface;

import org.apache.log4j.Category;
import org.apache.log4j.Level;
import org.jboss.annotation.ejb.Service;

//***************************************************************************
//* Class Definition and Members	                                          *
//***************************************************************************

@Service
@Local (AdmissionInterface.class)

public class AdmissionBean implements AdmissionInterface
	{
	@PersistenceContext (unitName="gecam")
	EntityManager m_GECAMedEntityManager;
	
    @EJB
    LogManager logManager;

//	@PersistenceContext (unitName="hl7import")
//	EntityManager m_GECAMedEntityManager;

	@EJB
	private RuleInterface							m_RuleInterface;
	
	@EJB
	private LoginInterface							m_LoginBean;

	private Country									m_Luxembourg	= null;
	
	private static Hashtable <String,Insurance>				m_Insurances			 = null;
	private static Hashtable <String,InsurancePlan>			m_InsurancePlans		 = null;
	private static Hashtable <String,HospitalisationClass>	m_HospitalisationClasses = null;
	private static Hashtable <String,HospitalDepartment>	m_HospitalDepartments	 = null;;
	
//***************************************************************************
//* Constants	                                                           *
//***************************************************************************

//	private static final long serialVersionUID = 1L;

	private static final Category m_Logger = Category.getInstance(AdmissionBean.class.getName());
	
	private static final int c_MaxPatientNameLength 	 = 40;	
	private static final int c_MaxPatientFirstNameLength = 40;
	private static final int c_MaxPatientMaidenNameLength = 40;
	
	//========================================================================
    //= Various Regex Patterns used for matching incoming HL7 Data with
	//= GECAMed data.
    //========================================================================

	private static Pattern 
    
    //c_NamePattern = Pattern.compile ("([A-Z\\-']+)\\b",Pattern.CASE_INSENSITIVE);
	c_NamePattern = Pattern.compile ("([^\\t\\n\\x0B\\f\\r]+)\\b",Pattern.CASE_INSENSITIVE);

	private static Pattern 
    
    c_ComposedLocalityPattern = Pattern.compile ("^(.*?)-sur-(.*?)$",Pattern.CASE_INSENSITIVE);

    private static Pattern 
    
    c_HL7AddressZipPattern = Pattern.compile ("(\\d{4})$",Pattern.CASE_INSENSITIVE);
    
    private static Pattern 
    
//    c_HL7AddressStreetPattern = Pattern.compile ("^(\\d*[^\\s]*)?\\s*(.*)$",Pattern.CASE_INSENSITIVE);
    c_HL7AddressStreetPattern = Pattern.compile ("^(\\d+[^\\s]*)\\s*(.*)$",Pattern.CASE_INSENSITIVE);
    
    private static Pattern 
    
    c_StreetNumberFirstPattern = Pattern.compile ("^(\\d+).*",Pattern.CASE_INSENSITIVE);

    private static Pattern 
    
    c_StreetNumberLastPattern = Pattern.compile (".*?(\\d+)$",Pattern.CASE_INSENSITIVE);

    private static Pattern 
    
    c_AvenuePattern = Pattern.compile (".*?\\b(av\\.?|ave\\.?)\\s",Pattern.CASE_INSENSITIVE);

    private static Pattern 
    
    c_BoulevardPattern = Pattern.compile (".*?\\b(bd\\.?|bld\\.?)\\s",Pattern.CASE_INSENSITIVE);
 
    private static Pattern 
    
    c_PlacePattern = Pattern.compile (".*?\\b(pl\\.?)\\s",Pattern.CASE_INSENSITIVE);

   private static Pattern 
    
    c_HL7AddressEndingPattern = Pattern.compile ("^.*?\\b(\\w*)$",Pattern.CASE_INSENSITIVE);
	
   private static Pattern 
    
   c_HL7AddressCountryPattern = Pattern.compile ("^luxembourg",Pattern.CASE_INSENSITIVE);

   private static Pattern 
   
   c_HL7PhoneNumberPattern = Pattern.compile ("^(\\d{1})(.*)$",Pattern.CASE_INSENSITIVE);

   private static Pattern 
   
   c_AccidentNumberPattern = Pattern.compile ("^U(\\d{4})\\/?(\\d{5})$",Pattern.CASE_INSENSITIVE);

   //========================================================================
    //= HL7 Gender Translator
    //= Used to match HL7 Gender information with GECAMed Gender. 
   	//========================================================================
   
    private static final Hashtable <String,String> m_GenderTranslator = new Hashtable <String,String> ();
    
    static 	{
    			m_GenderTranslator.put( HL7Admission.c_Male , 	Gender.MALE);
    			m_GenderTranslator.put( HL7Admission.c_Female,	Gender.FEMALE);
    			m_GenderTranslator.put( HL7Admission.c_Other,	Gender.OTHER);
    			m_GenderTranslator.put( HL7Admission.c_Unknown,	Gender.UNKNOWN);
    			}

    //========================================================================
    //= HL7 Marital Status Translator
    //= Used to match HL7 Marital Status with GECAMed Marital Status. 
    //========================================================================
   
    private static final Hashtable <String,String> m_MaritalStatusTranslator = new Hashtable <String,String> ();
    
    static 	{
    			m_MaritalStatusTranslator.put( HL7Admission.c_Separated, 	MaritalStatus.SEPARATED);
    			m_MaritalStatusTranslator.put( HL7Admission.c_Divorced,		MaritalStatus.DIVORCED);
    			m_MaritalStatusTranslator.put( HL7Admission.c_Married,		MaritalStatus.MARRIED);
    			m_MaritalStatusTranslator.put( HL7Admission.c_Single,		MaritalStatus.SINGLE);
    			m_MaritalStatusTranslator.put( HL7Admission.c_Widowed,		MaritalStatus.WIDOWED);
    			m_MaritalStatusTranslator.put( HL7Admission.c_Remarried,	MaritalStatus.MARRIED);
    			//FACT-HIS add variable here
    			m_MaritalStatusTranslator.put( HL7Admission.c_Partnership,	MaritalStatus.PARTNERSHIP);
    			m_MaritalStatusTranslator.put( HL7Admission.c_MSOther,		MaritalStatus.OTHER);
    			}
   
    //========================================================================
    //= HL7 Health Insurance Translator
    //= Used to match HL7 Patient Health Insurance data with GECAMed Health
    //= Insurance data.
    //========================================================================
   
    // TODO now we map the insurance over a mapping in the database so the Hashtable m_HealthInsuranceTranslator 
    // is not longer required 
//    private static final Hashtable <String,String> m_HealthInsuranceTranslator = new Hashtable <String,String> ();
//     
//    static {
//			m_HealthInsuranceTranslator.put( HL7Insurance.c_CNS	    		,"CNS");
//			m_HealthInsuranceTranslator.put( HL7Insurance.c_CNAMO			,"CNAMO");
//			m_HealthInsuranceTranslator.put( HL7Insurance.c_CMFEP			,"CMFEP");
//			m_HealthInsuranceTranslator.put( HL7Insurance.c_CMFEC			,"CMFEC");
//			m_HealthInsuranceTranslator.put( HL7Insurance.c_CMEP			,"CMEP");
//			m_HealthInsuranceTranslator.put( HL7Insurance.c_CMPI			,"CMPI");
//			m_HealthInsuranceTranslator.put( HL7Insurance.c_CMOA			,"CMOA");
//			m_HealthInsuranceTranslator.put( HL7Insurance.c_CMEA			,"CMEA");
//			m_HealthInsuranceTranslator.put( HL7Insurance.c_CMA				,"CMA");
//			m_HealthInsuranceTranslator.put( HL7Insurance.c_EMCFL			,"EM-CFL");
//			m_HealthInsuranceTranslator.put( HL7Insurance.c_ARMEE			,"ARMEE");
//			m_HealthInsuranceTranslator.put( HL7Insurance.c_GUERRE			,"GUERRE");
//			m_HealthInsuranceTranslator.put( HL7Insurance.c_MAMMO			,"MAMMO");
//			m_HealthInsuranceTranslator.put( HL7Insurance.c_SISTEHL			,"SIST-EHL");
//
//			m_HealthInsuranceTranslator.put( HL7Insurance.c_PE				,"RCAM");
//			m_HealthInsuranceTranslator.put( HL7Insurance.c_CE				,"RCAM");
//			m_HealthInsuranceTranslator.put( HL7Insurance.c_CECA			,"RCAM");
//			m_HealthInsuranceTranslator.put( HL7Insurance.c_BEI				,"BEI");
//			m_HealthInsuranceTranslator.put( HL7Insurance.c_RCAM			,"RCAM");
//			m_HealthInsuranceTranslator.put( HL7Insurance.c_EUCTL			,"EUCTL");
//			m_HealthInsuranceTranslator.put( HL7Insurance.c_NAMSA			,"NAMSA");
//
//			m_HealthInsuranceTranslator.put( HL7Insurance.c_SANTE			,"SANTE");
//			m_HealthInsuranceTranslator.put( HL7Insurance.c_EPL				,"EPL");
//			m_HealthInsuranceTranslator.put( HL7Insurance.c_MEPS			,"MEPS");
//			m_HealthInsuranceTranslator.put( HL7Insurance.c_SMA				,"SMA");
//			m_HealthInsuranceTranslator.put( HL7Insurance.c_PARQUET			,"PARQUET");
//			m_HealthInsuranceTranslator.put( HL7Insurance.c_CGE				,"CGE");
//			
//			m_HealthInsuranceTranslator.put( HL7Insurance.c_E111			,"E111");
//			m_HealthInsuranceTranslator.put( HL7Insurance.c_PRIV			,"PRIV");
//			
//			m_HealthInsuranceTranslator.put( HL7Insurance.c_ASTF			,"ASTF");
//			m_HealthInsuranceTranslator.put( HL7Insurance.c_ADMAERO			,"ADMAERO");
//			m_HealthInsuranceTranslator.put( HL7Insurance.c_ADMDESBIENS		,"ADMDESBIENS");
//			m_HealthInsuranceTranslator.put( HL7Insurance.c_AIRRESCUE		,"AIRRESCUE");
//			m_HealthInsuranceTranslator.put( HL7Insurance.c_ALLIANZ			,"ALLIANZ");
//			m_HealthInsuranceTranslator.put( HL7Insurance.c_AMELIFE			,"AMELIFE");
//			m_HealthInsuranceTranslator.put( HL7Insurance.c_AXA				,"AXA");
//			m_HealthInsuranceTranslator.put( HL7Insurance.c_AXAVIE			,"AXAVIE");
//			m_HealthInsuranceTranslator.put( HL7Insurance.c_BALOISE			,"BALOISE");
//			m_HealthInsuranceTranslator.put( HL7Insurance.c_CAEAVIATION		,"CAEAVIATION");
//			m_HealthInsuranceTranslator.put( HL7Insurance.c_CARGOLUX		,"CARGOLUX");
//			m_HealthInsuranceTranslator.put( HL7Insurance.c_CNSAAGR			,"CNS-AAGR");
//			m_HealthInsuranceTranslator.put( HL7Insurance.c_CNSAAIN			,"CNS-AAIN");
//			m_HealthInsuranceTranslator.put( HL7Insurance.c_CONSARBITRAL	,"CONSARBITRAL");
//			m_HealthInsuranceTranslator.put( HL7Insurance.c_DKVLUX			,"DKVLUX");
//			m_HealthInsuranceTranslator.put( HL7Insurance.c_DKV				,"DKV");
//			m_HealthInsuranceTranslator.put( HL7Insurance.c_FORTIS			,"FORTIS");
//			m_HealthInsuranceTranslator.put( HL7Insurance.c_FOYERSANTE		,"FOYERSANTE");
//			m_HealthInsuranceTranslator.put( HL7Insurance.c_LUXAIR			,"LUXAIR");
//			m_HealthInsuranceTranslator.put( HL7Insurance.c_LUXAVIATION		,"LUXAVIATION");
//			m_HealthInsuranceTranslator.put( HL7Insurance.c_OLAI			,"OLAI");
//			m_HealthInsuranceTranslator.put( HL7Insurance.c_PARLEMENT		,"PARLEMENT");
//			m_HealthInsuranceTranslator.put( HL7Insurance.c_RAIFFEISEN		,"RAIFFEISEN");
//			m_HealthInsuranceTranslator.put( HL7Insurance.c_VIVIUM			,"VIVIUM");
//			m_HealthInsuranceTranslator.put( HL7Insurance.c_VLM				,"VLM");
//
//			
////			m_HealthInsuranceTranslator.put( "MT-CFL",		HL7Insurance.c_MTCFL);
////			m_HealthInsuranceTranslator.put( "STA",		HL7Insurance.c_STA);
//    		}
     
    private static final String c_DefaultHealthInsurance   	= "PRIV";
    
    private static final String	c_InsurancePlanNormal		= "CMCM-NORMAL";
    private static final String	c_InsurancePlanPrestaPlus	= "CMCM-PRESTAPLUS";
     
    private static final String	c_Ambulatory				= "A";
    private static final String	c_FirstClass				= "1";
    private static final String	c_SecondClass				= "2";
    
    private static final String c_Space						= " ";
    
    private static final String c_DischargeDeceased			= "53";
    
    private static final String c_Avenue					= "avenue";
    private static final String c_Boulevard					= "boulevard";
    private static final String c_Place						= "place";
    
    //========================================================================
    //= Levenshtein Distance Thresholds.
    //= These constants define the maximum allowed edit distance thresholds for
    //= matching attempts using the levenshtein algorithm. Thresholds are expressed
    //= as the ratio EditDistance / StringLength. A Threshold of 0.25 says that
    // for a string length of 12 characters we allow a maximum edit distance of
    // 3. Specified value have been determined empirically.
    //========================================================================

    private static final double	c_LocalityMatchThreshold	= 0.25;
    private static final double	c_StreetMatchThreshold		= 0.25;
    
//***************************************************************************
//* Constructor(s)                                                          *
//***************************************************************************

//---------------------------------------------------------------------------
/**
 * The postConstruct method is an EJB life cycle callback method. The method
 * will be invoked after the class has been instantiated. In the scope of
 * the AdmissionBean class, we're going to prefetch a certain number of objects
 * from the GECAMed database in order to limit repeating database lookups for 
 * common objects. Prefetched objects will be kept in dedicated hashtables for 
 * faster lookup.
 */   
//---------------------------------------------------------------------------
       
//@PostConstruct
//public void postConstruct ()
public void start ()
    {
	Collection	<Insurance> 			l_Insurances;
	Iterator	<Insurance>				l_InsuranceIterator;
	
	Collection	<InsurancePlan> 		l_InsurancePlans;
	Iterator	<InsurancePlan>			l_InsurancePlanIterator;
	
	Collection	<HospitalisationClass> 	l_HospitalisationClasses;
	Iterator	<HospitalisationClass>	l_HospitalisationClassIterator;
	
	Collection	<HospitalDepartment> 	l_HospitalDepartments;
	Iterator	<HospitalDepartment>	l_HospitalDepartmentIterator;
	
	Insurance							l_Insurance;
	InsurancePlan						l_InsurancePlan;
	HospitalisationClass				l_HospitalisationClass;
	HospitalDepartment					l_HospitalDepartment;
	String								l_Key;
	
	try	{
		m_Luxembourg = this.getCountryByName("Luxembourg");
		
		//====================================================================
		//= Pretch List of available Health Insurances from GECAMed database
		//====================================================================
		
		if (m_Insurances == null)
			{
			m_Insurances = new Hashtable <String,Insurance> ();
			l_Insurances = this.getAllInsurances();
			if (l_Insurances != null)
				{
				l_InsuranceIterator = l_Insurances.iterator();
				while (l_InsuranceIterator.hasNext())
					{
					l_Insurance = l_InsuranceIterator.next();
					// TODO not get Acronym take the new HL7Acronym
					m_Insurances.put (l_Insurance.getHl7Acronym(),l_Insurance);
					}
				}
			}	
		//====================================================================
		//= Pretch List of available Insurance Plans from GECAMed database
		//====================================================================

		if (m_InsurancePlans == null)
			{
			m_InsurancePlans = new Hashtable <String,InsurancePlan> ();
			l_InsurancePlans = this.getAllInsurancePlans();
			if (l_InsurancePlans != null)
				{
				l_InsurancePlanIterator = l_InsurancePlans.iterator();
				while (l_InsurancePlanIterator.hasNext())
					{
					l_InsurancePlan = l_InsurancePlanIterator.next();
					l_Key = l_InsurancePlan.getCompany().getAcronym() + "-" +l_InsurancePlan.getName();
					m_InsurancePlans.put (l_Key,l_InsurancePlan);
					}		
				}
			}	
		
		//====================================================================
		//= Pretch List of available Hospitalisation Classes from GECAMed database
		//====================================================================

		if (m_HospitalisationClasses == null)
			{
			m_HospitalisationClasses = new Hashtable <String,HospitalisationClass> ();
			l_HospitalisationClasses = this.getAllHospitalisationClasses();
			if (l_HospitalisationClasses != null)
				{
				l_HospitalisationClassIterator = l_HospitalisationClasses.iterator();
				while (l_HospitalisationClassIterator.hasNext())
					{
					l_HospitalisationClass = l_HospitalisationClassIterator.next();
					m_HospitalisationClasses.put (l_HospitalisationClass.getAcronym(),l_HospitalisationClass);
					}		
				}
			}	
		
		//====================================================================
		//= Pretch List of available Hospital Departments from GECAMed database
		//====================================================================

		if (m_HospitalDepartments == null)
			{
			m_HospitalDepartments = new Hashtable <String,HospitalDepartment> ();
			l_HospitalDepartments = this.getAllHospitalDepartments();
			if (l_HospitalDepartments != null)
				{
				l_HospitalDepartmentIterator = l_HospitalDepartments.iterator();
				while (l_HospitalDepartmentIterator.hasNext())
					{
					l_HospitalDepartment = l_HospitalDepartmentIterator.next();
					m_HospitalDepartments.put (l_HospitalDepartment.getName(),l_HospitalDepartment);
					}		
				}
			}	
		
		this.log (Level.INFO, "AdmissionBean ready to accept requests!");
		}
	catch (Exception p_Exception)
		{
		this.log (Level.FATAL, "Failed to prefetch required Objects",p_Exception);
		}
	}

//---------------------------------------------------------------------------

public void stop ()
	{
	this.log (Level.INFO, "AdmissionBean stopped!");
	}

//---------------------------------------------------------------------------

//***************************************************************************
//* Class Primitives                                                        *
//***************************************************************************

//---------------------------------------------------------------------------

private void log (Level p_Level, String p_Message)
	{
	this.log(p_Level,p_Message,null);
	}

//---------------------------------------------------------------------------

private void log (Level p_Level, String p_Message, Exception p_Exception)
	{
	StackTraceElement[] l_StackTrace; 
	String				l_MethodName;
	                  
	l_StackTrace = new Throwable().getStackTrace();
	l_MethodName = l_StackTrace[1].getMethodName();
	
	if (l_MethodName.equals("log")) l_MethodName = l_StackTrace[2].getMethodName();
	p_Message = "\n" + l_MethodName + " => " + p_Message;
	
	if (p_Exception != null) m_Logger.log (p_Level,p_Message,p_Exception);
						else m_Logger.log (p_Level,p_Message);
	}

//---------------------------------------------------------------------------

private RuleInterface getRuleInterface ()
	{
	if (m_RuleInterface != null) return m_RuleInterface;

	try {
		m_RuleInterface = (RuleInterface) ManagerFactory.getRemote(RuleBean.class);
//		InitialContext l_Context = new InitialContext();
//		m_RuleInterface = (RuleInterface) l_Context.lookup("RuleBean/remote");
//		l_Context.close();
		} 
	catch (Exception p_Exception) 
		{
		this.log (Level.FATAL, "Failed to get RuleInterface",p_Exception);
		}

	return m_RuleInterface;
	}


//===========================================================================
//= HL7 Context Primitives											    	=
//===========================================================================
	
//---------------------------------------------------------------------------
/**
 * Returns the guarantor.The guarantor is the person or the organization with 
 * financial responsibility for payment of a patient account.
 * @param p_MessageId specifies the message ID in the HL7 import database
 * to get guarantor data for.
 * @return a HL7Guarantor object associated with the specified
 * message ID, <b>null</b> if none were found. 
 */
//---------------------------------------------------------------------------

private HL7Guarantor getGuarantorByMessageId (Integer p_MessageId) throws Exception 
	{
	HL7Guarantor l_Guarantor;

	try	{	
		l_Guarantor = (HL7Guarantor) m_GECAMedEntityManager.createNamedQuery ("getHL7GuarantorByMessageId")
													   .setParameter("messageid", p_MessageId)
													   .getSingleResult();
		}
	catch (NoResultException p_Exception)
		{
		l_Guarantor = null;
		}

	return l_Guarantor;
	}

//---------------------------------------------------------------------------
/**
 * Returns all HL7 Patient addresses for a given message ID.
 * @param p_MessageId specifies the message ID in the HL7 import database
 * to get addresses for.
 * @return a Collection of HL7Address objects associated with the specified
 * message ID, <b>null</b> if none were found. 
 */
//---------------------------------------------------------------------------

private Collection <HL7Address> getAddressesByMessageId (Integer p_MessageId) throws Exception 
	{
	Collection l_Addresses;

	try	{	
		l_Addresses = m_GECAMedEntityManager.createNamedQuery ("getHL7AddressesByMessageId")
									.setParameter("messageid", p_MessageId)
									.getResultList();
		}
	catch (NoResultException p_Exception)
		{
		l_Addresses = null;
		}

	return l_Addresses;
	}

//---------------------------------------------------------------------------
/**
 * Returns all HL7 Patient phone numbers for a given message ID.
 * @param p_MessageId specifies the message ID in the HL7 import database
 * to get phone numbers for.
 * @return a Collection of HL7Phone objects associated with the specified
 * message ID, <b>null</b> if none were found. 
 */
//---------------------------------------------------------------------------

private Collection <HL7Phone> getPhoneNumbersByMessageId (Integer p_MessageId) throws Exception 
	{
	Collection l_PhoneNumbers;

	try	{	
		l_PhoneNumbers = m_GECAMedEntityManager.createNamedQuery ("getHL7PhonesByMessageId")
									.setParameter("messageid", p_MessageId)
									.getResultList();
		}
	catch (NoResultException p_Exception)
		{
		l_PhoneNumbers = null;
		}

	return l_PhoneNumbers;
	}

//---------------------------------------------------------------------------
/**
 * Returns all HL7 Patient Health Insurance data for a given message ID.
 * @param p_MessageId specifies the message ID in the HL7 import database
 * to get health insurance data for.
 * @return a Collection of HL7Insurance objects associated with the specified
 * message ID, <b>null</b> if none were found. 
 */
//---------------------------------------------------------------------------

private Collection <HL7Insurance> getInsurancesByMessageId (Integer p_MessageId) throws Exception 
	{
	Collection l_Insurances;

	try	{	
		l_Insurances = m_GECAMedEntityManager.createNamedQuery ("getHL7InsurancesByMessageId")
									.setParameter("messageid", p_MessageId)
									.getResultList();
		}
	catch (NoResultException p_Exception)
		{
		l_Insurances = null;
		}

	return l_Insurances;
	}

//---------------------------------------------------------------------------
/**
 * Rejects the specified HL7Admission by setting its rejected flag.
 * @param p_Admission specifies the HL7 Admission object to be rejected
 */
//---------------------------------------------------------------------------

private HL7Admission quarantineHL7Admission (HL7Admission p_Admission)
	{
	if (p_Admission == null) return null;
	
	p_Admission.setQuarantined(Boolean.TRUE);
	p_Admission = m_GECAMedEntityManager.merge(p_Admission);
		
	return p_Admission;
	}

//---------------------------------------------------------------------------
/**
 * Deletes the specified HL7Admission object from the HL7 import database.
 * @param p_Admission specifies the HL7 Admission object to be deleted from
 * database
 */
//---------------------------------------------------------------------------

private void deleteHL7Admission (HL7Admission p_Admission)
	{
	if (p_Admission == null) return;
	
	p_Admission = m_GECAMedEntityManager.find (HL7Admission.class, p_Admission.getId());
	if ((p_Admission != null) && (p_Admission.getQuarantined() == false))
		m_GECAMedEntityManager.remove(p_Admission);
	}

//---------------------------------------------------------------------------
/**
 * Rejects the specified HL7Address by setting its rejected flag.
 * @param p_Address specifies the HL7Address object to be rejected
 */
//---------------------------------------------------------------------------

private HL7Address quarantineHL7Address (HL7Address p_Address)
	{
	if (p_Address == null) return null;
	
	p_Address.setQuarantined(Boolean.TRUE);
	p_Address = m_GECAMedEntityManager.merge(p_Address);
		
	return p_Address;
	}

//---------------------------------------------------------------------------
/**
 * Deletes the specified HL7Address object from the HL7 import database.
 * @param p_Address specifies the HL7 Address object to be deleted from
 * database
 */
//---------------------------------------------------------------------------

private void deleteHL7Address (HL7Address p_Address)
	{
	if (p_Address == null) return;
	
	p_Address = m_GECAMedEntityManager.find (HL7Address.class, p_Address.getId());
	if ((p_Address != null) && (p_Address.getQuarantined() == false)) 
		m_GECAMedEntityManager.remove(p_Address);
	}

//---------------------------------------------------------------------------
/**
 * Rejects the specified HL7Phone by setting its rejected flag.
 * @param p_Phone specifies the HL7Phone object to be rejected
 */
//---------------------------------------------------------------------------

private HL7Phone quarantineHL7Phone (HL7Phone p_Phone)
	{
	if (p_Phone == null) return null;
	
	p_Phone.setQuarantined(Boolean.TRUE);
	p_Phone = m_GECAMedEntityManager.merge(p_Phone);
		
	return p_Phone;
	}

//---------------------------------------------------------------------------
/**
 * Deletes the specified HL7Phone object from the HL7 import database.
 * @param p_Phone specifies the HL7 Phone object to be deleted from
 * database
 */
//---------------------------------------------------------------------------

private void deleteHL7Phone (HL7Phone p_Phone)
	{
	if (p_Phone == null) return;
	
	p_Phone = m_GECAMedEntityManager.find (HL7Phone.class, p_Phone.getId());
	if ((p_Phone != null) && (p_Phone.getQuarantined() == false))
		m_GECAMedEntityManager.remove(p_Phone);
	}

//---------------------------------------------------------------------------
/**
 * Rejects the specified HL7Admission by setting its rejected flag.
 * @param p_Admission specifies the HL7 Admission object to be rejected
 */
//---------------------------------------------------------------------------

private HL7Insurance quarantineHL7Insurance (HL7Insurance p_Insurance)
	{
	if (p_Insurance == null) return null;
	
	p_Insurance.setQuarantined(Boolean.TRUE);
	p_Insurance = m_GECAMedEntityManager.merge(p_Insurance);
		
	return p_Insurance;
	}

//---------------------------------------------------------------------------
/**
 * Deletes the specified HL7Insurance object from the HL7 import database.
 * @param p_Insurance specifies the HL7 Insurance object to be deleted from
 * database
 */
//---------------------------------------------------------------------------

private void deleteHL7Insurance (HL7Insurance p_Insurance)
	{
	if (p_Insurance == null) return;
	
	p_Insurance = m_GECAMedEntityManager.find (HL7Insurance.class, p_Insurance.getId());
	if ((p_Insurance != null) && (p_Insurance.getQuarantined() == false))
		m_GECAMedEntityManager.remove(p_Insurance);
	}

//---------------------------------------------------------------------------
/**
 * Rejects the specified HL7Admission by setting its rejected flag.
 * @param p_Admission specifies the HL7 Admission object to be rejected
 */
//---------------------------------------------------------------------------

private HL7Guarantor quarantineHL7Guarantor (HL7Guarantor p_Guarantor)
	{
	if (p_Guarantor == null) return null;
	
	p_Guarantor.setQuarantined(Boolean.TRUE);
	p_Guarantor = m_GECAMedEntityManager.merge(p_Guarantor);
		
	return p_Guarantor;
	}

//---------------------------------------------------------------------------
/**
 * Deletes the specified HL7Guarantor object from the HL7 import database.
 * @param p_Guarantor specifies the HL7 Guarantor object to be deleted from
 * database
 */
//---------------------------------------------------------------------------

private void deleteHL7Guarantor (HL7Guarantor p_Guarantor)
	{
	if (p_Guarantor == null) return;
	
	p_Guarantor = m_GECAMedEntityManager.find (HL7Guarantor.class, p_Guarantor.getId());
	if ((p_Guarantor != null) && (p_Guarantor.getQuarantined() == false))
		m_GECAMedEntityManager.remove(p_Guarantor);
	}

//---------------------------------------------------------------------------

@SuppressWarnings("unchecked")
private void deleteReferences (HL7Admission p_Admission)
	{
	Collection <HL7Address> 	l_Addresses;
	Iterator <HL7Address> 		l_AddressIterator;
	HL7Address					l_Address;
	
	Collection <HL7Phone>   	l_Phones;
	Iterator   <HL7Phone>   	l_PhoneIterator;
	HL7Phone					l_Phone;
	
	Collection <HL7Insurance> 	l_Insurances;
	Iterator <HL7Insurance> 	l_InsuranceIterator;
	HL7Insurance				l_Insurance;
	
	Collection <HL7Guarantor> 	l_Guarantors;
	Iterator <HL7Guarantor> 	l_GuarantorIterator;
 	HL7Guarantor				l_Guarantor;
	
	
	
	
	if (p_Admission == null) return;

	try	{	
//		m_GECAMedEntityManager.createNamedQuery ("deleteHL7AddressesByMessageId")
//		  					.setParameter("messageid", p_Admission.getMessageId())
//		  					.executeUpdate();
//	
//		m_GECAMedEntityManager.createNamedQuery ("deleteHL7PhonesByMessageId")
//		  					.setParameter("messageid", p_Admission.getMessageId())
//		  					.executeUpdate();
//	
//		m_GECAMedEntityManager.createNamedQuery ("deleteHL7InsurancesByMessageId")
//		  					.setParameter("messageid", p_Admission.getMessageId())
//		  					.executeUpdate();
//	
//		m_GECAMedEntityManager.createNamedQuery ("deleteHL7GuarantorsByMessageId")
//		  					.setParameter("messageid", p_Admission.getMessageId())
//		  					.executeUpdate();
		
		//====================================================================
		//= Delete orphaned HL7Address objects
		//====================================================================
		
		l_Addresses = m_GECAMedEntityManager.createNamedQuery ("getHL7AddressesByMessageId")
										.setParameter("messageid", p_Admission.getMessageId())
										.getResultList();
		
		if (l_Addresses != null)
			{
			l_AddressIterator = l_Addresses.iterator();
			while (l_AddressIterator.hasNext())
				{
				l_Address = l_AddressIterator.next();
				this.deleteHL7Address (l_Address);
				}	
			}

		//====================================================================
		//= Delete orphaned HL7Phone objects
		//====================================================================
		
		l_Phones = m_GECAMedEntityManager.createNamedQuery ("getHL7PhonesByMessageId")
										.setParameter("messageid", p_Admission.getMessageId())
										.getResultList();
		
		if (l_Phones != null)
			{
			l_PhoneIterator = l_Phones.iterator();
			while (l_PhoneIterator.hasNext())
				{
				l_Phone = l_PhoneIterator.next();
				this.deleteHL7Phone (l_Phone);
				}	
			}

		//====================================================================
		//= Delete orphaned HL7Insurance objects
		//====================================================================
		
		l_Insurances = m_GECAMedEntityManager.createNamedQuery ("getHL7InsurancesByMessageId")
										.setParameter("messageid", p_Admission.getMessageId())
										.getResultList();
		
		if (l_Insurances != null)
			{
			l_InsuranceIterator = l_Insurances.iterator();
			while (l_InsuranceIterator.hasNext())
				{
				l_Insurance = l_InsuranceIterator.next();
				this.deleteHL7Insurance (l_Insurance);
				}	
			}

		//====================================================================
		//= Delete orphaned HL7Guarantor objects
		//====================================================================
		
		l_Guarantors = m_GECAMedEntityManager.createNamedQuery ("getHL7GuarantorByMessageId")
										.setParameter("messageid", p_Admission.getMessageId())
										.getResultList();
		
		if (l_Guarantors != null)
			{
			l_GuarantorIterator = l_Guarantors.iterator();
			while (l_GuarantorIterator.hasNext())
				{
				l_Guarantor = l_GuarantorIterator.next();
				this.deleteHL7Guarantor (l_Guarantor);
				}	
			}
	
		
		}
	catch (Exception p_Exception)
		{
		this.log (Level.FATAL, "Failed to delete objects referenced by Admission with Message ID " + p_Admission.getMessageId(),p_Exception);
		}
	}

//---------------------------------------------------------------------------

@SuppressWarnings("unchecked")
private void quarantineReferences (HL7Admission p_Admission)
	{
	Collection <HL7Address> 	l_Addresses;
	Iterator <HL7Address> 		l_AddressIterator;
	HL7Address					l_Address;
	
	Collection <HL7Phone>   	l_Phones;
	Iterator   <HL7Phone>   	l_PhoneIterator;
	HL7Phone					l_Phone;
	
	Collection <HL7Insurance> 	l_Insurances;
	Iterator <HL7Insurance> 	l_InsuranceIterator;
	HL7Insurance				l_Insurance;
	
	Collection <HL7Guarantor> 	l_Guarantors;
	Iterator <HL7Guarantor> 	l_GuarantorIterator;
 	HL7Guarantor				l_Guarantor;
	
	if (p_Admission == null) return;

	try	{	
		
		//====================================================================
		//= Reject HL7Address objects
		//====================================================================
		
		l_Addresses = m_GECAMedEntityManager.createNamedQuery ("getHL7AddressesByMessageId")
										.setParameter("messageid", p_Admission.getMessageId())
										.getResultList();
		
		if (l_Addresses != null)
			{
			l_AddressIterator = l_Addresses.iterator();
			while (l_AddressIterator.hasNext())
				{
				l_Address = l_AddressIterator.next();
				this.quarantineHL7Address (l_Address);
				}	
			}

		//====================================================================
		//= Delete orphaned HL7Phone objects
		//====================================================================
		
		l_Phones = m_GECAMedEntityManager.createNamedQuery ("getHL7PhonesByMessageId")
										.setParameter("messageid", p_Admission.getMessageId())
										.getResultList();
		
		if (l_Phones != null)
			{
			l_PhoneIterator = l_Phones.iterator();
			while (l_PhoneIterator.hasNext())
				{
				l_Phone = l_PhoneIterator.next();
				this.quarantineHL7Phone (l_Phone);
				}	
			}

		//====================================================================
		//= Delete orphaned HL7Insurance objects
		//====================================================================
		
		l_Insurances = m_GECAMedEntityManager.createNamedQuery ("getHL7InsurancesByMessageId")
										.setParameter("messageid", p_Admission.getMessageId())
										.getResultList();
		
		if (l_Insurances != null)
			{
			l_InsuranceIterator = l_Insurances.iterator();
			while (l_InsuranceIterator.hasNext())
				{
				l_Insurance = l_InsuranceIterator.next();
				this.quarantineHL7Insurance (l_Insurance);
				}	
			}

		//====================================================================
		//= Delete orphaned HL7Guarantor objects
		//====================================================================
		
		l_Guarantors = m_GECAMedEntityManager.createNamedQuery ("getHL7GuarantorByMessageId")
										.setParameter("messageid", p_Admission.getMessageId())
										.getResultList();
		
		if (l_Guarantors != null)
			{
			l_GuarantorIterator = l_Guarantors.iterator();
			while (l_GuarantorIterator.hasNext())
				{
				l_Guarantor = l_GuarantorIterator.next();
				this.quarantineHL7Guarantor (l_Guarantor);
				}	
			}
	
		
		}
	catch (Exception p_Exception)
		{
		this.log (Level.FATAL, "Failed to quarantining objects referenced by Admission with Message ID " + p_Admission.getMessageId(),p_Exception);
		}
	}


//===========================================================================
//= GECAMed Context Primitives												=
//===========================================================================

//---------------------------------------------------------------------------

private Collection <Insurance> getAllInsurances () throws Exception
	{
	Collection	l_Insurances;
	
	try	{	
		l_Insurances = m_GECAMedEntityManager.createNamedQuery ("getAllInsurances")
									    .getResultList();
		}
	catch (NoResultException p_Exception)
		{
		l_Insurances = null;
		}

	return l_Insurances;
	}

//---------------------------------------------------------------------------

private Collection <InsurancePlan> getAllInsurancePlans () throws Exception
	{
	Collection	l_InsurancePlans;
	
	try	{	
		l_InsurancePlans = m_GECAMedEntityManager.createNamedQuery ("getAllInsurancePlans")
									    .getResultList();
		}
	catch (NoResultException p_Exception)
		{
		l_InsurancePlans = null;
		}

	return l_InsurancePlans;
	}

//---------------------------------------------------------------------------

private Collection <HospitalisationClass> getAllHospitalisationClasses () throws Exception
	{
	Collection	l_Classes;
	
	try	{	
		l_Classes = m_GECAMedEntityManager.createNamedQuery ("getAllHospitalisationClasses")
									    .getResultList();
		}
	catch (NoResultException p_Exception)
		{
		l_Classes = null;
		}

	return l_Classes;
	}

//---------------------------------------------------------------------------

private Collection <HospitalDepartment> getAllHospitalDepartments() throws Exception
	{
	Collection	l_Departments;
	
	try	{	
		l_Departments = m_GECAMedEntityManager.createNamedQuery ("getAllHospitalDepartments")
									    		.getResultList();
		}
	catch (NoResultException p_Exception)
		{
		l_Departments = null;
		}

	return l_Departments;
	}

//---------------------------------------------------------------------------

private Patient getPatientByRISid (String p_RISid) throws Exception 
	{	
	Patient l_Patient;

	try	{
		l_Patient = (Patient) m_GECAMedEntityManager.createNamedQuery (Patient.FIND_PATIENT_BY_RISID)
   		.setParameter("risID", p_RISid)
   		.setMaxResults(1)
   		.getSingleResult();
		}
	catch (NoResultException p_Exception)
		{
		l_Patient = null;
		}

	return l_Patient;
	}

//---------------------------------------------------------------------------

private PatientContact getContactForPatient (Patient p_Patient) throws Exception 
	{	
	PatientContact l_Contact = null;

	if ((p_Patient != null) && (p_Patient.isPersistent()))
		{
		try	{
			l_Contact = (PatientContact) m_GECAMedEntityManager.createNamedQuery ("findAllPatientContactByPatientId")
															   .setParameter("patientId", p_Patient.getId())
															   .setMaxResults(1)
															   .getSingleResult();
			}
		catch (NoResultException p_Exception)
			{
			l_Contact = null;
			}
		}
		
	return l_Contact;
	}

//---------------------------------------------------------------------------

private Country getCountryByName (String p_Name) throws Exception
	{
	Country l_Country;

	try	{
		l_Country = (Country) m_GECAMedEntityManager.createNamedQuery ("getCountryByName")
   							.setParameter("name", p_Name)
   							.getSingleResult();
  		}
	catch (NoResultException p_Exception)
		{
		l_Country = null;
		}

	return l_Country;
	}

//---------------------------------------------------------------------------

private Collection <Locality> getLocalitiesStartingWith (String p_FirstCharacters)
	{
	String		l_Query;
	Collection 	l_Localities = null;
	
	try	{
		p_FirstCharacters = p_FirstCharacters + "%";
		
		l_Query =	"SELECT OBJECT(o) FROM Locality o" +
	      		 	" WHERE lower(o.name) LIKE lower(:name) " +
	      		 	" OR lower(o.luxembourgishName) LIKE lower(:name)";

		l_Localities = m_GECAMedEntityManager.createQuery (l_Query)
										   .setParameter("name", p_FirstCharacters)
										   .getResultList();
		}
	catch (NoResultException p_Exception)
		{
		l_Localities = null;
		}
	
	return l_Localities;
	}

//---------------------------------------------------------------------------

private Collection <Locality> getLocalitiesByName (String p_Name)
	{
	String		l_Query;
	Collection 	l_Localities = null;
	
	try	{
		l_Query = "SELECT OBJECT(o) FROM Locality o" +
			      " WHERE lower(o.name) = lower(:name) " +
			      " OR lower(o.luxembourgishName) = lower(:name)";
		
		l_Localities = m_GECAMedEntityManager.createQuery (l_Query)
    											.setParameter("name", p_Name)
    											.getResultList();
		}
	catch (NoResultException p_Exception)
		{
		l_Localities = null;
		}
	
	return l_Localities;
	}

//---------------------------------------------------------------------------

private Collection <Zip> getZipsByLocality (Integer p_LocalityId)
	{
	Collection 	l_Zips = null;
	
	try	{
		l_Zips = m_GECAMedEntityManager.createNamedQuery ("findAllZipByLocality")
   				.setParameter("localityId",  p_LocalityId)
   				.getResultList();
		}
	catch (NoResultException p_Exception)
		{
		l_Zips = null;
		}
	
	return l_Zips;
	}

//---------------------------------------------------------------------------

private Collection <Zip> getZipsByStreetAndLocality (String p_Street, Integer p_LocalityId)
	{
	Collection 	l_Zips = null;
	
	try	{
		l_Zips = m_GECAMedEntityManager.createNamedQuery ("findAllZipByStreetAndLocality")
   				.setParameter("street",      p_Street)
   				.setParameter("localityId",  p_LocalityId)
   				.getResultList();
		}
	catch (NoResultException p_Exception)
		{
		l_Zips = null;
		}
	
	return l_Zips;
	}

//---------------------------------------------------------------------------

//private Collection <Zip> getZipsByCode (Integer p_Code) throws Exception
//	{
//	Collection 	l_Zips		= null;
//	
//	try	{
//		l_Zips = m_GECAMedEntityManager.createNamedQuery ("findAllZipByZip")
//   				.setParameter("zip", p_Code)
//   				.getResultList();
//		}
//	catch (NoResultException p_Exception)
//		{
//		l_Zips = null;
//		}
//	
//	return l_Zips;
//	}

//---------------------------------------------------------------------------

//private Locality	getLocalityById	(Integer p_LocalityId) throws Exception
//	{
//	Locality l_Locality;
//
//	try	{
//		l_Locality = (Locality) m_GECAMedEntityManager.find(Locality.class, p_LocalityId);
//  		}
//	catch (NoResultException p_Exception)
//		{
//		l_Locality = null;
//		}
//
//	return l_Locality;
//	}

//---------------------------------------------------------------------------

private Hospitalisation getHospitalisationByPassageId (String p_PassageId) throws Exception
	{
	Hospitalisation	l_Hospitalisation;
	
	try	{
		l_Hospitalisation = (Hospitalisation) m_GECAMedEntityManager.createNamedQuery ("getHospitalisationByPassageId")
											.setParameter("passageid", p_PassageId)
											.getSingleResult();
  		}
	catch (NoResultException p_Exception)
		{
		l_Hospitalisation = null;
		}
	
	return l_Hospitalisation;
	}

//---------------------------------------------------------------------------

@SuppressWarnings("unchecked")
private Collection <Invoice> getInvoicesByHospitalisation (Hospitalisation p_Hospitalisation) throws Exception
	{
	Collection<Invoice>	l_Invoices;
	
	if (p_Hospitalisation == null) return null;
	
	try	{	
		l_Invoices = m_GECAMedEntityManager.createNamedQuery ("getInvoicesByHospitalisation")
											.setParameter("hospitalisation", p_Hospitalisation)
									    	.getResultList();
		}
	catch (NoResultException p_Exception)
		{
		l_Invoices = null;
		}

	return l_Invoices;
	}

//---------------------------------------------------------------------------

private void deleteAddresses (Collection <PatientAddress> p_Addresses)
	{
	Iterator	<PatientAddress> 	l_AddressIterator;
	PatientAddress				l_Address = null;

	if (p_Addresses == null) return;
	
	try	{
		l_AddressIterator = p_Addresses.iterator();
		while (l_AddressIterator.hasNext())
			{
			l_Address = l_AddressIterator.next();
			m_GECAMedEntityManager.remove(l_Address);
			l_AddressIterator.remove();
			}
		}
	catch (Exception p_Exception)
		{
		if ((l_Address != null) && (l_Address.getId() != null))
			this.log (Level.FATAL, "Failed to delete PatientAddress object with Id " + l_Address.getId());
			this.log (Level.FATAL, "Exception while deleting PatientAddress object");	
		}
	}

//---------------------------------------------------------------------------

private void deletePhoneNumbers (Collection <PatientPhone> p_PhoneNumbers)
	{
	Iterator	<PatientPhone> 	l_PhoneNumberIterator;
	PatientPhone				l_PhoneNumber = null;

	if (p_PhoneNumbers == null) return;
	
	try	{
		l_PhoneNumberIterator = p_PhoneNumbers.iterator();
		while (l_PhoneNumberIterator.hasNext())
			{
			l_PhoneNumber = l_PhoneNumberIterator.next();
			m_GECAMedEntityManager.remove(l_PhoneNumber);
			l_PhoneNumberIterator.remove();
			}
		}
	catch (Exception p_Exception)
		{
		if ((l_PhoneNumber != null) && (l_PhoneNumber.getId() != null))
			this.log (Level.FATAL, "Failed to delete PatientPhone object with Id " + l_PhoneNumber.getId());
			this.log (Level.FATAL, "Exception while deleting PatientPhone object");	
		}
	}

//---------------------------------------------------------------------------

private void deleteHospitalisationPeriod (HospitalisationPeriod p_Period)
	{
	if (p_Period == null) return;
	
	try	{
		m_GECAMedEntityManager.remove(p_Period);
		}
	catch (Exception p_Exception)
		{
		if ((p_Period != null) && (p_Period.getId() != null))
			this.log (Level.FATAL, "Failed to delete HospitalisationPeriod object with Id " + p_Period.getId());
			this.log (Level.FATAL, "Exception while deleting HospitalisationPeriod object");	
		}
	}

//---------------------------------------------------------------------------

private HospitalDepartment saveHospitalDepartment (HospitalDepartment p_Department) throws Exception
	{
	if (p_Department == null) return null;
	
	p_Department= m_GECAMedEntityManager.merge(p_Department);

	return p_Department;
	}

//---------------------------------------------------------------------------

private Hospitalisation saveHospitalisation (Hospitalisation p_Hospitalisation) throws Exception
	{
	if (p_Hospitalisation == null) return null;
	
	p_Hospitalisation = m_GECAMedEntityManager.merge(p_Hospitalisation);
		
	return p_Hospitalisation;
	}

//---------------------------------------------------------------------------

private PatientContact saveContact (PatientContact p_Contact) throws Exception
	{
	if (p_Contact == null) return null;
	
	long start = System.currentTimeMillis();
	
	p_Contact = m_GECAMedEntityManager.merge(p_Contact);
	
	try {
		log(Level.INFO, "Saved Contact: " + p_Contact);
		Log logEntry = new Log(LogType.SYSTEM, "SAVE PATIENT", "HL7", "Saved Contact: " + p_Contact, System.currentTimeMillis()-start);
		logManager.saveLog(logEntry);
	} catch (Exception e) {
	}

	return p_Contact;
	}

//---------------------------------------------------------------------------

private Patient savePatient (Patient p_Patient) throws Exception
	{
	if (p_Patient == null) return null;
	
	long start = System.currentTimeMillis();
	
	p_Patient = m_GECAMedEntityManager.merge(p_Patient);

	try {
		log(Level.INFO, "Saved Patient: " + p_Patient.toLogString());
		
		Log logEntry = new Log(LogType.SYSTEM, "SAVE PATIENT", "HL7", "Saved Patient " + p_Patient.toLogString(), System.currentTimeMillis()-start);
		logManager.saveLog(logEntry);
	} catch (Exception e) {
	}
	
	return p_Patient;
	}

//---------------------------------------------------------------------------

private Invoice saveInvoice (Invoice p_Invoice) throws Exception
	{	
	if (p_Invoice == null) return null;

	long start = System.currentTimeMillis();
	
	p_Invoice.monetize();
		
	p_Invoice = m_GECAMedEntityManager.merge (p_Invoice);
	p_Invoice.updateFirstClassRequired();
	
	try {
		log(Level.INFO, "Saved Invoice: " + p_Invoice);
		
		Log logEntry = new Log(LogType.SYSTEM, "SAVE INVOICE", "HL7", "Saved Invoice " + p_Invoice, System.currentTimeMillis()-start);
		logManager.saveLog(logEntry);
	} catch (Exception e) {
	}
	
	return p_Invoice;
	}

//===========================================================================
//= General Primitives												    	=
//===========================================================================

//---------------------------------------------------------------------------

private String stringifyHL7Address (HL7Address p_Address)
	{
	StringBuffer	l_Address;
	
	l_Address = new StringBuffer ();
	
	if (p_Address != null)
		{
		l_Address.append ((p_Address.getStreet() != null)?p_Address.getStreet() + " ":"");
		l_Address.append ((p_Address.getZip() != null)?p_Address.getZip() + " ":"");
		l_Address.append ((p_Address.getCity() != null)?p_Address.getCity() + " ":"");
		l_Address.append ((p_Address.getCountry() != null)?p_Address.getCountry() + " ":"");
		}
	return l_Address.toString();	
	}

//---------------------------------------------------------------------------

private String stringifyAddress (PatientAddress p_Address)
	{
	StringBuffer	l_Address;
	
	l_Address = new StringBuffer ();
	
	if (p_Address != null)
		{
		l_Address.append ((p_Address.getStreetNumber() != null)?p_Address.getStreetNumber() + ", ":"");
		l_Address.append ((p_Address.getStreetName() != null)?p_Address.getStreetName() + " ":"");
		l_Address.append ((p_Address.getZip() != null)?p_Address.getZip() + " ":"");
		l_Address.append ((p_Address.getLocality() != null)?p_Address.getLocality() + " ":"");
		l_Address.append ((p_Address.getCountry() != null)?p_Address.getCountry() + " ":"");
		}
	return l_Address.toString();	
	}

//---------------------------------------------------------------------------
/**
 * The scrubName method extracts the name relevant fragments from the specified
 * String while ensuring that total length of scrubbed name does not exceed
 * specified maximum length.
 * @param p_Name specifies the raw name to be scrubbed.
 * @param p_MaxLength specifies the maximum allowed length of the scrubbed name.
 * @return the scrubbed name, that is a string containing only the name relevant
 * fragments of the specified name with a length inferior or equal to MaxLength.
 */
//---------------------------------------------------------------------------

private String scrubName (String p_Name, int p_MaxLength)
	{
	Matcher			l_NameScrubber;
	StringBuffer	l_ScrubbedName;
	String			l_Fragment;
	String			l_Separator;
	boolean			l_MaxLengthReached;
	
	l_ScrubbedName 		= new StringBuffer ();
	
	l_Separator			= "";
	l_MaxLengthReached	= false;
	
	l_NameScrubber = c_NamePattern.matcher(p_Name);
	l_NameScrubber.reset();
	
	while (l_NameScrubber.find() && !l_MaxLengthReached)
		{
		l_Fragment = l_NameScrubber.group(1);
		
		if (l_ScrubbedName.length() + l_Fragment.length() < p_MaxLength)
			{
			l_ScrubbedName.append(l_Separator);
			l_ScrubbedName.append(l_Fragment);
			}
		else l_MaxLengthReached = true;
		
		l_Separator = c_Space;
		}
//	// org
//	if (l_ScrubbedName.length() == 0)
//		l_ScrubbedName.append(p_Name.substring(0, p_MaxLength));
	// -->
	if (l_ScrubbedName.length() == 0)
	{
		if(p_Name.length() <= p_MaxLength)
			l_ScrubbedName.append(p_Name);
		else
			l_ScrubbedName.append(p_Name.substring(0, p_MaxLength));
	}
	
	return l_ScrubbedName.toString().trim();
	}

//---------------------------------------------------------------------------
/**
 * The scrubZip methods extracts the numeric part of a character based Zip code.
 * A regular expression matching only the last 4 digits will be used for this
 * purpose.
 * @param  p_ZipCode specifies the string based ZIP code to be scrubbed.
 * @return An Integer whose value was set using the last 4 digits of the specified
 * zip code. If regex could not match specified ZIP code, <b>0</b> will be returned.     
 */
//---------------------------------------------------------------------------

private Integer scrubZip (String p_ZipCode)
	{
	Matcher	l_ZipMatcher;
	Integer	l_Zip;
	
	if (p_ZipCode == null) return 0;
	
	l_ZipMatcher = c_HL7AddressZipPattern.matcher(p_ZipCode);
	if (l_ZipMatcher.matches())
		{
		try	{
			l_Zip = Integer.valueOf (l_ZipMatcher.group(1));
			}
		catch (NumberFormatException p_Exception)
			{
			l_Zip = Integer.valueOf (0);
			}
		}
	else l_Zip = Integer.valueOf (0);
	
	return l_Zip;
	}

//---------------------------------------------------------------------------

private String replaceMatch (String p_Original, Matcher p_Matcher, String p_Replacement)
	{
	StringBuffer	l_Replaced;
		
	l_Replaced = new StringBuffer (p_Original.substring(0, p_Matcher.start(1)));
	l_Replaced.append(p_Replacement);
	l_Replaced.append(p_Original.substring(p_Matcher.end(1)));
	
	return l_Replaced.toString();
	}

//---------------------------------------------------------------------------
/**
 * The method looks for common abbreviations in street names and expands them.
 * Doing so ensures better matching of street names with names in the database.
 * @param p_Streetname specifies the street name with abbreviations.
 * @return specified street name havings its abbreviations expanded.
 */
//---------------------------------------------------------------------------

private String expandAbbreviations (String p_Streetname)
	{
	Matcher l_Matcher;	
	String	l_ExpandedStreet;	
	
	l_Matcher = c_AvenuePattern.matcher (p_Streetname);
	if (l_Matcher.lookingAt()) 
		{
		l_ExpandedStreet = this.replaceMatch (p_Streetname, l_Matcher, c_Avenue);
		this.log(Level.INFO, "Expanded Streetname is " + l_ExpandedStreet);			
		return l_ExpandedStreet;
		}
		
	l_Matcher = c_BoulevardPattern.matcher (p_Streetname);
	if (l_Matcher.lookingAt()) 
		{
		l_ExpandedStreet = this.replaceMatch (p_Streetname, l_Matcher, c_Boulevard);
		this.log(Level.INFO, "Expanded Streetname is " + l_ExpandedStreet);			
		return l_ExpandedStreet;
		}
	
	l_Matcher = c_PlacePattern.matcher (p_Streetname);
	if (l_Matcher.lookingAt()) 
		{
		l_ExpandedStreet = this.replaceMatch (p_Streetname, l_Matcher, c_Place);
		this.log(Level.INFO, "Expanded Streetname is " + l_ExpandedStreet);			
		return l_ExpandedStreet;
		}

	return p_Streetname;
	}

//---------------------------------------------------------------------------
/**
 * Given a list of possible candidates, the getBestMatchingLocality attempts
 * to find among these candidates the locality whose name best matches the
 * specified name. The Levenshtein Edit Distance algorithm is used to determine
 * the best match. The c_LocalityMatchThreshold constant defines the matching
 * threshold. Please see definition of constant for more information about
 * threshold value.
 * @param p_Candidates specifies a collection of localities to look for a match
 * @param p_LocalityName specifies the name of the locality to match against
 * @result returns the best matching Locality object if a match was found,
 * <b>null</b> otherwise.
 */
//---------------------------------------------------------------------------

private Locality getBestMatchingLocality (Collection <Locality> p_Candidates, String p_LocalityName)
	{
	Iterator <Locality>	l_LocalityIterator;
	Locality			l_Locality;
	Locality			l_BestMatch;
	String				l_CandidateName;
	Matcher				l_ComposedName;
	int					l_ShortestDistance;
	int					l_EditDistance;
	double				l_EditLengthRatio;
	
	l_ShortestDistance 	= p_LocalityName.length();
	l_BestMatch			= null;
	
	p_LocalityName = p_LocalityName.toLowerCase();
	l_LocalityIterator = p_Candidates.iterator();
	
	while (l_LocalityIterator.hasNext())
		{
		//========================================================================
		//= Attempt 1 : First of all we're going to compare the name of the current
		//= current candidate with the specified locality name.
		//========================================================================
		
		l_Locality = l_LocalityIterator.next();
		l_CandidateName = l_Locality.getName().toLowerCase();
		l_EditDistance = StringUtilities.getLevenshteinDistance (l_CandidateName,p_LocalityName);

		if ( l_EditDistance < l_ShortestDistance)
			{
			l_ShortestDistance 	= l_EditDistance;
			l_BestMatch 			= l_Locality;
			}
		
		//========================================================================
		//= Attempt 2 : Some (luxembourgish) localities have composed names,e.g
		//= Esch-sur-Alzette, Boevange-sur-Attert. In attempt 2 we're going to 
		//= to compare shorter versions of those names (by replacing the -sur- part
		//= with the quiet common /) with the specified locality name.
		//========================================================================
		
		l_ComposedName = c_ComposedLocalityPattern.matcher(l_CandidateName);
		if (l_ComposedName.matches())
			{
			l_CandidateName = l_ComposedName.group(1) + "/" + l_ComposedName.group(2);
			l_EditDistance = StringUtilities.getLevenshteinDistance (l_CandidateName,p_LocalityName);

			if ( l_EditDistance < l_ShortestDistance)
				{
				l_ShortestDistance 	= l_EditDistance;
				l_BestMatch 			= l_Locality;
				}
			}

		//========================================================================
		//= Attempt 3 : Last but not least, we're comparing the specified locality
		//= name with the luxembourgish name of the current locality
		//========================================================================

		l_CandidateName = l_Locality.getLuxembourgishName().toLowerCase();
		l_EditDistance = StringUtilities.getLevenshteinDistance (l_CandidateName,p_LocalityName);

		if ( l_EditDistance < l_ShortestDistance)
			{
			l_ShortestDistance 	= l_EditDistance;
			l_BestMatch 			= l_Locality;
			}
		}
	
	//========================================================================
	//= Compute the resulting edit length ratio using the determined edit
	//= distance of best match and the string length of the specified Locality
	//= name. Edit Length ratio is computed by dividing edit distance of best
	//= match by string length of specified locality name. Only if the computed
	//= edit length ratio is lower or equal to threshold will the match be
	//= accepted.
	//========================================================================

	if (p_LocalityName.length() > 0)
		 l_EditLengthRatio = ((double)l_ShortestDistance/(double)p_LocalityName.length());
	else l_EditLengthRatio = 0;

	if ((l_BestMatch != null) && (l_EditLengthRatio <= c_LocalityMatchThreshold)) 
		{
		this.log (Level.INFO, "Match " + l_BestMatch.getName() + " to " + p_LocalityName);
		return l_BestMatch;
		}
	else 
		{
		this.log (Level.INFO, "No satisfactory match for " + p_LocalityName);
		return null;
		}
	}

//---------------------------------------------------------------------------
/**
 * Given a list of possible candidates, the getBestMatchingStreet attempts
 * to find among these candidates the strett whose name best matches the
 * specified name. The Levenshtein Edit Distance algorithm is used to determine
 * the best match. The c_StreetMatchThreshold constant defines the matching
 * threshold. Please see definition of constant for more information about
 * threshold value. The p_OnlyEndings flag allows to specify whether the full
 * street name will be used for matching or only the last word. Looking for a
 * best match with p_OnlyEndings flag being set relaxes matching constraints.
 * @param p_Candidates specifies a collection of Zips to look for a match
 * @param p_StreetName specifies the name of the street to match against
 * @param p_OnlyEndings specifies whether to match full street name or only
 * last word of it.
 * @result returns the best matching Zip object if a match was found,
 * <b>null</b> otherwise.
 */
//---------------------------------------------------------------------------

private Zip	getBestMatchingStreet (Collection <Zip> p_Candidates, String p_StreetName, boolean p_OnlyEndings)
	{
	Iterator	<Zip> 	l_ZipIterator;
	Zip					l_Zip;
	Zip					l_BestMatch;
	
	Matcher			l_EndingMatcher;
	String			l_Candidate;
	String			l_StreetName;
	
	int				l_ShortestDistance;
	int				l_EditDistance;
	double			l_EditLengthRatio;
	
	l_ShortestDistance 	= p_StreetName.length();
	l_BestMatch 		= null;
	
	p_StreetName = p_StreetName.toLowerCase();
	
	//========================================================================
	//= If p_OnlyEndings flag is set we're going to chop last word from 
	//= specified street name and we'll set street name to only last word.
	//= If p_OnlyEndings is not set, then we're trying to expand typical 
	//= abbreviations in specified street name in order to ensure better matches.
	//========================================================================

	if (p_OnlyEndings)
		{
		l_EndingMatcher = c_HL7AddressEndingPattern.matcher(p_StreetName);
		if (l_EndingMatcher.matches()) 
			l_StreetName  = l_EndingMatcher.group(1);
		else l_StreetName = p_StreetName;
		this.log (Level.INFO, "Looking for streets ending in " + l_StreetName);
		}
	else
		{
		l_StreetName = this.expandAbbreviations (p_StreetName);	
		}
	
	l_ZipIterator = p_Candidates.iterator();
	
	while (l_ZipIterator.hasNext())
		{
		l_Zip 		= l_ZipIterator.next();
		l_Candidate = l_Zip.getStreet().toLowerCase();
		
		//====================================================================
		//= If p_OnlyEndings flag is set we're going to chop last word from 
		//= candidate street name as well.
		//====================================================================

		if (p_OnlyEndings)
			{
			l_EndingMatcher = c_HL7AddressEndingPattern.matcher(l_Candidate);
			if (l_EndingMatcher.matches()) l_Candidate = l_EndingMatcher.group(1);
			}
				
		l_EditDistance = StringUtilities.getLevenshteinDistance (l_Candidate,l_StreetName);
		if ( l_EditDistance < l_ShortestDistance)
			{
			l_ShortestDistance 	= l_EditDistance;
			l_BestMatch 			= l_Zip;
			}
		}
	
	//========================================================================
	//= Compute the resulting edit length ratio using the determined edit
	//= distance of best match and the string length of the specified street
	//= name. Edit Length ratio is computed by dividing edit distance of best
	//= match by string length of specified street name. Only if the computed
	//= edit length ratio is lower or equal to threshold will the match be
	//= accepted.
	//========================================================================

	if (l_StreetName.length() > 0)
		 l_EditLengthRatio = ((double)l_ShortestDistance/(double)l_StreetName.length());
	else l_EditLengthRatio = 0;
	
	if ((l_BestMatch != null) && (l_EditLengthRatio <= c_StreetMatchThreshold)) 
		{
		this.log (Level.INFO, "Match " + l_BestMatch.getStreet() + " to " + l_StreetName);
		return l_BestMatch;
		}
	else 
		{
		this.log (Level.INFO, "No satisfactory match for " + l_StreetName);
		return null;
		}
	}

//---------------------------------------------------------------------------

private Zip getBestMatchingZip (Collection <Zip> p_Candidates,	
							    String p_StreetName, String p_StreetNumber, 
							    boolean p_OnlyEndings)
	{
	Zip					l_MatchedStreet;
	Collection <Zip>	l_StreetZips;
	Zip					l_StreetZip = null;
	
	this.log (Level.INFO, "Looking for ZIP of Streetnumber " + p_StreetNumber +
							 " in Street " + p_StreetName );
	
	l_MatchedStreet = this.getBestMatchingStreet(p_Candidates,p_StreetName,p_OnlyEndings);
	if (l_MatchedStreet != null)
		{
		this.log (Level.INFO, "Best Match appears to be " + l_MatchedStreet.getStreet());
		
		l_StreetZips = this.getZipsByStreetAndLocality(l_MatchedStreet.getStreet(), l_MatchedStreet.getLocalityId());
		if (l_StreetZips != null)
			{
			this.log (Level.INFO, "Found " + l_StreetZips.size() + " possible ZIPs!");
			
			l_StreetZip = this.getZipForStreetNumber(l_StreetZips, p_StreetNumber);
			}
		}

	return l_StreetZip;
	}

//---------------------------------------------------------------------------

private Zip getZipForStreetNumber (Collection <Zip> p_Candidates, String p_StreetNumber)
	{
	Iterator <Zip>	l_ZipIterator;
	Zip				l_Zip = null;
	
	Matcher			l_StreetNumberMatcher;
	Integer			l_StreetNumber;
	boolean			l_Found = false;
	
	l_StreetNumber = Integer.valueOf (0);
	
	if (p_StreetNumber == null) p_StreetNumber = "0";
	
	this.log (Level.INFO, "Looking for ZIP of house number " + p_StreetNumber);
	
	l_StreetNumberMatcher = c_StreetNumberFirstPattern.matcher(p_StreetNumber);
	if (l_StreetNumberMatcher.matches() == false)
		{
		l_StreetNumberMatcher = c_StreetNumberLastPattern.matcher(p_StreetNumber);
		}
	
	if (l_StreetNumberMatcher.matches())
		{		
		try	{
			l_StreetNumber = Integer.parseInt(l_StreetNumberMatcher.group(1));
			}
		catch (NumberFormatException p_Exception)
			{
			l_StreetNumber = Integer.valueOf (0);
			}
		}
		
	l_ZipIterator = p_Candidates.iterator();
	while (l_ZipIterator.hasNext() && !l_Found)
		{
		l_Zip = l_ZipIterator.next();
		
		//====================================================================
		//= If Zip candidate specifies a parity for street number, then we're
		//= going to check first of all whether parity condition is met by 
		//= specified street number. If not, we skip rest of tests
		//====================================================================
		
		if ((l_Zip.getParity() != null) && (!l_Zip.getParity().equals (Zip.NONE)))
			{
			l_Found = (		(l_Zip.getParity().equals (Zip.ODD)  && (l_StreetNumber.intValue() % 2 > 0))
						||  (l_Zip.getParity().equals (Zip.EVEN) && (l_StreetNumber.intValue() % 2 == 0))	); 
			
			if (l_Found == false) continue;
			}
		
		if ((l_Zip.getFirst() != null) && (l_Zip.getFirst() > 0))
			{
			l_Found = (l_StreetNumber.compareTo(l_Zip.getFirst()) >= 0);
			
			if (l_Found == false) continue;
			}
		
		if ((l_Zip.getLast() != null) && (l_Zip.getLast() > 0))
			{
			l_Found = (l_StreetNumber.compareTo(l_Zip.getLast()) <= 0);
		
			if (l_Found == false) continue;
			}
		
		l_Found = true;
		}
	
	if (l_Found) 
		 this.log (Level.INFO, "ZIP for house number " + p_StreetNumber + " is " + l_Zip.getZip());
	else l_Zip = null;
	
	return l_Zip;
	}

//---------------------------------------------------------------------------
/**
 * This method tries to sanitize a given address from HL7 import. Sanitizing
 * consists first of all in trying to match locality in specified address
 * with all localities available in the GECAMed database. Once locality has
 * been matched, the method attempts to match specified street name with
 * all street names available for that locality in GECAMed database.
 */

//---------------------------------------------------------------------------

private PatientAddress sanitizeByLocality (HL7Address p_HL7Address, PatientAddress p_Address) throws Exception
	{
	Collection <Locality> 		l_Candidates		= null;
	Iterator	<Locality>		l_CandidateIterator	= null;
	Locality					l_Locality			= null;
	
	Collection <Zip> 			l_Zips				= null;
	Zip							l_MatchingZip 		= null;
	
	String						l_FirstLetter;
	Integer						l_ZipCode;
	boolean						l_Matched 			= false;
	
	if (p_HL7Address.getCity() == null) return null;

	//========================================================================
	//= Step  1 : First step consists in attempting a direct lookup by name
	//= of the locality specified in the HL7 Address. If one or more matches
	//= could be found, then we'll use those. If direct lookup fails,
	//= then we're retrieving all the localities starting with the same letter
	//= as the one specified in the HL7 Address. Next we're trying to home in
	//= on the best match using Levenshtein distance on locality names.
	//========================================================================
	
	l_Candidates = this.getLocalitiesByName (p_HL7Address.getCity());
	
	if ((l_Candidates == null) || (l_Candidates.size() == 0))
		{
		l_FirstLetter = p_HL7Address.getCity().substring(0,1);
		
		l_Candidates = this.getLocalitiesStartingWith(l_FirstLetter);
		
		if (l_Candidates == null) return null;
			
		l_Locality = this.getBestMatchingLocality (l_Candidates,p_HL7Address.getCity());
		
		if (l_Locality == null) return null;
		
		l_Candidates.clear();
		l_Candidates.add (l_Locality);
		}
		
	//========================================================================
	//= Step  2 : Next step consists in attempting to match street name specified
	//= in HL7 Address with all streets of previously matched localities. Whenever
	//= a match is found, we'll double-check with specified zip code. If zip matches
	//= then we have a perfect match and we may abandon any further search. If zip
	//= does not match, we'll remember the match and keep on looking.
	//========================================================================
	
	l_CandidateIterator 	= l_Candidates.iterator();
	
	while (l_CandidateIterator.hasNext() && (l_Matched==false))
		{
		l_Locality = l_CandidateIterator.next();
		l_Zips = this.getZipsByLocality(l_Locality.getId());
		if (l_Zips != null)
			{
//			l_MatchingZip = this.getBestMatchingStreet(l_Zips,p_Address.getStreetName(),false);
			l_MatchingZip = this.getBestMatchingZip(l_Zips,p_Address.getStreetName(),p_Address.getStreetNumber(),false);
			if (l_MatchingZip != null)
				{
				l_ZipCode = this.scrubZip (p_HL7Address.getZip());
								
				if (l_ZipCode.intValue() == l_MatchingZip.getZip().intValue()) 
					{
					l_Matched = true;
					}
				}
			}
		}
	
	//========================================================================
	//= Step  3 : If a match was impossible, chances are that the zip
	//= code was wrong or that specified street name differed to much from
	//= possible candidates. The next attempt consists in repeating the
	//= same procedure as earlier, but limiting street name matching to the
	//= ending word,.e.g 'Rue G-D Charlotte' or 'Rue Thomas Byrne' matching
	//= will be reduced to match 'Charlotte' or 'Byrne'. Doing this will
	//= prevent matches to be rejected because of acronyms being used in
	//= street names, e.g 'G-D' instead of "Grand Duchesse".
	//========================================================================
	
	if (l_Matched == false)
		{
		this.log (Level.INFO, "Attempting Streetname ending match...");
		
		l_CandidateIterator 	= l_Candidates.iterator();
		
		while (l_CandidateIterator.hasNext() && (l_Matched==false))
			{
			l_Locality = l_CandidateIterator.next();
			l_Zips = this.getZipsByLocality(l_Locality.getId());
			if (l_Zips != null)
				{
//				l_MatchingZip = this.getBestMatchingStreet(l_Zips,p_Address.getStreetName(),true);
				l_MatchingZip = this.getBestMatchingZip(l_Zips,p_Address.getStreetName(),p_Address.getStreetNumber(),true);
				if (l_MatchingZip != null)
					{
					l_ZipCode = this.scrubZip (p_HL7Address.getZip());
									
					if (l_ZipCode.intValue() == l_MatchingZip.getZip().intValue()) 
						{
						l_Matched = true;
						}
					}
				}
			}
		}
	
	//========================================================================
	//= TODO : Last Resort. If no match could be established using locality
	//= and street name, perhaps we should use locality and zip to lookup
	//= street. At this stage, we'll limit ourself to a no match situation.
	//========================================================================

	if (l_MatchingZip == null) return null;
	
	//========================================================================
	//= If a match could be established, then we have to finalize santization.
	//= Finalization consist in copying matched street name and zip into
	//= specified PatientAddress object that will be returned. Last but not
	//= least we'll have to retrieve the correct locality among the possible 
	//= candidates that were used in the sanitization effort.
	//========================================================================
		
	p_Address.setStreetName (l_MatchingZip.getStreet());
	p_Address.setZip 	   (l_MatchingZip.getZip().toString());
	
	l_CandidateIterator 	= l_Candidates.iterator();
	l_Matched = false;
	
	while (l_CandidateIterator.hasNext() && (l_Matched==false))
		{
		l_Locality = l_CandidateIterator.next();
		if (l_Locality.getId().equals(l_MatchingZip.getLocalityId())) l_Matched = true;
		}
	p_Address.setLocality 	   (l_Locality.getName());
	p_Address.setLocalityId 	   (l_Locality.getId());
	
	this.log (Level.INFO, "Successfully matched : " +
							p_HL7Address.getStreet() + ", " + 
							p_HL7Address.getZip() + " " + 
							p_HL7Address.getCity() +
							" => " +
							p_Address.getStreetNumber() + " " + 
							p_Address.getStreetName() + ", " +
							p_Address.getZip() + " " + 
							p_Address.getLocality());
	
	return p_Address;
	}

//---------------------------------------------------------------------------
/**
 * Checks whether a particual Patient address object already exists in a 
 * pool of patient address.
 * @param p_Addresses specifies the pool of a patient address objects.
 * @param p_Candidate specifies the address object we would like to know
 * off whether it already exists in the pool.
 * @return <b>true</b> if candidate already exists in pool, <b>false</b>
 * otherwise.
 */
//---------------------------------------------------------------------------

private boolean addressAlreadyExists (Set <PatientAddress> p_Addresses, PatientAddress p_Candidate)
	{
	Iterator <PatientAddress>		l_AddressIterator;
	PatientAddress			 		l_Address;
	boolean					 		l_Found = false;
	
	if ((p_Addresses == null) || (p_Candidate == null)) return false;
	
	l_AddressIterator = p_Addresses.iterator();
	while ((l_AddressIterator.hasNext()) && (l_Found == false))
		{
		l_Found   = true;
		l_Address = l_AddressIterator.next();
		l_Found &=  (l_Address.getCountry() != null) 		? l_Address.getCountry().equals( (p_Candidate.getCountry())) : false;
		l_Found &= (l_Address.getLocality() != null) 		? l_Address.getLocality().equals( (p_Candidate.getLocality())) : false;
		l_Found &= (l_Address.getZip() != null) 	   		? l_Address.getZip().equals( (p_Candidate.getZip())) : false;
		l_Found &= (l_Address.getStreetName() != null)		? l_Address.getStreetName().equals( (p_Candidate.getStreetName())) : false;
		l_Found &= (l_Address.getStreetNumber() != null)	? l_Address.getStreetNumber().equals( (p_Candidate.getStreetNumber())) : false;
		l_Found &= (l_Address.getType() != null)			? l_Address.getType().equals( (p_Candidate.getType())) : false;
		}
	
	return l_Found;
	}

//---------------------------------------------------------------------------
/**
 * Returns the latest hospitalisation period of a given hospitalisation.
 * @param p_Hospitalisation specifies the hospitalisation to get the latest
 * period of.
 * @return the latest hospitalisation period if one could be found, <b>null</b>
 * otherwise
 */
//---------------------------------------------------------------------------

private HospitalisationPeriod getLatestHospitalisationPeriod (Hospitalisation p_Hospitalisation)
	{
	Set <HospitalisationPeriod> 		l_HospitalisationPeriods;
	Iterator <HospitalisationPeriod>	l_PeriodIterator;
	
	HospitalisationPeriod				l_CurrentPeriod	= null;
	HospitalisationPeriod				l_LatestPeriod	= null;
	
	if (p_Hospitalisation == null) return null;
	
	l_HospitalisationPeriods = p_Hospitalisation.getHospitalisationPeriods();
	if( (l_HospitalisationPeriods==null) || (l_HospitalisationPeriods.size() == 0)) return null;
	
	l_PeriodIterator = l_HospitalisationPeriods.iterator();
	while (l_PeriodIterator.hasNext())
		{
		l_CurrentPeriod = l_PeriodIterator.next();
		if (l_LatestPeriod == null) l_LatestPeriod = l_CurrentPeriod;
		
		//========================================================================
		//= Under normal circumstances the start date would be sufficient to determine
		//= latest period. In case start dates of two periods are the same, we'll
		//= resort to use the object id to determine the later one. Higher ID ought
		//= to be later period.
		//========================================================================

		if (	(l_CurrentPeriod.getStartDate().after (l_LatestPeriod.getStartDate()))
			 || (	 (l_CurrentPeriod.getStartDate().equals (l_LatestPeriod.getStartDate()))
				  && (l_CurrentPeriod.getId() > l_LatestPeriod.getId()) ) )
			{
			l_LatestPeriod = l_CurrentPeriod;
			}
		}
	
	return l_LatestPeriod;
	}

//---------------------------------------------------------------------------

//===========================================================================
//= Patient Related Methods
//===========================================================================

//---------------------------------------------------------------------------
/**
 * Updates attributes of a given GECAMed Patient using data from HL7
 * Admission object. If HL7 Admission data does not contain a courtesy
 * title for the patient, the method will generate one using the patients'
 * gender and marital status. State of GECAMed Patient will be set to STATUS_NEW.
 * @param p_Patient specifies the GECAMed patient to be update.
 * @param p_HL7Admission specifies the HL7 Admission object holding the
 * data to be used to update the specified patient.
 */
//---------------------------------------------------------------------------

private void updatePatientFromHL7Admission (Patient p_Patient, HL7Admission p_HL7Admission) throws Exception
	{
	if ((p_Patient == null) || (p_HL7Admission==null)) return;
	
	this.log (Level.INFO, "Setting Patient Data...");
	
	if (p_HL7Admission.getPatientFirstName() != null)
		{
		p_HL7Admission.setPatientFirstName (this.scrubName (p_HL7Admission.getPatientFirstName(), c_MaxPatientFirstNameLength)); 
		}
	
	if (p_HL7Admission.getPatientName() != null)
		{
		p_HL7Admission.setPatientName (this.scrubName (p_HL7Admission.getPatientName(),c_MaxPatientNameLength)); 
		}
		
	if (p_HL7Admission.getPatientMaidenName() != null)
		{
		p_HL7Admission.setPatientMaidenName (this.scrubName(p_HL7Admission.getPatientMaidenName(),c_MaxPatientMaidenNameLength)); 
		}
		
	p_Patient.setFirstName 	(StringUtilities.capitalizeWords(p_HL7Admission.getPatientFirstName()));
	p_Patient.setSurName   	(StringUtilities.capitalizeWords(p_HL7Admission.getPatientName()));
	p_Patient.setMaidenName	(StringUtilities.capitalizeWords(p_HL7Admission.getPatientMaidenName()));
		
	if (m_GenderTranslator.containsKey(p_HL7Admission.getPatientSex()))
		p_Patient.setGender (m_GenderTranslator.get(p_HL7Admission.getPatientSex()));
	else p_Patient.setGender (p_HL7Admission.getPatientSex());
	
	if (m_MaritalStatusTranslator.containsKey(p_HL7Admission.getPatientMaritalStatus()))
		p_Patient.setMaritalStatus (m_MaritalStatusTranslator.get(p_HL7Admission.getPatientMaritalStatus()));
	else p_Patient.setMaritalStatus (p_HL7Admission.getPatientMaritalStatus());
	
	p_Patient.setBirthDate 			(p_HL7Admission.getPatientBirthdate());
	
	if (p_HL7Admission.getPatientBirthplace() != null)
		{
		p_Patient.setBirthLocality		(p_HL7Admission.getPatientBirthplace());
		}

	if (p_HL7Admission.getPatientTitle() == null)
		{
		if (Gender.MALE.equals(p_Patient.getGender()))
			{
			p_Patient.setTitle(Title.MISTER);
			}
		else if (Gender.FEMALE.equals(p_Patient.getGender()))
			{
			if (MaritalStatus.SINGLE.equals(p_Patient.getMaritalStatus()))
				p_Patient.setTitle(Title.MISS);
			else p_Patient.setTitle(Title.MADAM);
			}		
		}
	else p_Patient.setTitle	(p_HL7Admission.getPatientTitle());
	
	if (c_DischargeDeceased.equals(p_HL7Admission.getDischargeReason()))
		{
		this.log (Level.INFO, "Patient is deceased...");
		p_Patient.setStatus(Patient.STATUS_DEPARTED);
		}
	else if (Integer.valueOf(Patient.STATUS_DEPARTED).equals(p_Patient.getStatus()))
		{
		this.log (Level.INFO, "Patient was deceased. Bringing him back to life!");
		p_Patient.setStatus(Patient.STATUS_ACTIVE);
		}
		
	p_Patient.setInsurance				(m_Insurances.get(c_DefaultHealthInsurance));
	p_Patient.setSocialSecurityNumber	(p_HL7Admission.getPatientSSN());
	p_Patient.setIdRIS					(p_HL7Admission.getPatientId().toString());
	}

//---------------------------------------------------------------------------
/**
 * Returns a patient for the specified HL7 Admission. If a GECAMed patient
 * matching the HIS ID specified in the HL7 Admission exists, the existing
 * GECAMed patient will be returned. If no patient exists for a given HIS
 * ID, then a new patient will be created and initialized using the data
 * contained in the HL7 Admission.
 * @param p_HL7Admission specifies the HL7 Admission data holding the data
 * to fetch an already existing GECAMed patient or to create a new one if
 * required.
 */
//---------------------------------------------------------------------------

private Patient getPatientFromAdmission (HL7Admission p_HL7Admission) throws Exception
	{
	Patient l_Patient;
	
	// exit if no patient ID is given.
	if (p_HL7Admission.getPatientId() == null || "".equals(p_HL7Admission.getPatientId()) || p_HL7Admission.getPatientId().toString().trim().length() == 0) {
		this.log (Level.ERROR, "HL7Admission has no RIS/Patient ID " + p_HL7Admission.getPatientId());
		throw new Exception("HL7Admission has no RIS/Patient ID " + p_HL7Admission.getPatientId());
	}

	this.log (Level.INFO, "Looking for patient with RIS ID " + p_HL7Admission.getPatientId());

	//========================================================================
	//= Step 1. First Step consists in checking whether admitted patient
	//= already exists in GECAMed database. Patient Lookup will be done via
	//= patient's RIS id from ADT message. In case the specified patient does not 
	//= exist in the GECAMed database, we're going to create a new Patient using 
	//= the data from the ADT message.
	//========================================================================
	
	// TODO modify patient matching to not only work on the RIS id.
	l_Patient = this.getPatientByRISid (p_HL7Admission.getPatientId().toString());
	if (l_Patient == null)
		{
		this.log (Level.INFO, "Not found! Creating new patient");
		
		l_Patient = new Patient ();
		l_Patient.setStatus (Patient.STATUS_NEW);
		l_Patient.setCreationDate(new Date());
		this.updatePatientFromHL7Admission (l_Patient, p_HL7Admission);
		l_Patient = this.savePatient(l_Patient);
		}
	
	return l_Patient;
	}

//---------------------------------------------------------------------------

//===========================================================================
//= Patient Contact Related Methods
//===========================================================================

//---------------------------------------------------------------------------

private boolean areSamePerson (Patient p_Patient, HL7Guarantor p_Guarantor)
	{
	boolean			l_SamePerson;
	
	l_SamePerson  = (p_Patient.getSurName().equalsIgnoreCase (p_Guarantor.getName())) ? true : false;
	l_SamePerson &= (p_Patient.getFirstName().equalsIgnoreCase (p_Guarantor.getFirstName())) ? true : false;
	l_SamePerson &= (p_Patient.getSocialSecurityNumber().equals (p_Guarantor.getSSN())) ? true : false;
	
	return l_SamePerson;
	}

//---------------------------------------------------------------------------

private PatientContact getContactFromGuarantor (Patient p_Patient, HL7Guarantor p_Guarantor) throws Exception
	{
	PatientContact	l_Contact;
	PatientAddress	l_ContactAddress;
	HL7Address		l_GuarantorAddress;
	String			l_GuarantorFirstName;
	String			l_GuarantorName;
	
	
	l_Contact = this.getContactForPatient (p_Patient);
	if (l_Contact == null)
		{
		l_Contact = new PatientContact ();
		l_Contact.setPatientId (p_Patient.getId());
		}

	l_GuarantorFirstName = (p_Guarantor.getFirstName()!=null)?p_Guarantor.getFirstName():"";
	l_GuarantorName      = (p_Guarantor.getName()!=null)?p_Guarantor.getName():"";
	
	l_Contact.setName (StringUtilities.capitalizeWords(l_GuarantorName)
					   + " " + 
					   StringUtilities.capitalizeWords(l_GuarantorFirstName));
	
	l_GuarantorAddress = new HL7Address ();
	l_GuarantorAddress.setStreet	(p_Guarantor.getStreet());
	l_GuarantorAddress.setZip		(p_Guarantor.getZip());
	l_GuarantorAddress.setCity		(p_Guarantor.getCity());
	l_GuarantorAddress.setCountry	(p_Guarantor.getCountry());
	
	l_ContactAddress = this.newAddressFromHL7Address (l_GuarantorAddress);
	
	if (	(l_ContactAddress.getStreetName() == null)
		 || (l_ContactAddress.getZip() == null)
		 || (l_ContactAddress.getLocality() == null))
		{
		l_ContactAddress = p_Patient.getHomeAddress();
		}
				
	l_Contact.setStreetName   (l_ContactAddress.getStreetName());
	l_Contact.setStreetNumber (l_ContactAddress.getStreetNumber());
	l_Contact.setZip		  (l_ContactAddress.getZip());
	l_Contact.setLocality	  (l_ContactAddress.getLocality());
	l_Contact.setLocalityId	  (l_ContactAddress.getLocalityId());
	l_Contact.setCountry	  (l_ContactAddress.getCountry());
		 
	return l_Contact;
	}

//---------------------------------------------------------------------------

private PatientContact setContactFromHL7Admission (Patient p_Patient, HL7Admission p_Admission) throws Exception
	{
	HL7Guarantor				l_Guarantor;
	PatientContact				l_Contact;
	PatientAddress				l_HomeAddress;
	PatientAddress				l_BillingAddress;
	Set <PatientAddress> 		l_PatientAddresses;
	Iterator <PatientAddress>	l_AddressIterator;
	PatientAddress				l_Address;
	
	l_Contact   	 = null;
	l_HomeAddress 	 = null;
	l_BillingAddress = null;
	
	this.log (Level.INFO, "Setting Guarantor from Admission...");

	l_Guarantor = this.getGuarantorByMessageId(p_Admission.getMessageId());
	if (l_Guarantor != null)
		{
		if ((l_Guarantor.getIdentity() != null) && (l_Guarantor.getIdentity().intValue() == HL7Guarantor.c_SamePerson))
			{
			//================================================================
			//= Case 1. Guarantor is same Person as Patient. In this case,
			//= which is the most common one, we're using the patients' home
			//= address as billing address.
			//================================================================
						
			this.log (Level.INFO, "Guarantor is same person as Patient. Just adding Billing Address!");
			
			l_PatientAddresses = p_Patient.getAddress();
			if (l_PatientAddresses != null)
				{
				l_AddressIterator = l_PatientAddresses.iterator();
				while (l_AddressIterator.hasNext())
					{
					l_Address = l_AddressIterator.next();
					if (AddressType.BILLING.equals(l_Address.getType())) l_BillingAddress = l_Address;
					else if (AddressType.PRIVATE.equals(l_Address.getType())) l_HomeAddress = l_Address;
					}
				
				if ((l_BillingAddress == null) && (l_HomeAddress != null))
					{
					l_BillingAddress = new PatientAddress();
					l_BillingAddress.setDate			(new Date ());
					
					l_BillingAddress.setType			(AddressType.BILLING);
					l_BillingAddress.setStreetName   	(l_HomeAddress.getStreetName());
					l_BillingAddress.setStreetNumber 	(l_HomeAddress.getStreetNumber());
					l_BillingAddress.setZip		  		(l_HomeAddress.getZip());
					l_BillingAddress.setLocality	  	(l_HomeAddress.getLocality());
					l_BillingAddress.setLocalityId		(l_HomeAddress.getLocalityId());
					l_BillingAddress.setCountry	  		(l_HomeAddress.getCountry());
					
					l_PatientAddresses.add(l_BillingAddress);
					p_Patient.setAddress (l_PatientAddresses);
					p_Patient = this.savePatient(p_Patient);
					}
				}
			else 
				{
				this.log (Level.WARN, "Patient " + p_Patient.getFirstName() + " " + 
													  p_Patient.getSurName() + 
										 " doesn't have any addresses. Can't add Billing Address!");
				}
			}
		else
			{
			//================================================================
			//= Case 2. Guarantor is a different Person than Patient. This case
			//= normally happens with children or foreign people. In this
			//= case a patient contact will be created from guarantor data,
			//= furthermore, guarantor address will be added as billing address
			//= to patients' address.
			//================================================================

			l_Contact = this.getContactFromGuarantor(p_Patient, l_Guarantor);
			this.log (Level.INFO, "Guarantor is " + l_Contact.getName());
			
			l_BillingAddress = new PatientAddress();
			l_BillingAddress.setDate(new Date());
			l_BillingAddress.setType			(AddressType.BILLING);
			l_BillingAddress.setStreetName   	(l_Contact.getStreetName());
			l_BillingAddress.setStreetNumber 	(l_Contact.getStreetNumber());
			l_BillingAddress.setZip		  		(l_Contact.getZip());
			l_BillingAddress.setLocality	  	(l_Contact.getLocality());
			l_BillingAddress.setLocalityId		(l_Contact.getLocalityId());
			l_BillingAddress.setCountry	  		(l_Contact.getCountry());
			
			l_PatientAddresses = p_Patient.getAddress();
			if (l_PatientAddresses == null)
				{
				l_PatientAddresses = new HashSet <PatientAddress> ();
				}
			l_PatientAddresses.add(l_BillingAddress);
			p_Patient.setAddress (l_PatientAddresses);
			p_Patient = this.savePatient(p_Patient);
			}
		
		this.deleteHL7Guarantor(l_Guarantor);
		}
	return l_Contact;
	}

//---------------------------------------------------------------------------

//===========================================================================
//= Address Related Methods
//===========================================================================

//---------------------------------------------------------------------------
/**
 * Creates a new GECAMed patient address from data provided in specified
 * HL7 Address object. For addresses in luxembourg, the method attempts
 * to sanitize available address data. Address data sanitization consists
 * in properly matching provided address data with consistent address data
 * available in the GECAMed database. If sanitation fails, then raw address
 * data will be used to preset newly created patient address.
 * @param p_HL7Address specifies the "original" data to be used to create
 * a new GECAMed patient address object.
 * @return A GECAMed patient address object intialized from the data provided
 * in p_HL7Address
 */
//---------------------------------------------------------------------------

private PatientAddress newAddressFromHL7Address (HL7Address p_HL7Address) throws Exception
	{
	Matcher			l_CountryMatcher;
	Matcher			l_StreetMatcher;
	Country			l_Country;
	PatientAddress	l_PatientAddress	= null;
	PatientAddress	l_SanitizedAddress	= null;
	
	//========================================================================
	//= Step 1 : Extract Street Number and Street Name from HL7 Address
	//========================================================================
	
	this.log (Level.INFO, "Creating new Patient Address from " + this.stringifyHL7Address(p_HL7Address));
	
	l_PatientAddress = new PatientAddress ();
	l_PatientAddress.setDate(new Date());
	
	if (p_HL7Address.getStreet() != null)
		{
		l_StreetMatcher = c_HL7AddressStreetPattern.matcher (p_HL7Address.getStreet());
		if (	l_StreetMatcher.matches() 
			&& (l_StreetMatcher.groupCount() > 1) 
			&& (l_StreetMatcher.group(2).length() > 0))
			{
			l_PatientAddress.setStreetNumber(l_StreetMatcher.group(1));
			l_PatientAddress.setStreetName  (l_StreetMatcher.group(2));
			}
		else l_PatientAddress.setStreetName (p_HL7Address.getStreet());
		}
	
	//========================================================================
	//= Step 2 : Check whether HL7Address is an address in Luxembourg. If so,
	//=	we'll try to sanitize the address.
	//========================================================================
	
	if (p_HL7Address.getCountry() == null) p_HL7Address.setCountry (m_Luxembourg.getName());
	
	if (p_HL7Address.getCountry() != null)
		{
		l_CountryMatcher = c_HL7AddressCountryPattern.matcher (p_HL7Address.getCountry());
		if (l_CountryMatcher.matches())
			{
			l_Country = m_Luxembourg;
			
			if (l_Country != null) l_PatientAddress.setCountry (l_Country.getName());
							 else l_PatientAddress.setCountry (p_HL7Address.getCountry());
			
			l_SanitizedAddress = this.sanitizeByLocality (p_HL7Address,l_PatientAddress);
			if (l_SanitizedAddress != null)
				{
				return l_SanitizedAddress;
				}
			}
		}
	
	//========================================================================
	//= Step 3 : In case its an non luxembourgish address or a proper match
	//= wasn't possible
	//========================================================================
	
	l_PatientAddress.setCountry		(p_HL7Address.getCountry());
	l_PatientAddress.setLocality	(p_HL7Address.getCity());
	l_PatientAddress.setZip			(p_HL7Address.getZip());
	
	return l_PatientAddress;
	}

//---------------------------------------------------------------------------
/**
 * Retrieves HL7 Addresses associated with specified HL7 Admissions and
 * creates new GECAMed patient address objects from them. The method makes
 * sure not to create duplicate addresses. Furthermore, for every newly
 * created GECAMed patient address object, the method stores a second one
 * holding the original HL7 Address data. This redundancy stems from the fact
 * that when creating a new GECAMed patient address object, the original HL7
 * Address will be santitized, i.e. mapped to consistent address data in 
 * GECAMed database. In order to be able to detect wrong or erronous matches
 * at a later stage, the original data will be stored as well.
 * @param p_Patient specifies the patient to set address data for
 * @param p_HL7Admission specifies the HL7 admission object to get associated
 * HL7 address data from.
 */
//---------------------------------------------------------------------------

private void setPatientAddressesFromHL7Admission (Patient p_Patient, HL7Admission p_HL7Admission) throws Exception
	{	
	Collection <HL7Address>		l_HL7Addresses		= null;
	Iterator	<HL7Address>	l_AddressIterator	= null;

	HashSet <PatientAddress> 	l_NewAddresses		= null;
	Set <PatientAddress> 		l_OldAddresses		= null;
		
	HL7Address					l_CurrentHL7Address	= null;
	
	PatientAddress				l_OriginalAddress	= null;
	PatientAddress				l_NewPrivateAddress	= null;
	PatientAddress				l_NewBillingAddress	= null;
		
	this.log (Level.INFO, "Setting Patient Addresses from Admission...");

	l_HL7Addresses = this.getAddressesByMessageId (p_HL7Admission.getMessageId());
	
	if ((l_HL7Addresses != null) && (l_HL7Addresses.size() > 0))
		{
		l_OldAddresses = p_Patient.getAddress();
		if (l_OldAddresses != null) this.deleteAddresses(l_OldAddresses);
		
		l_NewAddresses = new HashSet <PatientAddress> ();
		
		l_AddressIterator = l_HL7Addresses.iterator();
		
		while (l_AddressIterator.hasNext())
			{
			l_CurrentHL7Address = (HL7Address) l_AddressIterator.next();
						
			l_OriginalAddress = new PatientAddress ();
			l_OriginalAddress.setDate(new Date());
			//l_OriginalAddress.setPatientId	(p_Patient.getId());
			l_OriginalAddress.setCountry		(l_CurrentHL7Address.getCountry());
			l_OriginalAddress.setLocality		(l_CurrentHL7Address.getCity());
			l_OriginalAddress.setZip			(l_CurrentHL7Address.getZip());
			l_OriginalAddress.setStreetName		(l_CurrentHL7Address.getStreet());
			l_OriginalAddress.setStreetNumber	("");
			l_OriginalAddress.setType			(AddressType.HIS);
			
			if (!this.addressAlreadyExists(l_NewAddresses,l_OriginalAddress))
				{
				l_NewAddresses.add (l_OriginalAddress);
				
				l_NewPrivateAddress = newAddressFromHL7Address (l_CurrentHL7Address);		
				l_NewPrivateAddress.setType(AddressType.PRIVATE);	
				l_NewAddresses.add (l_NewPrivateAddress);			
				
				l_NewBillingAddress = (PatientAddress) l_NewPrivateAddress.clone();			
				l_NewBillingAddress.setType(AddressType.BILLING);	
				l_NewAddresses.add (l_NewBillingAddress);			
				}
			
			this.deleteHL7Address(l_CurrentHL7Address);
			}
		
		p_Patient.setAddress(l_NewAddresses);
		}
	}

//---------------------------------------------------------------------------

//===========================================================================
//= Phone Number Related Methods
//===========================================================================

//---------------------------------------------------------------------------
/**
 * Creates a new set of GECAMed patient phone numbers from specified HL7
 * phone data. A single HL7 phone object can hold two phone numbers: a private
 * one and an optional office phone number. Depending on whether none, one or
 * both fields are provided, the method returns a set holding zero, one or
 * two GECAMed patient phone objects.
 * @param p_Patient specifies the patient to create phone number objects for.
 * @param p_HL7Phone specifies the HL7 phone object to get phone number data
 * from
 * @return A set holding zero, one or two GECAMed patient phone objects, 
 * initialized from specified HL7 phone object.
 */
//---------------------------------------------------------------------------

private HashSet <PatientPhone> newPhoneNumbersFromHL7Phone (Patient p_Patient, HL7Phone p_HL7Phone) throws Exception
	{
	HashSet <PatientPhone> 	l_PhoneNumberPair 	= null;
	PatientPhone				l_PhoneNumber	 	= null;
	Matcher					l_PhoneNumberMatcher	= null;
	
	l_PhoneNumberPair = new HashSet <PatientPhone> ();
	
	if (p_HL7Phone.getPrivateNumber() != null) 
		{
		l_PhoneNumberMatcher = c_HL7PhoneNumberPattern.matcher (p_HL7Phone.getPrivateNumber());
		if (l_PhoneNumberMatcher.matches())
			{
			l_PhoneNumber = new PatientPhone ();
			//l_PhoneNumber.setPatientId(p_Patient.getId());
			l_PhoneNumber.setType(PhoneType.HOME);
			l_PhoneNumber.setNumber (l_PhoneNumberMatcher.group(0));
			l_PhoneNumberPair.add(l_PhoneNumber);
			}
		}
		
	if (p_HL7Phone.getOfficeNumber() != null) 
		{
		l_PhoneNumberMatcher = c_HL7PhoneNumberPattern.matcher (p_HL7Phone.getOfficeNumber());
		if (l_PhoneNumberMatcher.matches())
			{
			l_PhoneNumber = new PatientPhone ();
			//l_PhoneNumber.setPatientId(p_Patient.getId());
			l_PhoneNumber.setType(PhoneType.OFFICE);
			l_PhoneNumber.setNumber (l_PhoneNumberMatcher.group(0));
			l_PhoneNumberPair.add(l_PhoneNumber);
			}
		}
	
	return (l_PhoneNumberPair);
	}

//---------------------------------------------------------------------------
/**
 * Retrieves HL7 Phone numbers associated with specified HL7 Admission and
 * creates zero, one or two GECAMed phone number objects, depending on the
 * number of phone numbers defined in HL7 Phone object. For reasons of
 * simplicity, if more than one HL7 Phone number object is associated to
 * specified HL7 Admission, only the last one will be taken into account.
 * @param p_Patient specifies the patient to get phone numbers for.
 * @param p_HL7Admission specifies the HL7 Admission object to get associated
 * HL7 Phone numbers from.
 */
//---------------------------------------------------------------------------

private void setPhoneNumbersFromHL7Admission (Patient p_Patient, HL7Admission p_HL7Admission) throws Exception
	{	
	Collection <HL7Phone>		l_HL7PhoneNumbers		= null;
	Iterator	<HL7Phone>		l_HL7PhoneIterator		= null;

	HashSet <PatientPhone> 	l_NewPhoneNumbers		= null;
	Set <PatientPhone>		l_OldPhoneNumbers		= null;
	
	HL7Phone					l_HL7Phone				= null;
		
	if ((p_Patient == null) || (p_HL7Admission == null)) return;
	
	l_HL7PhoneNumbers = this.getPhoneNumbersByMessageId (p_HL7Admission.getMessageId());
	
	if ((l_HL7PhoneNumbers != null) && (l_HL7PhoneNumbers.size() > 0))
		{
		l_HL7PhoneIterator = l_HL7PhoneNumbers.iterator();
		
		while (l_HL7PhoneIterator.hasNext())
			{
			l_HL7Phone = l_HL7PhoneIterator.next();
			l_NewPhoneNumbers = this.newPhoneNumbersFromHL7Phone(p_Patient,l_HL7Phone);
			this.deleteHL7Phone(l_HL7Phone);
			}	
		
		l_OldPhoneNumbers = p_Patient.getPhones();
		if (l_OldPhoneNumbers != null) this.deletePhoneNumbers(l_OldPhoneNumbers);
		
		p_Patient.setPhones(l_NewPhoneNumbers);
		}
	}

//---------------------------------------------------------------------------

//===========================================================================
//= Health Insurance Related Methods
//===========================================================================

//---------------------------------------------------------------------------
/**
 * Retrieves Health Insurance relevant data from specified HL7 Admissions and
 * sets specified patients health insurance data. The method attempts to match
 * Hospital specific acronyms for health insurance companies with those defined
 * in the GECAMed database.
 * @param p_Patient specifies the patient to set health insurance data of.
 * @param p_HL7Admissions specifies the HL7 Admission object to get health
 * insurance data from.
 */
//---------------------------------------------------------------------------
//TODO now we map the insurance over a mapping in the database so the Hashtable m_HealthInsuranceTranslator 
// is not longer required
private void setPatientInsuranceFromHL7Admission (Patient 		p_Patient, 
											   HL7Admission 	p_HL7Admission) throws Exception
	{
	Collection <HL7Insurance>		l_HL7Insurances			= null;
	Iterator	<HL7Insurance>		l_HL7InsuranceIterator	= null;
	HL7Insurance					l_CurrentHL7Insurance		= null;
	
	Insurance					l_HealthInsurance		= null;
	InsurancePlan				l_ComplementaryInsurance	= null;
	
	String						l_HL7CompanyName			= null;
	String						l_CompanyName			= null;
	String						l_InsurancePlanName		= null;
	
	this.log (Level.INFO, "Setting Patient Insurance data...");

	l_HL7Insurances = this.getInsurancesByMessageId (p_HL7Admission.getMessageId());
	
	if ((l_HL7Insurances != null) && (l_HL7Insurances.size() > 0))
		{
		l_HL7InsuranceIterator = l_HL7Insurances.iterator();
		
		while (l_HL7InsuranceIterator.hasNext())
			{
			l_CurrentHL7Insurance = l_HL7InsuranceIterator.next();
			
			//================================================================
			//= Case 1. If Insurance Plan field's not set, then HL7Insurance
			//= specifies mandatory health insurance company.
			//================================================================
			if (l_CurrentHL7Insurance.getInsurancePlan() == null)
				{
				l_HL7CompanyName 	= l_CurrentHL7Insurance.getInsuranceName();
				if (l_HL7CompanyName != null)
					{
					// match HL7CompanyName with insurances from database 
					l_HealthInsurance 	= m_Insurances.get(l_HL7CompanyName);
					
					if (l_HealthInsurance != null) 
						{
						p_Patient.setInsurance	(l_HealthInsurance);	
						//p_Patient.setInsuranceID	(l_HealthInsurance.getId());	
						}
					else
						{
						this.log (Level.WARN, "Failed to match insurance " + l_HL7CompanyName);
						}
					
//					// match the company name over hash map
//					l_CompanyName = m_HealthInsuranceTranslator.get(l_HL7CompanyName);
//					if (l_CompanyName != null)
//						{
//						l_HealthInsurance 	= m_Insurances.get(l_CompanyName);
//				
//						if (l_HealthInsurance != null) 
//							{
//							p_Patient.setInsurance	(l_HealthInsurance);	
//							//p_Patient.setInsuranceID	(l_HealthInsurance.getId());	
//							}
//						else
//							{
//							this.log (Level.WARN, "Failed to match insurance " + l_CompanyName);
//							}
//						}
//					// if insurance key not in m_HealthInsuranceTranslator map get name form Database /TODO
//					else
//						{
//						this.log (Level.WARN, "Failed to match unknown insurance!");
//						//TODO : Perhaps adding failed match to PatientMemo
//						
//						// match HL7CompanyName with insurances from database 
//						l_HealthInsurance 	= m_Insurances.get(l_HL7CompanyName);
//						
//						if (l_HealthInsurance != null) 
//							{
//							p_Patient.setInsurance	(l_HealthInsurance);	
//							//p_Patient.setInsuranceID	(l_HealthInsurance.getId());	
//							}
//						else
//							{
//							this.log (Level.WARN, "Failed to match insurance " + l_HL7CompanyName);
//							}
//						}
					}else
					{
						this.log (Level.WARN, "Failed to match unknown insurance!");
						//TODO : Perhaps adding failed match to PatientMemo
					}
				}
			else
				{
				l_InsurancePlanName = l_CurrentHL7Insurance.getInsurancePlan();
				if (HL7Insurance.c_CMCM_Normal.equals(l_InsurancePlanName))
					{
					l_ComplementaryInsurance = m_InsurancePlans.get(c_InsurancePlanNormal);
					}
				else if (HL7Insurance.c_CMCM_Prestaplus.equals(l_InsurancePlanName))
					{
					l_ComplementaryInsurance = m_InsurancePlans.get(c_InsurancePlanPrestaPlus);
					}
				
				if (l_ComplementaryInsurance != null) 
					{
					p_Patient.setComplementary 	(l_ComplementaryInsurance);	
					//p_Patient.setComplementaryID	(l_ComplementaryInsurance.getId());	
					p_Patient.setPolicyNumber		(l_CurrentHL7Insurance.getPolicyNumber());
					}
				else
					{
					this.log (Level.WARN, "Failed to match insurance plan" + l_InsurancePlanName);
					//TODO : Perhaps adding failed match to PatientMemo
					}
				}
			this.deleteHL7Insurance(l_CurrentHL7Insurance);
			}
		}
	}

//---------------------------------------------------------------------------

//===========================================================================
//= Hospitalisation Related Methods
//===========================================================================

//---------------------------------------------------------------------------
/**
 * Retrieves Hospitalisation Period related data from specified HL7 Admission and
 * initializes specified GECAMed Hospitalisation Period. Furthermore, the
 * method automatically completes GECAMeds list of Hospitalisation Departments.
 * @param p_Period specifies the GECAMed hospitalisation period to initialize.
 * @param p_HL7Admission specifies the HL7 Admission object to get hospitalisation
 * data from. 
 */
//---------------------------------------------------------------------------

private HospitalisationPeriod setHospitalisationPeriodFromHL7Admission (HospitalisationPeriod p_Period,
													   					HL7Admission 		 p_HL7Admission) throws Exception
	{
	HospitalDepartment	l_NewDepartment;
	int					l_EventType;
	
	if ((p_Period == null) || (p_HL7Admission == null)) return p_Period;
	
	l_EventType = p_HL7Admission.getEvent();	
	
	switch (l_EventType)
		{
		case HL7Admission.c_TransferPatient :
		case HL7Admission.c_InToOutPatient 	:
		case HL7Admission.c_OutToInPatient 	: p_Period.setStartDate	(p_HL7Admission.getTransferTime());
											  break;
		default								: p_Period.setStartDate	(p_HL7Admission.getAdmissionTime());
											  break;
		}
	
	p_Period.setEndDate		(p_HL7Admission.getDischargeTime());
	p_Period.setDescription	(p_HL7Admission.getAdmissionReason());

	if ( (HL7Admission.c_OutPatient.equals (p_HL7Admission.getPatientClass())) ||
		 (HL7Admission.c_RecurringPatient.equals(p_HL7Admission.getPatientClass())) )	
		{
		p_Period.setHospitalisationClass(this.m_HospitalisationClasses.get(c_Ambulatory));
		}
	else if (HL7Admission.c_FirstClass.equals (p_HL7Admission.getHospitalisationClass()))
		{
		p_Period.setHospitalisationClass(this.m_HospitalisationClasses.get(c_FirstClass));
		p_Period.setHospitalBed (p_HL7Admission.getBed());
		}
	else if (HL7Admission.c_SecondClass.equals (p_HL7Admission.getHospitalisationClass()))
		{
		p_Period.setHospitalisationClass(this.m_HospitalisationClasses.get(c_SecondClass));
		p_Period.setHospitalBed (p_HL7Admission.getBed());
		}
	
	//========================================================================
	//= Check whether Point-of-Care from ADT Message already exists in GECAMeds'
	//= HospitalDepartment table. If so, we'll use the proper object right away.
	//= If specified Point-of-Care does not yet exist, then we're going to
	//= create it on order to complete GECAMeds' list automatically
	//========================================================================
	
	if (p_HL7Admission.getPointOfCare() != null)
		{
		if (m_HospitalDepartments.containsKey (p_HL7Admission.getPointOfCare()))
			{
			p_Period.setHospitalDepartment(this.m_HospitalDepartments.get(p_HL7Admission.getPointOfCare()));
			}
		else
			{
			l_NewDepartment = new HospitalDepartment ();
			l_NewDepartment.setName(p_HL7Admission.getPointOfCare());
			l_NewDepartment = this.saveHospitalDepartment(l_NewDepartment);
			m_HospitalDepartments.put(l_NewDepartment.getName(),l_NewDepartment);
			p_Period.setHospitalDepartment(l_NewDepartment);
			}
		}
		
	return p_Period;
	}

//---------------------------------------------------------------------------
/**
 * Retrieves Hospitalisation related data from specified HL7 Admission object
 * and adds a new Hospitalisation Period to specified Patients Hospitalisation.
 * In case there's not yet a matching GECAMed Hospitalisation to attach the newly
 * created hospitalisation period to, an new Hospitalisation record will be created.
 * @param p_Patient specifies the patient to add an hospitalisation period for.
 * @param p_HL7Admission specifies the HL7 Admission object to get hospitalisation
 * data from.
 * @return The specified patients current Hospitalisation record extended by
 * the newly create hospitalisation period.
 */
//---------------------------------------------------------------------------

private Hospitalisation addHospitalisationPeriodFromHL7Admission (Patient 	p_Patient,
															      HL7Admission p_HL7Admission) throws Exception
	{
	Hospitalisation					l_Hospitalisation;
	Set <HospitalisationPeriod> 	l_HospitalisationPeriods;
	HospitalisationPeriod			l_HospitalisationPeriod;
	Matcher							l_AccidentNumberMatcher;
	String							l_AccidentNumber;
	
	this.log (Level.INFO, "Setting Patient Hospitalisation data...");
	
	l_Hospitalisation = this.getHospitalisationByPassageId(p_HL7Admission.getVisitId().toString());
	if (l_Hospitalisation == null)
		{
		l_Hospitalisation = new Hospitalisation ();
		l_Hospitalisation.setPasID	(p_HL7Admission.getVisitId().toString());
		l_Hospitalisation.setPatientId(p_Patient.getId());
//		if (HL7Admission.c_Accident.equals(p_HL7Admission.getAdmissionType()))
		if (p_HL7Admission.getAccidentDate() != null)
			{
			l_Hospitalisation.setIsWorkAccident(Boolean.TRUE);
			l_Hospitalisation.setWorkAccidentDate(p_HL7Admission.getAccidentDate());
			
			if (p_HL7Admission.getAccidentNumber() != null)
				{
				l_AccidentNumberMatcher = c_AccidentNumberPattern.matcher(p_HL7Admission.getAccidentNumber());
				if (l_AccidentNumberMatcher.matches())
					{
					l_AccidentNumber = "U" + l_AccidentNumberMatcher.group(1) +
									   "/" + l_AccidentNumberMatcher.group(2);
				
					l_Hospitalisation.setWorkAccidentNumber(l_AccidentNumber);
					}
				else l_Hospitalisation.setWorkAccidentNumber(p_HL7Admission.getAccidentNumber());
				}
			else
				{
				l_AccidentNumber = AccidentNumberFormatter.getAccidentNumber (p_HL7Admission.getAccidentDate());
				l_Hospitalisation.setWorkAccidentNumber(l_AccidentNumber);
				}
			}
		else l_Hospitalisation.setIsWorkAccident(Boolean.FALSE);
		}
	
	l_HospitalisationPeriods = l_Hospitalisation.getHospitalisationPeriods();
	if (l_HospitalisationPeriods == null)
		l_HospitalisationPeriods = new HashSet <HospitalisationPeriod> ();
	
	l_HospitalisationPeriod = new HospitalisationPeriod ();
	l_HospitalisationPeriod = this.setHospitalisationPeriodFromHL7Admission (l_HospitalisationPeriod,p_HL7Admission);	
	l_HospitalisationPeriods.add(l_HospitalisationPeriod);
	
	l_Hospitalisation.setHospitalisationPeriods(l_HospitalisationPeriods);
	
	return l_Hospitalisation;
	}

//---------------------------------------------------------------------------
/**
 * Updates the latest hospitalisation period for specified patient using
 * hospitalisation data from specified HL7 Admission. If specified patient
 * does not have a current hospitalisation record a new one will be created.
 * @param p_Patient specifies the patient to update latest hospitalisation
 * period of.
 * @param p_HL7Admission specifies HL7 Admission object to get hospitalisation
 * data from.
 * @return The specified patients current Hospitalisation record with an
 * up to data last hospitalisation period.
 */
//---------------------------------------------------------------------------

private Hospitalisation updateHospitalisationPeriodFromHL7Admission (Patient 		p_Patient,
														   		 HL7Admission 	p_HL7Admission) throws Exception
	{
	Hospitalisation				l_Hospitalisation;
	Set <HospitalisationPeriod> 	l_HospitalisationPeriods;
	HospitalisationPeriod			l_HospitalisationPeriod;
	
	this.log (Level.INFO, "Updating Patient Hospitalisation data...");
	
	l_Hospitalisation = this.getHospitalisationByPassageId(p_HL7Admission.getVisitId().toString());
	if (l_Hospitalisation == null)
		{
		return this.addHospitalisationPeriodFromHL7Admission (p_Patient,p_HL7Admission);
		}
	
	l_HospitalisationPeriods = l_Hospitalisation.getHospitalisationPeriods();
	l_HospitalisationPeriod = this.getLatestHospitalisationPeriod (l_Hospitalisation);

	if ((l_HospitalisationPeriods != null) && (l_HospitalisationPeriod != null))
		{
		l_HospitalisationPeriod = this.setHospitalisationPeriodFromHL7Admission (l_HospitalisationPeriod,p_HL7Admission);	
		l_Hospitalisation.setHospitalisationPeriods(l_HospitalisationPeriods);
		}
	
	return l_Hospitalisation;
	}

//---------------------------------------------------------------------------

//===========================================================================
//= Invoice Related Methods
//===========================================================================

//---------------------------------------------------------------------------

private void uprateAmbulatoryInvoices (Hospitalisation p_Hospitalisation) throws Exception
	{
	Collection <Invoice>	l_ExistingInvoices;
	Iterator <Invoice>		l_InvoiceIterator;
	Invoice					l_Invoice;
	
	HospitalisationPeriod	l_HospitalizedPeriod;
	HospitalisationClass	l_HospitalizedClass;
	
	this.log (Level.INFO, "Looking for already existing ambulatory invoices to uprate..");
	
	l_ExistingInvoices = this.getInvoicesByHospitalisation(p_Hospitalisation);
	
	//=======================================================================
	//= In case no invoices are returned, we're done.
	//=======================================================================
	
	if (l_ExistingInvoices == null)
		{
		this.log (Level.INFO, "No invoices to uprate.");
		return;
		}
	
	//=======================================================================
	//= In case there are invoices, we have to uprate ambulatory invoices
	//= to hospitalized invoices
	//=======================================================================
	
	l_HospitalizedPeriod = this.getLatestHospitalisationPeriod(p_Hospitalisation);
	if (l_HospitalizedPeriod != null)
		{
		l_HospitalizedClass = l_HospitalizedPeriod.getHospitalisationClass();
		l_InvoiceIterator = l_ExistingInvoices.iterator();
		while (l_InvoiceIterator.hasNext())
			{
			l_Invoice = l_InvoiceIterator.next();
			if (	(l_Invoice.getHospitalisationClass() != null) &&
					(l_Invoice.getHospitalisationClass().equals(this.m_HospitalisationClasses.get(c_Ambulatory))) &&
					(!l_Invoice.isLocked()) )
				{
				this.log (Level.INFO, "Updating ambulatory invoice " + l_Invoice.formatInvoiceNumber(false, true) + 
						                 " to " + l_HospitalizedClass.getAcronym() + ". class");
				
				l_Invoice.setHospitalisationClass(l_HospitalizedClass);
				l_Invoice = this.applyRules(l_Invoice);
				l_Invoice = this.saveInvoice(l_Invoice);
				}			
			}
		}
	}

//---------------------------------------------------------------------------

private Invoice applyRules (Invoice p_Invoice)
	{
	this.log(Level.INFO, "Applying Rules to Invoice");
	
	m_RuleInterface = this.getRuleInterface();

	if (m_RuleInterface != null)
		{
		try {
			p_Invoice = m_RuleInterface.applyRules (p_Invoice, null);
			p_Invoice.monetize();
			}
		catch (Exception p_Exception) 
			{
			this.log (Level.FATAL, "Failed to apply Rules to Invoice",p_Exception);
			}
		}
	return p_Invoice;
	}

//---------------------------------------------------------------------------

//===========================================================================
//= Admission Related Methods
//===========================================================================

//---------------------------------------------------------------------------
/**
 * Cancels the admission specified by the HL7 Admission object. Canceling
 * GECAMed admission is done first of all by looking for a hospitalisation
 * record matching the visit specified by HL7 Admission object. If a matching
 * hospitalisation record could be found, then the latest hospitalisation record
 * will simply be removed.
 * @param p_HL7Admission specifies the HL7 Admission object identifying the
 * admission to be canceled.
 */
//---------------------------------------------------------------------------

private void cancelAdmission (HL7Admission p_HL7Admission) throws Exception
	{
	Hospitalisation				l_Hospitalisation;
	Set <HospitalisationPeriod> 	l_HospitalisationPeriods;
	HospitalisationPeriod			l_HospitalisationPeriod;
	
	this.log (Level.INFO, "Canceling admission or transfer for visit " + p_HL7Admission.getVisitId());

	l_Hospitalisation = this.getHospitalisationByPassageId (p_HL7Admission.getVisitId().toString());
	
	if (l_Hospitalisation == null) return;
	
	l_HospitalisationPeriods = l_Hospitalisation.getHospitalisationPeriods();
	l_HospitalisationPeriod = this.getLatestHospitalisationPeriod (l_Hospitalisation);
	if (l_HospitalisationPeriod != null)
		{
		this.deleteHospitalisationPeriod (l_HospitalisationPeriod);
		l_HospitalisationPeriods.remove(l_HospitalisationPeriod);
		
		l_Hospitalisation.setHospitalisationPeriods(l_HospitalisationPeriods);
		}
	this.saveHospitalisation(l_Hospitalisation);
	}

//---------------------------------------------------------------------------
/**
 * Discharges the patient owning the hospitalisation record matching the
 * visit specified by the HL7 Admission object. Discharging the patient is
 * done by looking for a hospitalisation record matching the visit specified by 
 * the HL7 Admission object. If a matching hospitalisation record could be found, 
 * then the end date of the latest hospitalisation record will be set to the
 * discharge date specified in HL7 Admission object.
 * @param p_HL7Admission specifies the HL7 Admission object identifying the
 * hospitalisation record and thus the patient to discharge.
 */
//---------------------------------------------------------------------------

private void dischargePatient (HL7Admission p_HL7Admission) throws Exception
	{
	Hospitalisation				l_Hospitalisation;
	HospitalisationPeriod		l_HospitalisationPeriod;
	
	this.log (Level.INFO, "Discharging patient from visit " + p_HL7Admission.getVisitId());

	l_Hospitalisation = this.getHospitalisationByPassageId (p_HL7Admission.getVisitId().toString());
	
	if (l_Hospitalisation == null) return;
	
	l_HospitalisationPeriod = this.getLatestHospitalisationPeriod (l_Hospitalisation);
	if (l_HospitalisationPeriod != null)
		{
		l_HospitalisationPeriod.setEndDate(p_HL7Admission.getDischargeTime());
		this.saveHospitalisation(l_Hospitalisation);
		}
	}

//---------------------------------------------------------------------------
/**
 * Cancels a previous patient discharge operation. To do so, the method
 * looks up the hospitalisation record matching the visit specified by the
 * HL7 Admission object. If a matching hospitalisation record could be found,
 * the end date of the latest hospitialisation period will be reset.
* @param p_HL7Admission specifies the HL7 Admission object identifying the
 * hospitalisation record of interest.
 */
//---------------------------------------------------------------------------

private void cancelDischarge (HL7Admission p_HL7Admission) throws Exception
	{
	Hospitalisation				l_Hospitalisation;
	HospitalisationPeriod			l_HospitalisationPeriod;
	
	this.log (Level.INFO, "Canceling discharge of patient from visit " + p_HL7Admission.getVisitId());

	l_Hospitalisation = this.getHospitalisationByPassageId (p_HL7Admission.getVisitId().toString());
	
	if (l_Hospitalisation == null) return;
	
	l_HospitalisationPeriod = this.getLatestHospitalisationPeriod (l_Hospitalisation);
	if (l_HospitalisationPeriod != null)
		{
		l_HospitalisationPeriod.setEndDate(null);
		this.saveHospitalisation(l_Hospitalisation);
		}
	}

//***************************************************************************
//* Class Body                                                              *
//***************************************************************************

//---------------------------------------------------------------------------

public Collection <HL7Admission> getAllAdmissions() throws Exception 
	{
	Collection l_Admissions;

	try	{	
		l_Admissions = m_GECAMedEntityManager.createNamedQuery ("getAllHL7Admissions")
											 .setMaxResults(HL7Bean.c_MaxBatchSize)	
											 .getResultList();
		}
	catch (NoResultException p_Exception)
		{
		l_Admissions = null;
		}
	
	return l_Admissions;
	}

//---------------------------------------------------------------------------

public Collection <HL7Admission> getAdmissionsByEvent (String p_Event) throws Exception 
	{
	Collection 	l_Admissions;

	if (p_Event == null) return null;
	
	try	{	
		l_Admissions = m_GECAMedEntityManager.createNamedQuery ("getHL7AdmissionsByEvent")
		  								.setParameter("event", p_Event)
		  								.getResultList();
		}
	catch (NoResultException p_Exception)
		{
		l_Admissions = null;
		}

	return l_Admissions;
	}

//---------------------------------------------------------------------------

@TransactionAttribute (TransactionAttributeType.REQUIRES_NEW)
public void processAdmission (HL7Admission p_Admission) throws Exception
	{
	Patient 					l_Patient;
	PatientContact				l_Contact;
	Hospitalisation				l_Hospitalisation;
	int							l_EventType;
	long						l_Time;
	
	if (p_Admission == null) return;
	
	l_Time = System.currentTimeMillis();

	try	{
	
		this.log(Level.INFO, "Processing Admission with MessageID " + p_Admission.getMessageId());
		if (p_Admission.getVisitId() != null) this.log (Level.INFO, "Visit ID of Admission is " + p_Admission.getVisitId()); 
		
		l_EventType = p_Admission.getEvent();	
		
		if (p_Admission.getTriggerEvent() != null)
			this.log(Level.INFO, "Admission Event Type is " + p_Admission.getTriggerEvent() + "!");
		
		String eventType = null;
		switch (l_EventType)
			{
			case HL7Admission.c_AdmissionInPatient 	:
			case HL7Admission.c_AdmissionOutPatient 	:
			case HL7Admission.c_PreAdmissionInPatient 	:
				
				l_Patient = this.getPatientFromAdmission 	(p_Admission);
				this.setPatientInsuranceFromHL7Admission 	(l_Patient,p_Admission);				
				this.setPhoneNumbersFromHL7Admission	   	(l_Patient,p_Admission);
				this.setPatientAddressesFromHL7Admission 	(l_Patient,p_Admission);
				l_Patient = this.savePatient(l_Patient);
				
				l_Contact = this.setContactFromHL7Admission (l_Patient, p_Admission);
				l_Contact = this.saveContact(l_Contact);
				
				l_Hospitalisation	= this.addHospitalisationPeriodFromHL7Admission (l_Patient,p_Admission);
				l_Hospitalisation	= this.saveHospitalisation (l_Hospitalisation);
				break;
	
			case HL7Admission.c_UpdatePatient		:
	
				l_Patient = this.getPatientFromAdmission 	(p_Admission);
				if (applyAdtUpdate(l_Patient))
					{
					this.updatePatientFromHL7Admission 			(l_Patient,p_Admission);
					this.setPatientInsuranceFromHL7Admission 	(l_Patient,p_Admission);
					this.setPhoneNumbersFromHL7Admission	   	(l_Patient,p_Admission);
					this.setPatientAddressesFromHL7Admission 	(l_Patient,p_Admission);
					l_Patient = this.savePatient(l_Patient);
				
					l_Contact = this.setContactFromHL7Admission (l_Patient, p_Admission);
					l_Contact = this.saveContact(l_Contact);
					}
				
	//			l_Hospitalisation 	= this.updateHospitalisationPeriodFromHL7Admission (l_Patient,p_Admission);
	//			l_Hospitalisation	= this.saveHospitalisation (l_Hospitalisation);
				break;
			
			case HL7Admission.c_TransferPatient		:
			case HL7Admission.c_InToOutPatient		:
	//		case HL7Admission.c_SwapPatient			:
				
				l_Patient   		= this.getPatientFromAdmission (p_Admission);
				l_Hospitalisation	= this.addHospitalisationPeriodFromHL7Admission (l_Patient,p_Admission);
				l_Hospitalisation	= this.saveHospitalisation (l_Hospitalisation);
				break;
				
			case HL7Admission.c_OutToInPatient		:
				
				l_Patient   		= this.getPatientFromAdmission (p_Admission);
				l_Hospitalisation	= this.addHospitalisationPeriodFromHL7Admission (l_Patient,p_Admission);
				l_Hospitalisation	= this.saveHospitalisation (l_Hospitalisation);
									  this.uprateAmbulatoryInvoices(l_Hospitalisation);
				break;
	
			case HL7Admission.c_CancelAdmission		:
			case HL7Admission.c_CancelPreAdmission	:
			case HL7Admission.c_CancelTransfer		:
			
				this.cancelAdmission (p_Admission);
				break;
			
			case HL7Admission.c_DischargePatient	:
				
				l_Patient   		= this.getPatientFromAdmission (p_Admission);
				
				this.updatePatientFromHL7Admission 	(l_Patient,p_Admission);
				this.dischargePatient (p_Admission);
				l_Patient = this.savePatient(l_Patient);				
				break;
				
			case HL7Admission.c_CancelDischarge		:
				
				l_Patient   		= this.getPatientFromAdmission (p_Admission);
				
				this.updatePatientFromHL7Admission 	(l_Patient,p_Admission);
				this.cancelDischarge (p_Admission);
				l_Patient = this.savePatient(l_Patient);				
				break;		
			}
		
		this.deleteReferences(p_Admission);
		this.deleteHL7Admission(p_Admission);
		
		l_Time = System.currentTimeMillis() - l_Time;
		
		this.log(Level.INFO, "Processed HL7 Message " + p_Admission + " took " + l_Time + " ms to complete!");
		
		
		Log logEntry = new Log(LogType.SYSTEM, "HL7 ADT IMPORT", "HL7", "Processed HL7 Message " + p_Admission, l_Time);
		logManager.saveLog(logEntry);
		
		}
	catch (Exception p_Exception)
		{
		
		l_Time = System.currentTimeMillis() - l_Time;
		
		this.log(Level.ERROR, "Error while processing HL7 Message " + p_Admission + ". Rejecting Admission!",p_Exception);
		
		Log logEntry = new Log(LogType.SYSTEM, "HL7 ADT ERROR", "HL7", "Error while processing HL7 Message " + p_Admission + ". Rejecting Admission!", l_Time);
		logManager.saveLog(logEntry);
		
		this.quarantineReferences(p_Admission);
		this.quarantineHL7Admission(p_Admission);
		}
	}

//---------------------------------------------------------------------------

@TransactionAttribute (TransactionAttributeType.REQUIRES_NEW)
public void dailyTask () throws Exception 
	{
	long				 l_Time;
	
	l_Time = System.currentTimeMillis();
	this.log(Level.INFO, "Processing Daily Task now!");
	
	l_Time = System.currentTimeMillis() - l_Time;
	
	this.log(Level.INFO, "Processing of daily task took " + l_Time + " ms to complete!");
	}

//---------------------------------------------------------------------------

private boolean applyAdtUpdate (Patient p)
{
	if (p == null || p.getStatus() == null)
		return false;
	else if (Patient.STATUS_NEW == p.getStatus().intValue())
		return true;
	else
	{
		Boolean apply = (Boolean) m_LoginBean.getAdminSettingValue(
				SettingPluginNames.PATIENT_ADMIN_ELEM_PLUGIN_NAME,
				SettingConstants.ALWAYS_APPLY_ADT_UPDATES);
		
		if (apply == null)
			return SettingConstants.DEFAULT_ALWAYS_APPLY_ADT_UPDATES;
		else
			return apply.booleanValue();
	}
}

//---------------------------------------------------------------------------
//***************************************************************************
//* End of Class															*
//***************************************************************************
}
