/*******************************************************************************
 * 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.Hashtable;
import java.util.Iterator;
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.NonUniqueResultException;
import javax.persistence.PersistenceContext;

import lu.tudor.santec.gecamed.billing.ejb.entity.beans.Act;
import lu.tudor.santec.gecamed.billing.ejb.entity.beans.Invoice;
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.hl7import.ejb.entity.beans.HL7Address;
import lu.tudor.santec.gecamed.hl7import.ejb.entity.beans.HL7Bean;
import lu.tudor.santec.gecamed.hl7import.ejb.entity.beans.HL7Observation;
import lu.tudor.santec.gecamed.hl7import.ejb.entity.beans.HL7Phone;
import lu.tudor.santec.gecamed.hl7import.ejb.entity.beans.HL7Report;
import lu.tudor.santec.gecamed.hl7import.ejb.session.interfaces.ObservationInterface;
import lu.tudor.santec.gecamed.office.ejb.entity.beans.Physician;
import lu.tudor.santec.gecamed.patient.ejb.entity.beans.HospitalPrescription;
import lu.tudor.santec.gecamed.patient.ejb.entity.beans.HospitalReport;
import lu.tudor.santec.gecamed.patient.ejb.entity.beans.Hospitalisation;
import lu.tudor.santec.gecamed.patient.ejb.entity.beans.Passage;
import lu.tudor.santec.gecamed.patient.ejb.entity.beans.Patient;

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

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

@Service
@Local (ObservationInterface.class)

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

	private static Hashtable <String,Physician>			m_PhysiciansByName		= null;
	private static Hashtable <String,Physician>			m_PhysiciansByUCMCode	= null;
		
//***************************************************************************
//* Constants	                                                           *
//***************************************************************************

	private static final long serialVersionUID = 1L;

	private static final Category m_Logger = Category.getInstance(ObservationBean.class.getName());
	
	private static Pattern 
    
	c_HL7InterpreterPattern = Pattern.compile ("^(\\d*)&(\\w*)\\s(.*)",Pattern.CASE_INSENSITIVE);

    private static Pattern 
    
    c_HL7UCMCodePattern = Pattern.compile ("^(\\d{6})(\\d{2})?$",Pattern.CASE_INSENSITIVE);
		
//***************************************************************************
//* Constructor(s)                                                          *
//***************************************************************************

//@PostConstruct
//public void postConstruct ()
public void start ()
    {
	Collection	<Physician> 			l_Physicians;
	Iterator	<Physician>				l_PhysicianIterator;
	Physician							l_Physician;
	String								l_PhysicianName;
	
	try	{	
		
		//====================================================================
		//= Pretch List of available Physicians from GECAMed database
		//====================================================================
		
		if ((m_PhysiciansByName == null) || (m_PhysiciansByUCMCode == null))
			{
			m_PhysiciansByName 	 = new Hashtable <String,Physician> ();
			m_PhysiciansByUCMCode = new Hashtable <String,Physician> ();
			l_Physicians = this.getAllPhysicians();
			if (l_Physicians != null)
				{
				l_PhysicianIterator = l_Physicians.iterator();
				while (l_PhysicianIterator.hasNext())
					{
					l_Physician = l_PhysicianIterator.next();
					l_PhysicianName = l_Physician.getFirstName() + " " + l_Physician.getName();
	
					m_PhysiciansByName.put (l_PhysicianName.toLowerCase(),l_Physician);
					if (l_Physician.getUcmCode() != null)
						{
						m_PhysiciansByUCMCode.put (l_Physician.getUcmCode(),l_Physician);
						}
					}
				}
			}
		
		this.log (Level.INFO, "ObservationBean 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, "ObservationBean 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);
	}

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

//===========================================================================
//= HL7Interface Context Primitives									    	=
//===========================================================================

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

@SuppressWarnings("unchecked")
private Collection <HL7Report> getHL7ReportsByMessageId (Integer p_MessageId) throws Exception 
	{	
	Collection l_Reports;

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

	return l_Reports;
	}

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

private HL7Observation quarantineHL7Observation (HL7Observation p_Observation)
	{
	if (p_Observation == null) return null;
	
	p_Observation.setQuarantined(Boolean.TRUE);
	p_Observation = m_GECAMedEntityManager.merge(p_Observation);
		
	return p_Observation;
	}
//---------------------------------------------------------------------------

private void deleteHL7Observation (HL7Observation p_Observation)
	{
	if (p_Observation == null) return;
	
	p_Observation = m_GECAMedEntityManager.find (HL7Observation.class, p_Observation.getId());
	if ((p_Observation != null) && (p_Observation.getQuarantined() == false)) 
		m_GECAMedEntityManager.remove(p_Observation);
	}

//---------------------------------------------------------------------------
/**
 * 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 HL7Report quarantineHL7Report (HL7Report p_Report)
	{
	if (p_Report == null) return null;
	
	p_Report.setQuarantined(Boolean.TRUE);
	p_Report = m_GECAMedEntityManager.merge(p_Report);
		
	return p_Report;
	}
//---------------------------------------------------------------------------

private void deleteHL7Report (HL7Report p_Report)
	{
	if (p_Report == null) return;
	
	p_Report = m_GECAMedEntityManager.find (HL7Report.class, p_Report.getId());
	if ((p_Report != null) && (p_Report.getQuarantined() == false))
		m_GECAMedEntityManager.remove(p_Report);
	}

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

@SuppressWarnings("unchecked")
private void deleteReferences (HL7Observation p_Observation)
	{
	Collection <HL7Address> 	l_Addresses;
	Iterator <HL7Address> 		l_AddressIterator;
	HL7Address					l_Address;

	Collection <HL7Phone>   	l_Phones;
	Iterator   <HL7Phone>   	l_PhoneIterator;
	HL7Phone					l_Phone;
	
	Collection <HL7Report> 	l_Reports;
	Iterator <HL7Report> 	l_ReportIterator;
	HL7Report				l_Report;

	if (p_Observation == null) return;

	try	{	
		//====================================================================
		//= Delete orphaned HL7Address objects
		//====================================================================
		
		l_Addresses = m_GECAMedEntityManager.createNamedQuery ("getHL7AddressesByMessageId")
										.setParameter("messageid", p_Observation.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_Observation.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 HL7Passage objects
		//====================================================================
		
		l_Reports = m_GECAMedEntityManager.createNamedQuery ("getHL7ReportsByMessageId")
										.setParameter("messageid", p_Observation.getMessageId())
										.getResultList();
		
		if (l_Reports != null)
			{
			l_ReportIterator = l_Reports.iterator();
			while (l_ReportIterator.hasNext())
				{
				l_Report = l_ReportIterator.next();
				this.deleteHL7Report (l_Report);
				}	
			}
		
		}
	catch (Exception p_Exception)
		{
		this.log (Level.FATAL, "Failed to delete objects referenced by Observation with Message ID " + p_Observation.getMessageId(),p_Exception);
		}
	}

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

@SuppressWarnings("unchecked")
private void quarantineReferences (HL7Observation p_Observation)
	{
	Collection <HL7Address> 	l_Addresses;
	Iterator <HL7Address> 		l_AddressIterator;
	HL7Address					l_Address;

	Collection <HL7Phone>   	l_Phones;
	Iterator   <HL7Phone>   	l_PhoneIterator;
	HL7Phone					l_Phone;
	
	Collection <HL7Report> 	l_Reports;
	Iterator <HL7Report> 	l_ReportIterator;
	HL7Report				l_Report;

	if (p_Observation == null) return;

	try	{	
		//====================================================================
		//= Delete orphaned HL7Address objects
		//====================================================================
		
		l_Addresses = m_GECAMedEntityManager.createNamedQuery ("getHL7AddressesByMessageId")
										.setParameter("messageid", p_Observation.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_Observation.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 HL7Passage objects
		//====================================================================
		
		l_Reports = m_GECAMedEntityManager.createNamedQuery ("getHL7ReportsByMessageId")
										.setParameter("messageid", p_Observation.getMessageId())
										.getResultList();
		
		if (l_Reports != null)
			{
			l_ReportIterator = l_Reports.iterator();
			while (l_ReportIterator.hasNext())
				{
				l_Report = l_ReportIterator.next();
				this.quarantineHL7Report (l_Report);
				}	
			}
		
		}
	catch (Exception p_Exception)
		{
		this.log (Level.FATAL, "Failed to quarantining objects referenced by Observation with Message ID " + p_Observation.getMessageId(),p_Exception);
		}
	}


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

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

@SuppressWarnings("unchecked")
private Collection <Physician> getAllPhysicians () throws Exception
	{
	Collection	l_Physicians = null;
	
	try	{	
		l_Physicians = m_GECAMedEntityManager.createNamedQuery ("findAllPhysician")
									    		.getResultList();
		}
	catch (NoResultException p_Exception)
		{
		l_Physicians = null;
		}

	return l_Physicians;
	}

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

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

	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 Hospitalisation getHospitalisationByVisitId (String p_VisitId) throws Exception
	{
	Hospitalisation	l_Hospitalisation = null;
	
	try	{
		l_Hospitalisation = (Hospitalisation) m_GECAMedEntityManager.createNamedQuery ("getHospitalisationByPassageId")
											.setParameter("passageid", p_VisitId)
											.getSingleResult();
  		}
	catch (NoResultException p_Exception)
		{
		l_Hospitalisation = null;
		}
	
	return l_Hospitalisation;
	}

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

private Passage getPassageByAccessionNumber (String p_AccessionNumber) throws Exception
	{
	Passage l_Passage = null;
	
	try	{
		l_Passage = (Passage) m_GECAMedEntityManager.createNamedQuery (Passage.FIND_PASSAGE_BY_ACCESSION_NUMBER)
													.setParameter("accessionNumber", p_AccessionNumber)
													.setMaxResults(1)
													.getSingleResult();
  		}
	catch (NoResultException p_Exception)
		{
		l_Passage = null;
		}
	
	return l_Passage;
	}

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

private Act getActByAccessionNumber (String p_AccessionNumber) throws Exception
	{
	Act 	l_Act;
	
	try	{
		l_Act = (Act) m_GECAMedEntityManager.createNamedQuery ("getActByAccessionNumber")
	 										.setParameter("accessionNumber", p_AccessionNumber)
	 										.getSingleResult();
		}
	catch (NoResultException p_Exception)
		{
		l_Act = null;
		}
	catch (NonUniqueResultException p_Exception)
		{
		this.log (Level.WARN, "Found more than one act for Accession Number " + p_AccessionNumber + "! Returning only latest one!");
		l_Act = this.getLatestAct(p_AccessionNumber);
		}
	
	return l_Act;
	}

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

private Invoice getInvoiceById (Integer p_InvoiceId) throws Exception
	{
	Invoice	l_Invoice;
	
	if (p_InvoiceId == null) return null;
	
	try	{
		l_Invoice = (Invoice) m_GECAMedEntityManager.find(Invoice.class, p_InvoiceId);
		l_Invoice.updateFirstClassRequired();
		}
	catch (NoResultException p_Exception)
		{
		l_Invoice = null;
		}
	
	return l_Invoice;
	}

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

private Invoice getInvoiceByAccessionNumber (String p_AccessionNumber) throws Exception
	{
	Act 	l_Act;
	Invoice	l_Invoice;
	
	l_Act = this.getActByAccessionNumber(p_AccessionNumber);
	
	if (l_Act == null) return null;
	
	l_Invoice = this.getInvoiceById(l_Act.getInvoiceId());
	
	return l_Invoice;
	}

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

@SuppressWarnings("unchecked")
private Act getLatestAct (String p_AccessionNumber) throws Exception
	{
	Collection <Act> l_Acts;
	Iterator <Act>	 l_ActIterator;
	Act				 l_Current;
	Act				 l_Latest = null;
	
	try	{
		l_Acts = m_GECAMedEntityManager.createNamedQuery ("getActByAccessionNumber")
 									   .setParameter("accessionNumber", p_AccessionNumber)
 									   .getResultList();
		}
	catch (NoResultException p_Exception)
		{
		l_Acts = null;
		}

	if (l_Acts == null) return null;
	
	l_ActIterator = l_Acts.iterator();
	while (l_ActIterator.hasNext())
		{
		l_Current = l_ActIterator.next();
		
		if ((l_Latest == null) || (l_Current.getId() > l_Latest.getId())) 
			{
			l_Latest = l_Current;
			}
		}
	
	return l_Latest;
	}

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

private HospitalPrescription getPrescriptionById (Integer p_PrescriptionId) throws Exception
	{
	HospitalPrescription l_Prescription = null;
	
	try	{
		l_Prescription = m_GECAMedEntityManager.find (HospitalPrescription.class,p_PrescriptionId);
  		}
	catch (NoResultException p_Exception)
		{
		l_Prescription = null;
		}
	
	return l_Prescription;
	}

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

private HospitalPrescription getPrescriptionByOrderNumber (String p_OrderNumber) throws Exception
	{
	HospitalPrescription l_Prescription = null;
	
	try	{
		l_Prescription = (HospitalPrescription) m_GECAMedEntityManager.createNamedQuery (HospitalPrescription.GET_BY_ORDER_NUMBER)
																	  .setParameter("orderNumber", p_OrderNumber)
																	  .setMaxResults(1)
																	  .getSingleResult();
  		}
	catch (NoResultException p_Exception)
		{
		l_Prescription = null;
		}
	
	return l_Prescription;
	}

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

private Passage savePassage (Passage p_Passage) throws Exception
	{
	if (p_Passage == null) return null;
	
	p_Passage = m_GECAMedEntityManager.merge(p_Passage);
	
	return p_Passage;	
	}

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

private HospitalPrescription savePrescription (HospitalPrescription p_Prescription) throws Exception
	{
	if (p_Prescription == null) return null;
	
	p_Prescription = m_GECAMedEntityManager.merge(p_Prescription);
	
	return p_Prescription;	
	}

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

private HospitalReport saveReport (HospitalReport p_Report) throws Exception
	{
	if (p_Report == null) return null;
	
	p_Report = m_GECAMedEntityManager.merge(p_Report);
	
	return p_Report;	
	}

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

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

	p_Invoice.monetize();
		
	p_Invoice = m_GECAMedEntityManager.merge (p_Invoice);
	p_Invoice.updateFirstClassRequired();
	
	return p_Invoice;
	}

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

private Act saveAct (Act p_Act) throws Exception
	{	
	if (p_Act == null) return null;

	p_Act.monetize();
		
	p_Act = m_GECAMedEntityManager.merge (p_Act);	
	
	return p_Act;
	}

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

//---------------------------------------------------------------------------
/**
 * The getPhysicianFromHL7Report method attempts to lookup the physician in
 * the GECAMed database that was specified has interpreter in the HL7Report
 * object. Matching is done using either the UCM code if available or the
 * via the name of the physician. If lookup was successful, the matching
 * physician object returned, otherwise <code>null</code>will be returned.
 * @param p_HL7Report specifies the HL7Report object containing the data
 * of the physician to look for.
 * @return returns the matching physician from the GECAMed database if lookup
 * was successful, <code>null</code> otherwise.
 */
