/*******************************************************************************
 * 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.labo.ejb.session.beans;

import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.LineNumberReader;
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.annotation.security.RolesAllowed;
import javax.ejb.EJB;
import javax.ejb.Remote;
import javax.ejb.Remove;
import javax.ejb.Stateful;
import javax.persistence.NoResultException;

import lu.tudor.santec.gecamed.addressbook.ejb.entity.beans.Contact;
import lu.tudor.santec.gecamed.core.ejb.session.beans.GECAMedSessionBean;
import lu.tudor.santec.gecamed.core.utils.SSNChecker;
import lu.tudor.santec.gecamed.core.utils.UCMCodeFormatter;
import lu.tudor.santec.gecamed.core.utils.entitymapper.XML2EntityMapper;
import lu.tudor.santec.gecamed.labo.ejb.entity.beans.Connection;
import lu.tudor.santec.gecamed.labo.ejb.entity.beans.Laboratory;
import lu.tudor.santec.gecamed.labo.ejb.entity.beans.LaboratoryAddress;
import lu.tudor.santec.gecamed.labo.ejb.entity.beans.LaboratoryCertificate;
import lu.tudor.santec.gecamed.labo.ejb.entity.beans.PhysicianKey;
import lu.tudor.santec.gecamed.labo.ejb.entity.beans.Result;
import lu.tudor.santec.gecamed.labo.ejb.session.interfaces.CryptoInterface;
import lu.tudor.santec.gecamed.labo.ejb.session.interfaces.ImportInterface;
import lu.tudor.santec.gecamed.labo.ejb.session.interfaces.LaboratoryInterface;
import lu.tudor.santec.gecamed.labo.ejb.session.interfaces.ResultInterface;
import lu.tudor.santec.gecamed.labo.utils.CertificateSynchronizer;
import lu.tudor.santec.gecamed.labo.utils.DecryptException;
import lu.tudor.santec.gecamed.labo.utils.LaboException;
import lu.tudor.santec.gecamed.labo.utils.PasswordEncrypter;
import lu.tudor.santec.gecamed.labo.utils.ResultDownloader;
import lu.tudor.santec.gecamed.labo.utils.ResultParser;
import lu.tudor.santec.gecamed.office.ejb.entity.beans.Physician;
import lu.tudor.santec.gecamed.patient.ejb.entity.beans.Incident;
import lu.tudor.santec.gecamed.patient.ejb.entity.beans.IncidentEntry;
import lu.tudor.santec.gecamed.patient.ejb.entity.beans.IncidentEntryType;
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.session.interfaces.IncidentManager;

import org.apache.log4j.Level;

@Stateful
@Remote (ImportInterface.class)

public class ImportBean extends GECAMedSessionBean implements ImportInterface
	{
	
	@EJB
	CryptoInterface					m_CryptoBean;
	
	@EJB
	LaboratoryInterface				m_LaboratoryBean;
	
	@EJB
	ResultInterface					m_ResultBean;
	
	@EJB
	IncidentManager					m_IncidentBean;
	
	private PhysicianKey			m_PhysicianKey;
	private String					m_KeyPassword;
	
	private CertificateSynchronizer	m_Synchronizer;
	private ResultDownloader		m_Downloader;
	private PasswordEncrypter		m_PasswordEncrypter;
	
//---------------------------------------------------------------------------
//***************************************************************************
//* Constants	                                                            *
//***************************************************************************
//---------------------------------------------------------------------------	
    
	private static final int c_MaxLookForPatternLines = 10;
    
//    private static byte[] c_Utf8BOM = { (byte)0xEF, (byte)0xBB, (byte)0xBF };
    
    protected static Pattern 
    
    c_EncryptionPattern = Pattern.compile ("^Content-Type: application/(x-)?pkcs7-mime",Pattern.CASE_INSENSITIVE);
    
    private static Pattern 
    
    c_FilenamePattern = Pattern.compile ("^(\\d{6}-\\d{2}[A-Z]?)(\\d{6}-\\d{2}[A-Z]?)(\\d{10})\\.xml$",Pattern.CASE_INSENSITIVE);
	
//---------------------------------------------------------------------------
//***************************************************************************
//* Constructor	                                                            *
//***************************************************************************
//---------------------------------------------------------------------------

    
//---------------------------------------------------------------------------
//***************************************************************************
//* Primitives	                                                            *
//***************************************************************************
//---------------------------------------------------------------------------    

private ResultDownloader getDownloader ()
	{
	if (m_Downloader == null) m_Downloader = new ResultDownloader ();
	
	return m_Downloader;	
	}
    
//---------------------------------------------------------------------------    

private Connection decryptConnectionPassword (Connection p_Connection) throws LaboException
	{
	Connection l_Connection;
	
	if (m_PasswordEncrypter == null) return p_Connection;
	
	if ((p_Connection != null) && (p_Connection.getPassword() != null))
		{
		l_Connection = (Connection) p_Connection.clone();
		l_Connection.setPassword (m_PasswordEncrypter.decryptPassword(p_Connection.getPassword()));
		}
	else l_Connection = p_Connection;
	
	return l_Connection;
	}

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

private Patient	getPatientBySocialSecurityNumber (String p_SocialSecurityNumber) throws Exception
	{
	Patient	l_Patient;
		
	try	{
		l_Patient = (Patient) this.getEntityManager().createNamedQuery("findPatientBySocialSecurityNumber")
									  				 .setParameter("socialSecurityNumber", p_SocialSecurityNumber)
									  				 .setMaxResults(1)
									  				 .getSingleResult();
		}
	catch (NoResultException p_Exception)
		{
		l_Patient = null;
		}
	
	return l_Patient;
	}

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

//private Patient savePatient (Patient p_Patient) throws Exception
//	{
//	if (p_Patient == null) return p_Patient;
//	
//	p_Patient = this.getEntityManager().merge (p_Patient);
//	
//	return p_Patient;
//	}

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

private Physician	getPhysicianByUCMCode (String p_UCMCode) throws Exception
	{
	Physician	l_Physician;
		
	try	{
		l_Physician = (Physician) this.getEntityManager().createNamedQuery("getPhysicianByUCMCode")
									  				 .setParameter("ucmCode", p_UCMCode)
									  				 .setMaxResults(1)
									  				 .getSingleResult();
		}
	catch (NoResultException p_Exception)
		{
		l_Physician = null;	
		}
	
	return l_Physician;
	}

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

public Result saveResult (Result p_Result) throws Exception
	{
//	try {
		IncidentEntry l_IncidentEntry;
		
		if (p_Result == null)
			return null;
		
		if (p_Result.getId() == null)
		{
			l_IncidentEntry = this.createIncidentForResult (p_Result);
			if ((l_IncidentEntry != null) && (l_IncidentEntry.isPersistent()))		
				p_Result.setIncidentEntryId(l_IncidentEntry.getId());
		}
		
		if (p_Result != null) p_Result = this.getEntityManager().merge(p_Result);
		return p_Result;
//		}
//	catch (Exception p_Exception)
//		{
//		log(Level.ERROR, "ERROR while saving Result", p_Exception);
//		throw p_Exception;
//		}
	}

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

private Incident saveIncident (Incident p_Incident) throws Exception
	{
	if (p_Incident != null) p_Incident = m_IncidentBean.saveIncident (p_Incident);
	
	return p_Incident;
	}

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

private IncidentEntry saveIncidentEntry (IncidentEntry p_Entry) throws Exception
	{
	if (p_Entry != null) p_Entry = m_IncidentBean.saveEntry (p_Entry, 
															 Result.c_LaboResultIncident,
															 Result.c_LaboResultMime);
	
	return p_Entry;
	}

//---------------------------------------------------------------------------    
/**
 * scans the first couple lines of the specified data for the specified pattern. 
 * The number of lines being scanned is defined the c_MaxLookForPatternLines constant.
 * @return <code>true</code> if the specified pattern was encounter in the first few
 * lines of the stream, <code>false</code> otherwise.
 */