//---------------------------------------------------------------------------

private Physician getPhysicianFromHL7Report (HL7Report p_HL7Report) throws Exception
	{
	Matcher		l_InterpreterMatcher;
	Matcher		l_UCMCodeMatcher;
	
	String		l_UCMCode;
	String		l_Name;
	String		l_FirstName;
	
	Physician	l_Physician	= null;
	
	if ((p_HL7Report == null) || (p_HL7Report.getInterpreter()==null)) return null;

	l_InterpreterMatcher = c_HL7InterpreterPattern.matcher (p_HL7Report.getInterpreter());
	if (l_InterpreterMatcher.matches())
		{
		l_UCMCode 	= l_InterpreterMatcher.group(1);
		l_Name 		= l_InterpreterMatcher.group(2);
		l_FirstName = l_InterpreterMatcher.group(3);
		
		//====================================================================
		//= Check first of all whether specified UCM code is complete. If so,
		//= we may use it to lookup physician from GECAMed database.
		//====================================================================
		
		if (l_UCMCode != null)
			{
			l_UCMCodeMatcher = c_HL7UCMCodePattern.matcher (l_UCMCode);
			if (l_UCMCodeMatcher.matches() && (l_UCMCodeMatcher.groupCount() == 2))
				{
				l_UCMCode = l_UCMCodeMatcher.group(1) + "-" + l_UCMCodeMatcher.group(2);
				l_Physician = m_PhysiciansByUCMCode.get(l_UCMCode);
				}
			}
		
		//====================================================================
		//= If either UCM code was not complete or didn't return a valid
		//= match, then we're attempting to lookup physician via name.
		//====================================================================
		
		if (l_Physician == null)
			{
			l_Name = l_FirstName + " " + l_Name;
			l_Physician = m_PhysiciansByName.get(l_Name.toLowerCase());
			}
		}
	
	if (l_Physician == null)
		{
		this.log (Level.WARN, "Failed to match physician " + p_HL7Report.getInterpreter());
		}
	
	return l_Physician;
	}

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