//---------------------------------------------------------------------------    
   
private boolean findPattern (InputStream p_Stream, Pattern p_Pattern)
    {
    LineNumberReader l_Reader      		= null;
    Matcher 		 l_PatternMatcher	= null;
    String           l_Line;
    boolean          l_Found = false;
    
    if (p_Stream == null) 
    	{
    	log(Level.WARN, "The given stream was null!");
    	return false;
    	}
    
    try {
        l_Reader = new LineNumberReader (new InputStreamReader(p_Stream));
      
        do  {
            l_Line = l_Reader.readLine();
            if (l_Line != null)
                {
                l_PatternMatcher = p_Pattern.matcher (l_Line);
                l_Found = l_PatternMatcher.find();
                }
                
            } while (   (l_Line != null) 
                     && (l_Reader.getLineNumber() < c_MaxLookForPatternLines) 
                     && !l_Found  );
        }    
    catch (Exception p_Exception)
        {
        this.log (Level.ERROR,"Error while scanning for pattern " + p_Pattern.pattern() + "!", p_Exception);
        l_Found = false;
        }
    finally 
        {
        try {
            l_Reader.close();
            } 
        catch(Exception p_Exception) 
            {
            this.log (Level.WARN,"Error while closing stream !", p_Exception);
            l_Found = false;
            }
        }
    
    return l_Found;    
    }

//---------------------------------------------------------------------------    
/** checks the encryption state of the specified stream by scanning the first lines
 *  for the s/mime encryption
 * @return <code>true</code> if the s/mime content type was encounter in the first 
 * lines of the stream, <code>false</code> otherwise.
 * @see #findPattern(File, Pattern)
 */
//---------------------------------------------------------------------------    
   
private boolean getEncryptionState (InputStream p_ResultStream)
    {
    return findPattern (p_ResultStream, c_EncryptionPattern);    
    }

//---------------------------------------------------------------------------
/**
 * checks whether the specified stream contains a valid Result. Validity check 
 * consists in checking whether data in stream is encrypted or not. 
 * @param p_ResultStream specifies the stream to check validity of.
 * @return <CODE>true</CODE> if data in specified stream is valid, 
 * <CODE>false</CODE> otherwise.
 */
//---------------------------------------------------------------------------

private boolean isValid (InputStream p_ResultStream)
    {
    boolean         l_Validity;
    boolean         l_SecureMode;
    
    l_Validity 	 = false;
    l_SecureMode = true;
    
    if (l_SecureMode == true)
        l_Validity = this.getEncryptionState (p_ResultStream);
    else
    	l_Validity = true;
    
    return l_Validity;
   }
	
//---------------------------------------------------------------------------

private byte[] skipByteOrderMarker (byte[] p_Content)
	{
	int		l_Count;
	int		l_BOMLength;
	boolean	l_BOMSkipped = false;
	byte[]	l_WithoutBOM = null;
	
	l_BOMLength = 0;
	while ((l_BOMLength < p_Content.length) && (!l_BOMSkipped))
		{
		if (p_Content [l_BOMLength] == '<')
			 l_BOMSkipped = true;
		else l_BOMLength++;
		}
	
	if (l_BOMSkipped && (l_BOMLength > 0))
		{
		l_WithoutBOM = new byte[p_Content.length - l_BOMLength];
		for (l_Count = 0; l_Count < l_WithoutBOM.length; l_Count++)
			{
			l_WithoutBOM [l_Count] = p_Content [l_BOMLength + l_Count]; 
			}
		}
	else l_WithoutBOM = p_Content;
	
	return 	l_WithoutBOM;
	}

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