private HospitalPrescription getPrescriptionForPassage (Passage p_Passage) throws Exception
	{
	HospitalPrescription	l_Prescription		= null;

	if (p_Passage == null) return null;
	
	if (p_Passage.getHospitalPrescriptionId() != null)
		{
		this.log(Level.INFO, "Fetching Prescription with ID " + p_Passage.getHospitalPrescriptionId());
		
		l_Prescription = this.getPrescriptionById(p_Passage.getHospitalPrescriptionId());
		}
			
	if (l_Prescription == null)
		{
		this.log(Level.INFO, "Not assigned to a prescription yet! Creating a new one!");
			
		l_Prescription = new HospitalPrescription ();
		l_Prescription.setOrderNumber(HospitalPrescription.NOT_AVAILABLE);
		l_Prescription.setHospperiodId(p_Passage.getHospperiodId());
		l_Prescription = this.savePrescription(l_Prescription);
		}
	
	return l_Prescription;
	}

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

private void updatePhysicianOfAct (Physician p_Physician, String p_AccessionNumber) throws Exception
	{
	Act			l_Act;
	Invoice		l_Invoice;
	Integer		l_CommonPhysicianId	= Invoice.c_NoCommonPhysician;
	
	if ((p_Physician == null) || (p_AccessionNumber == null)) return;
	
	this.log(Level.INFO, "Looking for act with accession number " + p_AccessionNumber);

	l_Act = this.getActByAccessionNumber (p_AccessionNumber);
	if (l_Act != null)
		{				
		if ((l_Act.getPhysicianId() == null) || (!l_Act.getPhysicianId().equals(p_Physician.getId())))
			{
			this.log (Level.INFO, "Performing physician of act with accession number " + l_Act.getAccessionNumber() + 
									 " differs from Interpreting physician, who is " + p_Physician.toString() + 
									  "! Changing performing physician of act to interpreting physician!");
			
			l_Act.setPhysicianId(p_Physician.getId());
			l_Act = this.saveAct(l_Act);
			
			l_Invoice = this.getInvoiceById(l_Act.getInvoiceId());
			if (l_Invoice != null)
				{
				l_CommonPhysicianId = l_Invoice.getCommonPhysicianId();
				if (l_CommonPhysicianId.equals(p_Physician.getId()))
					{
					this.log (Level.INFO, "Invoice " + l_Invoice.formatInvoiceNumber(Invoice.c_LongFormat, true) + 
									 		 " contains only acts of same physician. Setting physician of invoice also to intrepreting pyhsician!");
					
					l_Invoice.setPhysician (p_Physician);
					l_Invoice = this.saveInvoice(l_Invoice);
					}
				}
			}
		}
	}

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