private PhysicianKey	getKey (String p_ResultFileName) throws Exception
	{
	Matcher			l_FileNameMatcher;
	String			l_KeyLabel;
	PhysicianKey	l_Key	= null;	
	
	l_FileNameMatcher = c_FilenamePattern.matcher(p_ResultFileName);
	if (l_FileNameMatcher.matches())
		{
		l_KeyLabel = l_FileNameMatcher.group(1);	
		l_Key = m_CryptoBean.getKeyByLabel(l_KeyLabel);	
		if (l_Key == null)
			{
			this.log (Level.WARN, "Could not find a physician key with label " + l_KeyLabel + "!");	
			}
		}
	else throw new DecryptException(ImportInterface.FILENAME_DOESNT_MATCH_PATTERN);
	return l_Key;
	}

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

private LaboratoryCertificate getCertificate (String p_ResultFileName) throws Exception
	{
	Matcher					l_FileNameMatcher;
	String					l_CertificateLabel;
	LaboratoryCertificate	l_Certificate	= null;	
	
	l_FileNameMatcher = c_FilenamePattern.matcher(p_ResultFileName);
	if (l_FileNameMatcher.matches())
		{
		l_CertificateLabel = l_FileNameMatcher.group(2);	
		l_Certificate = m_LaboratoryBean.getCertificateByLabel(l_CertificateLabel);
		if (l_Certificate == null)
			{
			this.log (Level.WARN, "Could not find a laboratory certificate with label " + l_CertificateLabel + "!");	
			}
		}
	return l_Certificate;
	}

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

private Integer getResultNumber (String p_ResultFileName) throws Exception
	{
	Matcher		l_FileNameMatcher;
	String		l_ResultNumber;
	Integer		l_Number	= null;	
	
	l_FileNameMatcher = c_FilenamePattern.matcher(p_ResultFileName);
	if (l_FileNameMatcher.matches())
		{
		l_ResultNumber = l_FileNameMatcher.group(3);	
		try	{
			l_Number = Integer.parseInt(l_ResultNumber);
			}
		catch (NumberFormatException p_Exception)
			{
			this.log (Level.WARN, "Failed to convert " + l_ResultNumber + " to integer!");	
			}
		}
	return l_Number;
	}

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

private Laboratory	updateContactsForLaboratory (Laboratory p_Laboratory, Set <Contact>	p_Contacts) throws Exception
	{
	Set <Contact>		l_ExistingContacts;
	Iterator <Contact>	l_ContactIterator;
	Contact				l_Contact;
	String				l_ContactKey;
	
	Set <Contact>		l_NewContacts;
	
	Hashtable <String,Contact>	l_ContactLookup = null;
	
	if ((p_Laboratory == null) || (p_Contacts == null)) return p_Laboratory;
		
	//========================================================================
	//= 1. Build a lookup table of already existing contact persons for specified
	//= laboratory
	//========================================================================
	
	l_ExistingContacts = p_Laboratory.getContactPersons();
	if (l_ExistingContacts != null)
		{
		l_ContactLookup = new Hashtable <String,Contact> ();
		l_ContactIterator = l_ExistingContacts.iterator();
		while (l_ContactIterator.hasNext())
			{
			l_Contact    = l_ContactIterator.next();	
			l_ContactKey = l_Contact.getNameFirst() + "_" + l_Contact.getName();
			
			l_ContactLookup.put(l_ContactKey, l_Contact);
			}
		}
	
	//========================================================================
	//= 2. Check which of the specified contacts already exist. Already exisiting
	//= contacts will be ignored.
	//========================================================================
	
	l_NewContacts = new HashSet <Contact> ();
	
	l_ContactIterator = p_Contacts.iterator();
	while (l_ContactIterator.hasNext())
		{
		l_Contact    = l_ContactIterator.next();	
		l_ContactKey = l_Contact.getNameFirst() + "_" + l_Contact.getName();

		if ((l_ContactLookup == null) || !l_ContactLookup.containsKey(l_ContactKey))
			{
			l_NewContacts.add(l_Contact);		
			}
		}
	
	//========================================================================
	//= 3. Add new contacts to already exisiting ones.
	//========================================================================
	
	if (l_ExistingContacts != null) 
		 l_ExistingContacts.addAll(l_NewContacts);
	else l_ExistingContacts = l_NewContacts;
	
	p_Laboratory.setContactPersons (l_ExistingContacts);
	
	if (l_NewContacts.size() > 0) p_Laboratory = m_LaboratoryBean.saveLaboratory (p_Laboratory);	
		
	return p_Laboratory;
	}

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

private Laboratory getLaboratoryFromResult (XML2EntityMapper p_Result) throws Exception
	{
	Laboratory				l_Laboratory;
	Laboratory				l_ExistingLaboratory;	
	
	Set <LaboratoryAddress>	l_LaboratoryAddresses;
	String					l_UCMCode;
		
	l_Laboratory  = (Laboratory) p_Result.mapSingle (Laboratory.class.getName(), "/lu/tudor/santec/gecamed/labo/mapping/Laboratory.properties");	
		
	l_UCMCode = UCMCodeFormatter.extractNumeric (l_Laboratory.getUcmCode());
	l_Laboratory.setUcmCode(l_UCMCode);
	l_ExistingLaboratory = m_LaboratoryBean.getLaboratoryByUCMCode (l_UCMCode);
	
	if (l_ExistingLaboratory != null) l_Laboratory = l_ExistingLaboratory;
	else 
		{
		l_LaboratoryAddresses = (Set <LaboratoryAddress>) p_Result.mapRepeating (
				LaboratoryAddress.class.getName(), "/lu/tudor/santec/gecamed/labo/mapping/LaboratoryAddress.properties");	
		l_Laboratory.setAddresses (l_LaboratoryAddresses);	
//		l_Laboratory = m_LaboratoryBean.saveLaboratory (l_Laboratory);	
		}
	
	return l_Laboratory;
	}

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

private Set <Contact> getContactsFromResult (XML2EntityMapper p_Result, final Laboratory p_Laboratory) throws Exception
	{
	Set <Contact>	l_Contacts;
	Laboratory		l_Laboratory;
	
	l_Contacts  = (Set <Contact>) p_Result.mapRepeating (
			Contact.class.getName(), "/lu/tudor/santec/gecamed/labo/mapping/Contact.properties");	
	
	l_Laboratory = this.updateContactsForLaboratory (p_Laboratory, l_Contacts);
	if (l_Laboratory != null)
		l_Laboratory.update(p_Laboratory);
	else
		log(Level.WARN, "Couldn't update laboratory, because it is NULL");
	
	return l_Contacts;
	}

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

private Patient getPatientFromResult (XML2EntityMapper p_Result) throws Exception
	{
	Patient					l_Patient;
	Patient					l_ExistingPatient;
	Set <PatientAddress>	l_PatientAddresses;
	String					l_SocialSecurityNumber;
	
	l_Patient = (Patient) p_Result.mapSingle (Patient.class.getName(), "/lu/tudor/santec/gecamed/labo/mapping/Patient.properties");
	
	// if patient doesn't exist or SSN is empty
//	if (l_Patient == null || l_Patient.getSocialSecurityNumber().equals(Patient.EMPTY_SOCIAL_SECURITY))
//		throw new DecoderException("The tag for the patient's SSN is invalid");
	
	l_SocialSecurityNumber = l_Patient.getSocialSecurityNumber();
	l_SocialSecurityNumber = l_SocialSecurityNumber != null 
			? l_SocialSecurityNumber.replace(" ", "") : "";
	if (SSNChecker.PATTERN_EMPTY_SSN.matcher(l_SocialSecurityNumber).matches())
		 l_ExistingPatient = null;
	else l_ExistingPatient = this.getPatientBySocialSecurityNumber(l_SocialSecurityNumber);
	
	if (l_ExistingPatient != null) l_Patient = l_ExistingPatient;
	else 
		{
		l_PatientAddresses = (Set <PatientAddress>) p_Result.mapRepeating (PatientAddress.class.getName(), "/lu/tudor/santec/gecamed/labo/mapping/PatientAddress.properties");	
		l_Patient.setAddress(l_PatientAddresses);
//		l_Patient = this.savePatient (l_Patient);
		}
	
	return l_Patient;
	}

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