private Passage updatePassage (Passage p_Passage, Physician p_Physician, HospitalPrescription p_Prescription) throws Exception
	{
	boolean l_WasModified = false;
	
	if ((p_Passage == null) || (p_Physician == null)) return p_Passage;

	if ((p_Passage.getPhysicianId() == null) || (!p_Passage.getPhysicianId().equals(p_Physician.getId())))
		{
		p_Passage.setPhysicianId(p_Physician.getId());
		l_WasModified = true;
		}
				
	if ((p_Passage.getHospitalPrescriptionId() == null) && (p_Prescription != null))
		{
		p_Passage.setHospitalPrescriptionId (p_Prescription.getId());
		l_WasModified = true;
		}
				
	if (l_WasModified) p_Passage = this.savePassage(p_Passage);
	
	return p_Passage;
	}

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

private void newReportFromHL7Report (HL7Report p_HL7Report, Physician p_Physician, HospitalPrescription p_Prescription) throws Exception
	{
	HospitalReport			l_Report = null;

	if ((p_HL7Report == null) || (p_Physician == null) || (p_Prescription == null)) return;
	
	//================================================================
	//= Iif HL7Report actually contains data (normally the
	//= last one), only then will we create a new report in the
	//= GECAMed database, using the data provided by HL7Report object.
	//================================================================

	if (p_HL7Report.getReport() != null)
		{
		l_Report = new HospitalReport ();
		l_Report.setInterpreterID(p_Physician.getId());
		l_Report.setDate(p_HL7Report.getReportGenerationDate());
		l_Report.setData(p_HL7Report.getReport());
		l_Report.setHospitalPrescriptionID(p_Prescription.getId());
		l_Report = this.saveReport(l_Report);
		}
	}

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