private Physician getPhysicianFromResult (XML2EntityMapper p_Result) throws Exception
	{
	Physician		l_Physician;
	Physician		l_ExistingPhysician;
	String			l_UCMCode;
	
//	l_Physician = (Physician) p_Result.mapSingle (Physician.class.getName(), "/lu/tudor/santec/gecamed/labo/mapping/Physician.properties");
	l_Physician = (Physician) p_Result.mapSingle (Physician.class.getName(), "/lu/tudor/santec/gecamed/labo/mapping/Prescriber.properties");
	
	if (	   (l_Physician.getUcmCode() == null
			 || l_Physician.getUcmCode().equals(Physician.UCM_DEFAULT)
			)&& l_Physician.getUcmCodeTemp() != null
			 && !l_Physician.getUcmCodeTemp().equals(Physician.UCM_DEFAULT))
		{
		/* the "CodePrescriterUCM" of the xml was NOT entered, but the "CodePhysicianUCM"
		 * => set the "CodePhysicianUCM" as ucm code
		 */
		l_Physician.setUcmCode(l_Physician.getUcmCodeTemp());
		}
		
	
	l_UCMCode = UCMCodeFormatter.extractNumeric (l_Physician.getUcmCode());
	l_UCMCode = UCMCodeFormatter.formatNumeric  (l_UCMCode);
	
	if (l_UCMCode == null || l_UCMCode.equals(Physician.UCM_DEFAULT))
		 l_ExistingPhysician = null;
	else l_ExistingPhysician = this.getPhysicianByUCMCode(l_UCMCode);
	if (l_ExistingPhysician != null) l_Physician = l_ExistingPhysician;
	
	return l_Physician;
	}

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

private Result parseResult (byte[] p_ResultData, String p_ResultFileName) throws Exception
	{
	ResultParser		l_Parser;
	XML2EntityMapper	l_Mapper;
	
	Laboratory			l_Laboratory;
	Patient				l_Patient;
	Physician			l_Physician;
	Result				l_Result;
	
	Set <Contact>		l_ContactPersons;
	
	p_ResultData = this.skipByteOrderMarker (p_ResultData);
	
	l_Parser = new ResultParser (p_ResultData);
	l_Parser.parse();

	l_Mapper = new XML2EntityMapper (l_Parser.getDocument(), false);
	
	l_Laboratory 	 = this.getLaboratoryFromResult (l_Mapper);
	l_ContactPersons = this.getContactsFromResult (l_Mapper, l_Laboratory);
	
	l_Patient    = this.getPatientFromResult    (l_Mapper);
	l_Physician  = this.getPhysicianFromResult  (l_Mapper);
	
	l_Result  = (Result)  l_Mapper.mapSingle (Result.class.getName(), "/lu/tudor/santec/gecamed/labo/mapping/Result.properties");
	
	if (l_Result != null)
		{
		l_Result.setLaboratory  	(l_Laboratory);	
		l_Result.setContactPersons	(l_ContactPersons);
		l_Result.setPrescriber  	(l_Physician);
		l_Result.setPatient	    	(l_Patient);
		l_Result.setResultNumber	(this.getResultNumber(p_ResultFileName));
		l_Result.setDownloaded		(new Date ());
		}
	
	return l_Result;
	}

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

private IncidentEntry createIncidentForResult (Result p_Result) throws Exception
	{
	Incident		l_LaboIncident;	
	IncidentEntry	l_LaboEntry;
	StringBuffer	l_IncidentLabel;
	IncidentEntryType l_IncidentEntryType;
	
	l_LaboIncident = new Incident  (p_Result.getPatient().getId(), p_Result.getPrescriber().getId());
	l_LaboIncident.setIncidentDate (p_Result.getExaminationDate());
	
	l_LaboIncident = this.saveIncident (l_LaboIncident);
	
	l_LaboEntry = new IncidentEntry();
	l_LaboEntry.setIncident(l_LaboIncident);
	
	l_IncidentEntryType = new IncidentEntryType();
	l_IncidentEntryType.setName(ResultBean.INCIDENT_ENTRY_TYPE);
	l_LaboEntry.setEntryType(l_IncidentEntryType);
	
	l_IncidentLabel = new StringBuffer ();
	l_IncidentLabel.append("<b>").append(p_Result.getLaboratory().getName()).append ("</b> ");
	l_IncidentLabel.append("(<i>").append(p_Result.getExaminationStatus()).append("</i>)");
	l_LaboEntry.setTextContent(l_IncidentLabel.toString());
	l_LaboEntry = this.saveIncidentEntry(l_LaboEntry);
	
	return l_LaboEntry;
	}

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

@RolesAllowed("gecam")
public void	setPasswordEncrypter (PasswordEncrypter p_Encrypter)
	{
	m_PasswordEncrypter = p_Encrypter;
	m_CryptoBean.setPasswordEncrypter(p_Encrypter);
	}

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

@RolesAllowed("gecam")
public void setPhysicianKey (PhysicianKey p_Key, String p_KeyPassword)
	{
	m_PhysicianKey = p_Key;
	m_KeyPassword  = p_KeyPassword;
	}

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

@RolesAllowed("gecam")
public Boolean openConnection (Connection p_Connection) throws LaboException
	{
	ResultDownloader	l_Downloader;
	
	l_Downloader = this.getDownloader();
	if (l_Downloader.isConnected()) l_Downloader.closeConnection();	
		
	return l_Downloader.openConnection(this.decryptConnectionPassword (p_Connection), m_LaboratoryBean);
	}

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

@RolesAllowed("gecam")
public Collection <String>	getAvailableResults	() throws Exception
	{
	ResultDownloader	l_Downloader;
		
	Collection <String>	l_AvailableResults = null;
		
	l_Downloader = this.getDownloader();	
	if (l_Downloader.isConnected())
		{
		l_AvailableResults = l_Downloader.getListOfAvailableResults();
		}
	
	return l_AvailableResults;
	}

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