private void newReportFromObservation (Patient p_Patient, HL7Observation p_Observation) throws Exception
	{
	Hospitalisation			l_Hospitalisation	= null;
	HospitalPrescription	l_Prescription		= null;
	Collection <HL7Report>	l_HL7Reports		= null;
	Iterator <HL7Report>	l_HL7ReportIterator	= null;
	HL7Report				l_HL7Report			= null;
	
	String					l_OrderNumber		= null;
	String					l_AccessionNumber	= null;
	
	Passage					l_Passage			= null;
	Physician				l_Physician			= null;
	
	//========================================================================
	//= Step 1 First of all, we're trying to a hospitalisation matching the
	//= visit id specified by the observation. If none could be found, then
	//= we'll abort processing of observation right away.
	//========================================================================
	
	this.log(Level.INFO, "Looking for Hospitalisation with Visit ID " + p_Observation.getVisitId().toString());

	l_Hospitalisation = this.getHospitalisationByVisitId (p_Observation.getVisitId().toString());
	if (l_Hospitalisation == null)
		{
		this.log(Level.WARN, "No matching Hospitalisation found! Putting Observation into quarantine!");
		this.quarantineReferences (p_Observation);
		this.quarantineHL7Observation (p_Observation);
		return;
		}
	
	if (p_Patient != null)
		{
		if (   (l_Hospitalisation.getPatientId() == null)
			|| (!l_Hospitalisation.getPatientId().equals(p_Patient.getId())))
			{
			this.log (Level.WARN, "Specified Patient and Patient associated with Hospitalisation do not match! Putting Observation into quarantine!");
			this.quarantineReferences (p_Observation);
			this.quarantineHL7Observation (p_Observation);
			return;
			}
		}
		
	//========================================================================
	//= Step 2 If a matching hospitalisation could be found, we're fetching
	//= the reports which are attached to specified observation. The reports
	//= attached to the observation merely specify which passages are affected
	//= be the observation. Only the last HL7Report object contains the actual
	//= data of the actual interpretation itself.
	//========================================================================

	this.log(Level.INFO, "Looking for Reports with MessageID " + p_Observation.getMessageId());
	 
	l_HL7Reports = this.getHL7ReportsByMessageId (p_Observation.getMessageId());
	if (l_HL7Reports != null)
		{
		this.log(Level.INFO, "Found " + l_HL7Reports.size() + " Reports!");
		
		l_HL7ReportIterator = l_HL7Reports.iterator();
		while (l_HL7ReportIterator.hasNext())
			{
			l_HL7Report = l_HL7ReportIterator.next();

			//================================================================
			//= Step 3 For each HL7Report, we're going to do the following things.
			//= First of all, we're retrieving the interpreting physician. 
			//= If the physician is unkown to the system, we're skiping the
			//= report.
			//================================================================
			
			l_Physician = this.getPhysicianFromHL7Report (l_HL7Report);
			if (l_Physician == null)
				{
				this.log (Level.WARN, "Received Report from unkown physician! Message Control ID was " + p_Observation.getControlId());
				continue;
				}

			//================================================================
			//= Step 4 For each HL7Report, we're attempting to locate the
			//= passage specified by the enclosed accession number and the
			//= prescription specified by the enclosed order number.
			//================================================================
						
			l_AccessionNumber = l_HL7Report.getPlacerOrderNumber();
			l_OrderNumber     = l_HL7Report.getFillerOrderNumber();

			this.log(Level.INFO, "Looking for passage with accession number " + l_AccessionNumber);
			
			l_Passage = this.getPassageByAccessionNumber(l_AccessionNumber);
			if (l_Passage == null)
				{
				this.log (Level.WARN, "No passage found for Accession Number " + l_AccessionNumber + ".Putting Report into quarantine!");
				this.quarantineHL7Observation(p_Observation);
				this.quarantineHL7Report(l_HL7Report);
				continue;
				}
			else this.log(Level.INFO, "Found!");
			
			//================================================================
			//= Step 5 For each HL7Report, we're attempting to locate the
			//= act specified by the enclosed accession number in order to
			//= update the physician who performed the acr
			//================================================================
			
			this.updatePhysicianOfAct (l_Physician, l_AccessionNumber);
			 			
			//================================================================
			//= Step 6 consists in looking for the prescription this report
			//= refers to. Locating the prescription is done via the order
			//= number. If no matching prescription could be found, we're
			//= simply creating a new one (without a scan) in order to attach
			//= the passage and report.
			//================================================================
			
			if ((l_Prescription == null) || (!l_Prescription.getOrderNumber().equals(l_OrderNumber)))
				{
				this.log(Level.INFO, "Looking for prescription with order number " + l_OrderNumber);
				
				l_Prescription = this.getPrescriptionByOrderNumber (l_OrderNumber);
				if (l_Prescription == null)
					{
					this.log(Level.INFO, "Not found! Creating a new one!");
					
					l_Prescription = new HospitalPrescription ();
					l_Prescription.setOrderNumber (l_OrderNumber);
					l_Prescription.setHospperiodId (l_Passage.getHospperiodId());
					l_Prescription = this.savePrescription (l_Prescription);
					}
				}
			
			//================================================================
			//= Step 7. Update Passage
			//================================================================
			
			l_Passage = this.updatePassage (l_Passage, l_Physician, l_Prescription);
						
			//================================================================
			//= Step 8 Create Report from HL7Report
			//================================================================

			this.newReportFromHL7Report(l_HL7Report, l_Physician, l_Prescription);
			
			this.deleteHL7Report(l_HL7Report);
			}
		}
	}

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


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

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