@RolesAllowed("gecam")
public Result importResult	(byte[] p_EncryptedResult, String p_ResultFileName) throws Exception
	{
	ByteArrayInputStream 	l_ResultStream;
	PhysicianKey		 	l_Key;	
	LaboratoryCertificate	l_Certificate;
	byte[]				 	l_ResultData = null;
	Result					l_Result;
	Result					l_ExistingResult;
//	IncidentEntry			l_IncidentEntry;
	
	try {
		if ((p_EncryptedResult == null) || (p_ResultFileName == null)) 
			return null;
		
		l_ResultStream = new ByteArrayInputStream (p_EncryptedResult);
		if (!isValid (l_ResultStream))
			{
//			this.log (Level.WARN,"Specified data is not a valid result! Result will be rejected!");
//			throw new DecryptException("Invalid result. Maybe the result is not encrypted.", null, ImportInterface.RESULT_NOT_ENCRYPTED, p_EncryptedResult);
			
			this.log(Level.WARN, "The Result seems not to be encrypted. The stream will be passed undecrypted ...");
			l_ResultData	= new byte[l_ResultStream.available()];
			l_ResultStream.read(l_ResultData);
			}
		else
			{
			if (m_PhysicianKey == null)
				{
				l_Key = this.getKey(p_ResultFileName);
				if (l_Key == null)
					{
					this.log (Level.WARN,"Could not retrieve physician key for result \"" + p_ResultFileName + "\"! Result will be rejected!");
		//			return Boolean.FALSE;
					throw new DecryptException(ImportInterface.NO_KEY_SET);
					}
				}
			else l_Key = m_PhysicianKey;
			
			l_Certificate = this.getCertificate(p_ResultFileName);
			if (l_Certificate == null)
				{
				this.log (Level.WARN,"Could not retrieve laboratory certificate for result \"" + p_ResultFileName + "\"! Result will be rejected!");
		//		return Boolean.FALSE;
				throw new DecryptException(ImportInterface.NO_MATCHING_CERTIFICATE_FOUND);
				}
			
			try {
				m_CryptoBean.setupKeystore (l_Key, m_KeyPassword);
				}
			catch (Exception p_Exception)
				{
				log(Level.ERROR, p_Exception.getMessage(), p_Exception);
				throw new DecryptException(p_Exception.getMessage(), p_Exception, ImportInterface.UNABLE_TO_SETUP_KEYSTORE);
				}
			
			l_ResultData = m_CryptoBean.decrypt (p_EncryptedResult, l_Key, l_Certificate);
			}
		
		if (l_ResultData == null)
			{
	//		return Boolean.FALSE;
	//		return getEmptyResultWithSetState(ImportInterface.);
			}
		
		l_Result = this.parseResult (l_ResultData,p_ResultFileName);
		if (l_Result != null)
			{
			l_ExistingResult = m_ResultBean.getAlreadyExistingResult(l_Result.getLaboratory().getId(), 
																	 l_Result.getPrescriber().getId(),
																	 l_Result.getResultNumber());
			
			if (l_ExistingResult != null)
				{
				l_Result.setId (l_ExistingResult.getId());	
				l_Result.setIncidentEntryId (l_ExistingResult.getIncidentEntryId());
				l_Result.setState(ImportInterface.RESULT_ALREADY_EXISTS);
				}
	//		else
	//			{
	//			l_IncidentEntry = this.createIncidentForResult (l_Result);
	//			if ((l_IncidentEntry != null) && (l_IncidentEntry.isPersistent()))		
	//				l_Result.setIncidentEntryId(l_IncidentEntry.getId());
	//			}
			
	//		l_Result = this.saveResult(l_Result);
			return l_Result;
			}
	//	else return Boolean.FALSE;
		else 
			{
			log(Level.ERROR, "Unexpected Exception");
			throw new DecryptException("Result is null, but no error trown.", null, ImportInterface.UNEXPECTED_EXCEPTION, l_ResultData != null ? l_ResultData : p_EncryptedResult);
			}
		}
	catch (Exception p_Exception)
		{
		log(Level.WARN, p_Exception.getMessage(), p_Exception);
		throw new DecryptException("Error while importing result " + p_ResultFileName, p_Exception, ImportInterface.UNEXPECTED_EXCEPTION, (l_ResultData != null ? l_ResultData: p_EncryptedResult));
		}
	}

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

@RolesAllowed("gecam")
public void moveResultFile (String p_ResultFileName) throws DecryptException
	{
	ResultDownloader l_Downloader = this.getDownloader();
	
	l_Downloader.renameFile(p_ResultFileName, "old/" + p_ResultFileName);
	}

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

@RolesAllowed("gecam")
public Result importResult	(String p_ResultFilename) throws Exception
	{
	ResultDownloader	l_Downloader;

	l_Downloader = this.getDownloader();
	if (l_Downloader.isConnected())
		{
		return this.importResult (l_Downloader.downloadResult(p_ResultFilename), p_ResultFilename);
		}
	else return null;
	}

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

@RolesAllowed("gecam")
public void closeConnection ()
	{
	ResultDownloader	l_Downloader;

	l_Downloader = this.getDownloader();
	if (l_Downloader.isConnected())
		{
		l_Downloader.closeConnection();
		}	
	
	m_PhysicianKey = null;
	m_KeyPassword  = null;
	}

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

@RolesAllowed("gecam")
public Boolean synchronizeCertificates (Connection p_Connection) throws Exception
	{
	m_Synchronizer = new CertificateSynchronizer ();
	return m_Synchronizer.synchronizeCertificates (this.decryptConnectionPassword (p_Connection), m_LaboratoryBean);
	}

//---------------------------------------------------------------------------
/**
 * Releases this stateful session bean.
 */
//---------------------------------------------------------------------------

@Remove
@RolesAllowed("gecam")
public void remove ()
	{	
	this.closeConnection();
	}

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

	}