@SuppressWarnings("unchecked")
public Collection <HL7Observation> getAllObservations() throws Exception 
	{
	Collection l_Observations;

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

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

@TransactionAttribute (TransactionAttributeType.REQUIRES_NEW)
public void processObservation (HL7Observation p_Observation) throws Exception 
	{
	Patient 				l_Patient = null;
	long					l_Time;
	
	if (p_Observation == null) return;
	
	l_Time = System.currentTimeMillis();

	try	{	
		this.log(Level.INFO, "Processing Observation with MessageID " + p_Observation.getMessageId());
	 	
		if (p_Observation.getPatientId() != null)
			{
			this.log(Level.INFO,"Looking for patient with RIS ID " + p_Observation.getPatientId().toString());	
			l_Patient = this.getPatientByRISid (p_Observation.getPatientId().toString());
			}
		
		if ((l_Patient != null) && (l_Patient.isPersistent()))
			{
			this.newReportFromObservation(l_Patient, p_Observation);
			}
		else
			{
			this.log(Level.INFO,"Couldn't find patient by RIS ID. Using Visit ID instead!");	
			this.newReportFromObservation (null, p_Observation);
				
//			this.log(Level.WARN,"Couldn't find patient with RIS ID " + p_Observation.getPatientId().toString() +
//			   ". Putting Observation into Quarantine!");
//			this.quarantineReferences (p_Observation);
//			this.quarantineHL7Observation (p_Observation);
			}
		
		this.deleteReferences(p_Observation);
		this.deleteHL7Observation(p_Observation);
	
		l_Time = System.currentTimeMillis() - l_Time;
		
		this.log(Level.INFO, "Processed HL7 Message " + p_Observation +
				                 " took " + l_Time + " ms to complete!");
		
		Log logEntry = new Log(LogType.SYSTEM, "HL7 ORU IMPORT", "HL7", "Processed HL7 Message " + p_Observation, l_Time);
		logManager.saveLog(logEntry);
		}
	catch (Exception p_Exception)
		{
		this.log(Level.ERROR, "Error while processing HL7 Message " + p_Observation + ". Putting Observation into Quarantine!",p_Exception);
		
		Log logEntry = new Log(LogType.SYSTEM, "HL7 ORU ERROR", "HL7", "Error while processing HL7 Message " + p_Observation + ". Putting Observation into Quarantine!", l_Time);
		logManager.saveLog(logEntry);
		
		
		this.quarantineReferences (p_Observation);
		this.quarantineHL7Observation (p_Observation);
		}
	}
	
//---------------------------------------------------------------------------

@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!");
	}

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

}
