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

import java.io.Serializable;
import java.math.BigInteger;
import java.util.Collection;
import java.util.Date;
import java.util.HashMap;
import java.util.List;

import javax.annotation.Resource;
import javax.annotation.security.RolesAllowed;
import javax.ejb.EJB;
import javax.ejb.EJBException;
import javax.ejb.Remote;
import javax.ejb.SessionContext;
import javax.ejb.Stateless;
import javax.ejb.TransactionAttribute;
import javax.ejb.TransactionAttributeType;
import javax.jms.JMSException;
import javax.jms.ObjectMessage;
import javax.jms.Topic;
import javax.jms.TopicConnection;
import javax.jms.TopicConnectionFactory;
import javax.jms.TopicPublisher;
import javax.jms.TopicSession;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import javax.persistence.Query;

import lu.tudor.santec.gecamed.billing.utils.InvoiceWorkflow;
import lu.tudor.santec.gecamed.core.utils.FileUtils;
import lu.tudor.santec.gecamed.core.utils.ManagerFactory;
import lu.tudor.santec.gecamed.core.utils.SSNChecker;
import lu.tudor.santec.gecamed.core.utils.ServerConfig;
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.PatientIncidentStatisticStub;
import lu.tudor.santec.gecamed.patient.ejb.entity.beans.PatientPhone;
import lu.tudor.santec.gecamed.patient.ejb.entity.beans.PatientStub;
import lu.tudor.santec.gecamed.patient.ejb.session.interfaces.PatientAdminInterface;
import lu.tudor.santec.gecamed.patient.utils.PatientNameFormat;
import lu.tudor.santec.gecamed.patient.utils.PatientNameFormatter;
import lu.tudor.santec.gecamed.patient.utils.PatientSearchParameter;
import lu.tudor.santec.gecamed.patient.utils.PatientUpdateObject;
import lu.tudor.santec.gecamed.usermanagement.ejb.session.interfaces.LoginInterface;

import org.apache.log4j.Level;
import org.apache.log4j.Logger;
import org.jboss.annotation.security.SecurityDomain;


/**
 * Sesstion Bean that manages the access to the Patient EJB. 
 * it provides methods to add and delete or modify Patients.
 *
 * 1   patient.id,
 * 2   patient.socialSecurityNumber,
 * 3   patient.surName,
 * 4   patient.firstName,
 * 5   patient.maidenName,
 * 6   patient.gender,
 * 7   patient.insuranceID
 * 8   address.zip,
 * 9   address.locality,
 *10  address.streetName,
 *11  address.streetNumber,
 *12  address.country
 *13  patient.status
 *
 * @author Johannes Hermen johannes.hermen(at)tudor.lu
 *
 * @version
 * <br>$Log: PatientAdminBean.java,v $
 * <br>Revision 1.90  2013-12-27 18:09:23  donak
 * <br>Cleanup of imports
 * <br>
 * <br>Revision 1.89  2013-07-15 06:18:35  ferring
 * <br>logging changed
 * <br>
 * <br>Revision 1.88  2013-04-18 14:58:08  troth
 * <br>Add patient to the export query which have a act in the table billing in the database.
 * <br>
 * <br>Revision 1.87  2013-04-11 13:21:05  ferring
 * <br>query time output removed
 * <br>
 * <br>Revision 1.86  2013-04-09 07:37:30  ferring
 * <br>Patient search unaccented
 * <br>
 * <br>Revision 1.85  2013-03-26 10:25:49  ferring
 * <br>Option added in admin settings, to format the patient names
 * <br>
 * <br>Revision 1.84  2013-03-21 15:35:44  troth
 * <br>1. Add a dialog inform the user if patienten search limit is reach.
 * <br>2. Make some GUI changes.
 * <br>
 * <br>Revision 1.83  2013-02-14 16:28:48  troth
 * <br>Change the export search data for patient to patientStub
 * <br>
 * <br>Revision 1.82  2013-02-13 15:18:23  troth
 * <br>Code clearup.
 * <br>
 * <br>Revision 1.81  2013-02-12 17:22:13  troth
 * <br>add new function for the MM Export tab to search for patient.
 * <br>
 * <br>Revision 1.80  2012-11-28 16:30:12  troth
 * <br>New PatientAdminBean function to get patient by over his RIS-Id.
 * <br>
 * <br>Revision 1.79  2012-07-19 06:27:45  ferring
 * <br>right parenthesis in query to much
 * <br>
 * <br>Revision 1.78  2012-05-08 09:01:20  ferring
 * <br>will search for ssn, even if search string is not a number
 * <br>
 * <br>Revision 1.77  2012-03-28 12:48:19  ferring
 * <br>Prescription printing now always takes the correct patient, independent of the SSN.
 * <br>
 * <br>Revision 1.76  2010-07-08 08:25:37  hermen
 * <br>*** empty log message ***
 * <br>
 * <br>Revision 1.75  2010-06-24 11:55:09  hermen
 * <br>version 1.1
 * <br>
 * <br>Revision 1.74  2009-03-10 13:39:38  hermen
 * <br>added view and stub bean for patient incident statistics
 * <br>
 * <br>Revision 1.73  2008-10-24 08:37:15  hermen
 * <br>Fixed Bug #230
 * <br>Sort Patients in the Patient List by Lastname, Firstname
 * <br>Lastname works, bus Firstname as 2nd sorting is not used....
 * <br>
 * <br>Revision 1.72  2008-09-25 09:43:06  heinemann
 * <br>fixed copyrights
 * <br>
 * <br>Revision 1.71  2008-09-15 09:18:35  heinemann
 * <br>included deletion of patient files in patient delete process.
 * <br>
 * <br>Revision 1.70  2008-08-29 12:57:14  heinemann
 * <br>*** empty log message ***
 * <br>
 * <br>Revision 1.69  2008-08-19 10:25:08  heinemann
 * <br>cleanup
 * <br>
 * <br>Revision 1.68  2008-07-17 08:07:47  mack
 * <br>Removed stacktrace when failing to close JMS the second time.
 * <br>
 * <br>Revision 1.67  2008-05-28 11:48:23  mack
 * <br>Removed obsolete imports
 * <br>
 * <br>Revision 1.66  2008-05-27 13:50:54  mack
 * <br>Added stacktrace
 * <br>
 * <br>Revision 1.65  2008-05-26 14:23:33  heinemann
 * <br>*** empty log message ***
 * <br>
 * <br>Revision 1.64  2008-05-05 08:18:26  hermen
 * <br>removed debugging
 * <br>
 * <br>Revision 1.63  2008-04-28 08:05:48  hermen
 * <br>changed patient search to use patient_stub view and bean
 * <br>
 * <br>Revision 1.62  2008-04-03 15:03:01  hermen
 * <br>fixed matricule query
 * <br>
 * <br>Revision 1.61  2008-02-25 15:47:36  heinemann
 * <br>added batch fetching of patient names
 * <br>
 * <br>Revision 1.60  2008-02-11 16:48:45  heinemann
 * <br>added getter for SCN
 * <br>
 * <br>Revision 1.59  2008-01-17 09:52:33  hermen
 * <br>updated Javadoc and refactured code
 * <br>
 *
 */

@Stateless
@Remote (PatientAdminInterface.class)
@SecurityDomain ("gecamedLoginDS")
public class PatientAdminBean implements PatientAdminInterface
										 
{
    //~ Static fields/initializers =============================================

    
//    private static final long serialVersionUID = 1L;
    /**
     * the name of the Topic to descripe to. this MessageTopic has to exist in the
     * JBoss configuration
     */
    private static final String TOPIC_NAME = "topic/GeCam/patientmanagementTopic";
    
    //~ Instance fields ========================================================

    public static final int  patient_id = 0;
    public static final int  patient_socialSecurityNumber = 1;
    public static final int  patient_surName = 2;
    public static final int  patient_firstName = 3;
    public static final int  patient_maidenName = 4;
    public static final int  patient_gender = 5;
    public static final int  patient_insuranceID = 6;
    public static final int  address_zip = 7;
    public static final int  address_locality = 8;
    public static final int  address_streetName = 9;
    public static final int  address_streetNumber = 10;
    public static final int  address_country = 11;
    public static final int  patient_status = 12;
    public static final int  address_merged = 7;
    
    
    /** the logger Object for this class */
	private static Logger logger = Logger.getLogger(PatientAdminBean.class.getName());

    private boolean disableJMS = false;

    private TopicConnection conn = null;
    private TopicSession session = null;

//    private static final Category m_Logger = Category.getInstance(PatientAdminBean.class.getName());

    @PersistenceContext(unitName="gecam")
    EntityManager em;

    @Resource
    SessionContext sessionContext;

    @EJB
    LoginInterface login;

    @Resource (mappedName=TOPIC_NAME)
    private Topic topic;

    @Resource (mappedName="ConnectionFactory")
    private TopicConnectionFactory factory;
    
//    private static int instanceNr;

    //~ Methods ================================================================


    @RolesAllowed("gecam")
//    @TransactionAttribute(TransactionAttributeType.REQUIRES_NEW)
    public Patient savePatient(Patient patient)
        throws Exception {
    	/* ================================================== */
    	Patient p = savePatientOperation(patient);

        // delete all deleted addresses and phones of the patient to
        // prevent the database running full of unrelated objects
    	deleteOrphanAddresses(p);
		deleteOrphanPhones(p);
		deleteOrphanPatientContact(p);

		return p;

		/* ================================================== */
    }
    /* (non-Javadoc)
	 * @see lu.tudor.santec.gecamed.patient.ejb.session.beans.PatientAdminInterface#setPatient(lu.tudor.santec.gecamed.patient.ejb.entity.beans.Patient)
	 */
    @RolesAllowed("gecam")
    @TransactionAttribute(TransactionAttributeType.REQUIRES_NEW)
    private Patient savePatientOperation(Patient patient)
        throws Exception
    {
        /* ================================================================================== */
        // Throw Exception if right not granted
    	if (! this.login.userHasPermission("PatientModule.createPatients")) return patient;
        /* ---------------------------------------------------------------------------------- */

    	// save the new patient
        patient = em.merge(patient);
        em.flush();
        
//    	patient = (Patient) em.createQuery("SELECT OBJECT(o) FROM Patient o WHERE o.id = :id")
//    					.setParameter("id", patient.getId())
//    					.getSingleResult();
//    	/* ------------------------------------------------------- */
//    	// fast fix. The patientId of the PatientAddress object is null. Don't know why.
    	if (patient.getAddress() != null)
    		for (PatientAddress pa : patient.getAddress())
    			pa.setPatientId(patient.getId());
    	
    	
    	/* ---------------------------------------------------------------------------------- */
        // send message with patientID
		try {
			this.sendPatientMessage(patient);
		} catch (Exception e) {
			e.printStackTrace();
		}
        /* ---------------------------------------------------------------------------------- */
        return patient;
        /* ================================================================================== */
    }

    @SuppressWarnings("unchecked")
	@TransactionAttribute(TransactionAttributeType.REQUIRES_NEW)
    private void deleteOrphanAddresses(Patient patient) {
    	/* ====================================================== */
    	Query q = em.createQuery("SELECT Object(o) FROM PatientAddress o " +
    			"WHERE o.patientId = null");
    	try {
	    	List<PatientAddress> result = q.getResultList();
	    	if (result != null)
	    		for (PatientAddress p : result) {
	    			/* ------------------------------------------------------- */
	    			if (patient.getAddress() != null)
	    				if (patient.getAddress().contains(p))
	    					continue;
	    			em.remove(p);
	    			/* ------------------------------------------------------- */
	    		}
    	} catch (Exception e) {
    		logger.log(Level.ERROR, e.getMessage(), e);
		}
		/* ====================================================== */
    }


    @SuppressWarnings("unchecked")
	@TransactionAttribute(TransactionAttributeType.REQUIRES_NEW)
    private void deleteOrphanPatientContact(Patient patient) {
    	/* ====================================================== */
    	Query q = em.createQuery("SELECT Object(o) FROM PatientContact o " +
    			"WHERE o.patientId = null");
    	try {
	    	List<PatientContact> result = q.getResultList();
	    	if (result != null)
	    		for (PatientContact p : result) {
	    			/* ------------------------------------------------------- */
	    			if (patient.getContacts() != null)
	    				if (patient.getContacts().contains(p))
	    					continue;
	    			em.remove(p);
	    			/* ------------------------------------------------------- */
	    		}
    	} catch (Exception e) {
    		logger.log(Level.WARN, e.getMessage());
		}
		/* ====================================================== */
    }


    @SuppressWarnings("unchecked")
	@TransactionAttribute(TransactionAttributeType.REQUIRES_NEW)
    private void deleteOrphanPhones(Patient patient) {
    	/* ====================================================== */
    	Query q = em.createQuery("SELECT Object(o) FROM PatientPhone o " +
    			"WHERE o.patientId = null");
    	try {
	    	List<PatientPhone> result = q.getResultList();
	    	if (result != null)
	    		for (PatientPhone p : result){
	    			/* ------------------------------------------------------- */
	    			if (patient.getPhones() != null)
	    				if (patient.getPhones().contains(p))
	    					continue;
	    			em.remove(p);
	    			/* ------------------------------------------------------- */
	    		}
    	} catch (Exception e) {
    		logger.log(Level.WARN, e.getMessage());
		}
		/* ====================================================== */
    }


    /* (non-Javadoc)
	 * @see lu.tudor.santec.gecamed.patient.ejb.session.beans.PatientAdminInterface#getPatient(java.lang.Integer)
	 */
    public Patient getPatient(Integer patientID) throws Exception
    {
        /* ================================================================================== */
        // Throw Exception if right not granted
        /* ---------------------------------------------------------------------------------- */
//        if ((patientID == null) || (! this.login.userHasPermission("PatientModule.viewPatients"))) {
//            return null;
//        }
        /* ---------------------------------------------------------------------------------- */
    	Query q = em.createQuery("SELECT OBJECT(o) FROM Patient o WHERE o.id = :id");
    	q.setParameter("id", patientID);
    	
        Patient pv = (Patient) q.getSingleResult();
        /* ---------------------------------------------------------------------------------- */
        return pv;
        /* ================================================================================== */
    }

    /* (non-Javadoc)
	 * @see lu.tudor.santec.gecamed.patient.ejb.session.beans.PatientAdminInterface#deletePatientFile(lu.tudor.santec.gecamed.patient.ejb.entity.beans.FileInfo)
	 */
	@RolesAllowed("gecam")
	@Deprecated
	public Patient getPatientBySocialSecurityNumber (String socialSecurityNumber) throws Exception
	{
	Patient l_Patient;

	l_Patient = (Patient) em.createNamedQuery("findPatientBySocialSecurityNumber")
	 						.setParameter("socialSecurityNumber", socialSecurityNumber)
	 						.setMaxResults(1)
	 						.getSingleResult();

	return l_Patient;
	}
	
	
	public Patient getPatientOfIncident(Integer incidentId) throws Exception
	{
		return (Patient) em.createNamedQuery(Patient.GET_PATIENT_OF_INCIDENT)
				.setParameter("incidentId", incidentId)
				.getSingleResult();
	}
	

    /* (non-Javadoc)
	 * @see lu.tudor.santec.gecamed.patient.ejb.session.beans.PatientAdminInterface#sendMessage(java.io.Serializable)
	 */
    @RolesAllowed("gecam")
    public void sendPatientMessage(Patient object)
    {
        /* ======================================================================================= */
        TopicPublisher send;
        /* --------------------------------------------------------------------------------------- */
        try {
            conn = factory.createTopicConnection();
//            conn.setExceptionListener(this);
            conn.start();
            disableJMS = false;
        } catch (Exception e) {
            disableJMS = true;
        	throw new EJBException(e);
    	}
        
        if (disableJMS) return;
        
        try {
            /* --------------------------------------------------------------------------------------- */
         	session = conn.createTopicSession(false, TopicSession.AUTO_ACKNOWLEDGE);
            send = session.createPublisher(topic);
            ObjectMessage om = session.createObjectMessage(object);
            send.publish(om);
            send.close();
            session.close();
            conn.stop();
            conn.close();
            /* --------------------------------------------------------------------------------------- */
        } catch (JMSException e) {
            /* --------------------------------------------------------------------------------------- */
    		logger.log(Level.ERROR, e.getMessage(), e);
            /* --------------------------------------------------------------------------------------- */
        }
        /* ======================================================================================= */
    }

    public void sendMessage(PatientUpdateObject object)
    {
        /* ======================================================================================= */
        TopicPublisher send;
        /* --------------------------------------------------------------------------------------- */
        try {
            conn = factory.createTopicConnection();
//            conn.setExceptionListener(this);
            conn.start();
            disableJMS = false;
        } catch (Exception e) {
            disableJMS = true;
        	throw new EJBException(e);
    	}
        
        if (disableJMS) return;
        
        try {
            /* --------------------------------------------------------------------------------------- */
         	session = conn.createTopicSession(false, TopicSession.AUTO_ACKNOWLEDGE);
            send = session.createPublisher(topic);
            ObjectMessage om = session.createObjectMessage(object);
            send.publish(om);
            send.close();
            session.close();
            conn.stop();
            conn.close();
            /* --------------------------------------------------------------------------------------- */
        } catch (JMSException e) {
            /* --------------------------------------------------------------------------------------- */
    		logger.log(Level.ERROR, e.getMessage(), e);
            /* --------------------------------------------------------------------------------------- */
        }
        /* ======================================================================================= */
    }
    /* (non-Javadoc)
     * @see lu.tudor.santec.gecamed.patient.ejb.session.interfaces.PatientAdminInterface#getPatientName(java.lang.Integer)
     */
    public String getPatientName(Integer patientId) {
    	/* =========================================================== */
    	if (patientId != null) {
//	    	Query q = em.createQuery("SELECT p.surName,p.firstName FROM Patient AS p" +
//	    								"WHERE p.id = :patientId");
//	    	q.setParameter("patientId", patientId);
//	    	Object[] obj = (Object[]) q.getSingleResult();
//	    	String s = ""+obj[0]+", "+obj[1];
//	    	return s;
//    		Patient p = em.find(Patient.class, patientId);
    		try {
				/* ------------------------------------------------------- */
    			Patient p = (Patient) em.createQuery("SELECT OBJECT(o) FROM Patient o " +
														"WHERE o.id = :id")
										.setParameter("id", patientId)
										.getSingleResult();
    			/* ------------------------------------------------------- */
				String t = "";
				t = ""+ p.getSurName()+", "+p.getFirstName();
				return t;
    			/* ------------------------------------------------------- */
			} catch (Exception e) {
				return null;
			}
    		
    	} else
    		return null;
    	/* =========================================================== */
    }

    @SuppressWarnings("unchecked")
	@RolesAllowed("gecam")
    public HashMap<Integer, String> getPatientNames(List<Integer> patientIds) {
    	/* =========================================================== */
    	if (patientIds != null && patientIds.size() > 0) {
    		
    		try {
				/* ------------------------------------------------------- */
    			
    			Query q = em.createQuery("SELECT OBJECT(o) FROM Patient o " +
											"WHERE o.id in (:dada)");
				q.setParameter("dada", patientIds);
				List<Patient> result = q.getResultList();
    			/* ------------------------------------------------------- */
    			HashMap<Integer, String> resultMap = new HashMap<Integer, String>();
    			for (Patient p : result) {
    				/* ------------------------------------------------------- */
    				resultMap.put(p.getId(), ""+ p.getSurName()+", "+p.getFirstName());
    				/* ------------------------------------------------------- */
    			}
				
				return resultMap;
    			/* ------------------------------------------------------- */
			} catch (Exception e) {
	    		logger.log(Level.ERROR, e.getMessage(), e);
				return null;
			}
    		
    	} else
    		return new HashMap<Integer, String>(0);
    	/* =========================================================== */
    }

	/* (non-Javadoc)
	 * @see lu.tudor.santec.gecamed.patient.ejb.session.interfaces.PatientAdminInterface#checkDuplicateSSN(java.lang.String)
	 */
	@SuppressWarnings("unchecked")
	public List<Patient> checkDuplicateSSN (String ssn)
	{
		Query q;
		
		
		if (ssn == null)
			return null;

		try
		{
			ssn = ssn.replaceAll("\\s", "");
			ssn = ssn.replaceAll("0+$", "");
			
			if (ssn.matches("\\d{11}0{0,2}"))
				ssn = ssn.substring(0,11);
			
			if (ssn.matches("\\d{11}|\\d{13}"))
			{
				String ssn11;
				String ssn13;
				
				if (ssn.length() == 11)
				{
					ssn11 = ssn;
					ssn13 = SSNChecker.get13DigitsSSN(ssn, false);
				}
				else
				{
					ssn11 = ssn.substring(0, 11);
					ssn13 = ssn;
				}
				
				q = em.createQuery(
						"SELECT Object(o) FROM Patient o " +
						"WHERE o.socialSecurityNumber = :ssn11 " + 
						"OR o.socialSecurityNumber = :ssn13 " + 
						"OR o.socialSecurityNumber = :ssn00" + 
						" ORDER BY o.surName, o.firstName");
//				List<String> ssns = new ArrayList<String>(2);
//				ssns.add(ssn11);
//				ssns.add(ssn13);
//				q.setParameter("ssn", ssns);
//				q.setParameter("ssn", new String[] { ssn11, ssn13 });
				q.setParameter("ssn11", ssn11);
				q.setParameter("ssn13", ssn13);
				q.setParameter("ssn00", ssn11 + "00");
				q.setMaxResults(10);
			}
			else
			{
				// count the results
				q = em.createQuery(
						"SELECT Object(o) FROM Patient o " +
						"WHERE o.socialSecurityNumber like :ssn" + 
						" ORDER BY o.surName, o.firstName");
				q.setParameter("ssn", ssn+"%");
				q.setMaxResults(10);
			}
			
			List<Patient> result = q.getResultList();
			
			if (result == null || result.isEmpty())
				return null;
			else
				return result;
		}
		catch (Exception e)
		{
			logger.error("Error when trying to check for duplicate SSNs", e);
			return null;
		}
	}

	/* (non-Javadoc)
	 * @see lu.tudor.santec.gecamed.patient.ejb.session.interfaces.PatientAdminInterface#deletePatient(java.lang.Integer)
	 */
	public Boolean deletePatient(Integer patientId) throws Exception{
		/* ====================================================== */
		if (patientId == null)
			return false;
		/* ------------------------------------------------------- */
		// get the entity for the patient id
		Query q = em.createQuery("SELECT Object(o) FROM Patient o " +
									"WHERE o.id = "+patientId);
			/* ------------------------------------------------------- */
			Patient dPat = (Patient) q.getSingleResult();
			/* ------------------------------------------------------- */
			// if a spouse is set, we must delete the reference in the spouse
			/* ------------------------------------------------------- */
			if (dPat.getSpouseId() != null) {
				/* ------------------------------------------------------- */
				Patient spouse = getPatient(dPat.getSpouseId());
				spouse.setSpouseName(dPat.toShortString());
				spouse.setSpouseId(null);
				
				em.merge(spouse);
				/* ------------------------------------------------------- */
			}
			/* ------------------------------------------------------- */
			// remove the patient
			em.remove(dPat);
			/* ------------------------------------------------------- */
			// remove the files of the patient
			/* ------------------------------------------------------- */
			FileUtils.deleteGECAMedPatientFolder(ServerConfig.getProperty(ServerConfig.PATIENT_FILES_DIR), 
													dPat.getId());
			/* ------------------------------------------------------- */
			// remove the dicom files of the patient
			/* ------------------------------------------------------- */
			FileUtils.deleteGECAMedPatientFolder(ServerConfig.getProperty(ServerConfig.DICOM_DIR), 
													dPat.getId());
			/* ------------------------------------------------------- */
			// return true
			return true;
			/* ------------------------------------------------------- */
		/* ====================================================== */
	}
		
	/* (non-Javadoc)
	 * @see lu.tudor.santec.gecamed.patient.ejb.session.interfaces.PatientAdminInterface#getOpenInvoices(java.lang.Integer)
	 */
	public Long getOpenInvoices(Integer patientId) {
		/* ================================================== */
		Query q = em.createQuery("SELECT COUNT(o) FROM Invoice o " +
									"WHERE o.patient.id = :patientId " +
									"AND o.state >= :state1 " +
									"AND o.state < :state2");
		q.setParameter("patientId", patientId);
		q.setParameter("state1", InvoiceWorkflow.c_PrintedState);
		q.setParameter("state2", InvoiceWorkflow.c_PaidState);
		
		return (Long) q.getSingleResult();
		/* ================================================== */
	}
	
	
	/* (non-Javadoc)
	 * @see lu.tudor.santec.gecamed.patient.ejb.session.interfaces.PatientAdminInterface#getSocialSecurityNumber(java.lang.Integer)
	 */
	public String getSocialSecurityNumber(Integer patientId) {
		/* ====================================================== */
		if (patientId != null) {
    		try {
				/* ------------------------------------------------------- */
    			Patient p = (Patient) em.createQuery("SELECT OBJECT(o) FROM Patient o " +
														"WHERE o.id = :id")
										.setParameter("id", patientId)
										.getSingleResult();
    			/* ------------------------------------------------------- */
				String t = "";
				t = ""+ p.getSocialSecurityNumber();
				return t;
    			/* ------------------------------------------------------- */
			} catch (Exception e) {
				return null;
			}
    		
    	} else
    		return null;
		/* ====================================================== */
	}
	
	
	@SuppressWarnings("unchecked")
	public Collection<PatientStub> getPatientStubListBySSec(String ssecString, Integer limit) throws Exception {
	    return em.createNamedQuery(PatientStub.findPatientStubBySSN)
	    	.setParameter("socialSecurityNumber", ssecString + "%")
	    	.setMaxResults(limit)
	    	.getResultList();
	}
	
	
	/* (non-Javadoc)
	 * @see lu.tudor.santec.gecamed.patient.ejb.session.interfaces.PatientAdminInterface#getPatientStubListBySearchString(java.lang.String, java.lang.String, java.lang.String, java.lang.Integer)
	 */
	@SuppressWarnings("unchecked")
	public Collection<PatientStub> getPatientStubListBySearchString(String searchNameString, String searchAddressString, String filter, Integer limit) throws Exception
	{
		// replace evil chars
		searchNameString = searchNameString.replaceAll("\'", "\'\'");
		searchAddressString = searchAddressString.replaceAll("\'", "\'\'");
		
		// create the query
		StringBuffer queryString = new StringBuffer("\n SELECT OBJECT(patient) FROM PatientStub patient ");
		
		// split the name search into single words
		String[] searchNameStrings = searchNameString.split(" ");
		
		// create the where clause for the name search
		for (int i = 0; i < searchNameStrings.length; i++)
		{
			// get the singe search word
			String searchWord = searchNameStrings[i];
			// break if it is empty
			if (searchWord == null || "".equals(searchWord))
			{
				if (filter == null || filter.length() == 0)
					break;
			}
			
			// append the right word....
			if (i == 0)
			{
				queryString.append("\nWHERE ");
			}
			else
			{
				queryString.append("AND ");
			}
			
			// if searchword is a number, search the ssec
			try
			{
				long nr = Long.parseLong(searchWord);
				queryString.append("( patient.socialSecurityNumber LIKE '" + searchWord + "%' ) OR \n");
				queryString.append("( patient.id = " + nr + " ) \n");
				// else search the name fields
			}
			catch (NumberFormatException e)
			{
				searchWord = PatientNameFormatter.unaccent(searchWord).toUpperCase();
				
				// parameter for old search query
//				querryString.append(" ( \n");
//				// search from column start
//				querryString.append("\tupper(patient.socialSecurityNumber) LIKE '" + searchWord + "%' OR \n");
//				querryString.append("\tupper(patient.maidenName) LIKE '"	+ searchWord + "%' OR \n");
//				querryString.append("\tupper(patient.surName)    LIKE '"	+ searchWord + "%' OR \n");
//				querryString.append("\tupper(patient.firstName)  LIKE '"	+ searchWord + "%' OR \n");
//				// search from word start
//				querryString.append("\tupper(patient.maidenName) LIKE '% "	+ searchWord + "%' OR \n");
//				querryString.append("\tupper(patient.surName)    LIKE '% "	+ searchWord + "%' OR \n");
//				querryString.append("\tupper(patient.firstName)  LIKE '% "	+ searchWord + "%' \n");
//				querryString.append(" ) \n");
				
				queryString.append("patient.searchHelper LIKE '% ")
						.append(searchWord)
						.append("%'\n");
			}
		}
		
		// split the address search into single words
		String[] searchAddressStrings = searchAddressString.split(" ");
		
		// create the where clause for the name search
		for (int i = 0; i < searchAddressStrings.length; i++)
		{
			// get the singe search word
			String searchWord = searchAddressStrings[i];
			// break if it is empty
			if (searchWord == null || "".equals(searchWord))
			{
				if (filter == null || filter.length() == 0)
					break;
			}
			
			// append the right word....
			if (queryString.toString().indexOf("WHERE") <= 0)
			{
				queryString.append(" WHERE \n");
			}
			else
			{
				queryString.append(" AND \n");
			}
			
			// if searchword is a number, search the zip and phone
			try
			{
				Long.parseLong(searchWord);
				queryString.append(" ( \n");
				// for zip search (start of column)
				queryString.append("\t patient.address LIKE '" + searchWord + "%'  OR \n");
				// for phone search (start of new word)
				queryString.append("\t patient.phone LIKE '% " + searchWord + "%'   \n");
				
				
				queryString.append(" ) \n");
				// else search the address fields
			}
			catch (Exception e)
			{
				// search from word start
				queryString.append("\tupper(patient.address) LIKE upper('% " + searchWord + "%') \n");
			}
		}
		
		if (filter != null && filter.length() != 0)
		{
			if (searchNameStrings.length == 0)
			{
				queryString.append(" WHERE ");
			}
			else
			{
				queryString.append(" AND ");
			}
			queryString.append(filter);
		}
		queryString.append(" ORDER BY patient.surName, patient.firstName");
//		System.out.println("--------------------------------------------------------------");
//		System.out.println(queryString.toString());
//		System.out.println("++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++");
		Query q = em.createQuery(queryString.toString());
		try
		{
			// check the limit and set the default limit, if it doesn't fit
			if (limit != null && limit.intValue() >= 0)
			{
				q.setMaxResults(limit);
			}
			else
			{
				logger.warn("Limit for patient search was invalid (limit = " + limit + "). "
						+ "Therefore changed to default limit (default limit = " 
						+ PatientSearchParameter.DEFAULT_SEARCH_LIMIT + ").");
				q.setMaxResults(PatientSearchParameter.DEFAULT_SEARCH_LIMIT);
			}
		}
		catch (Exception e)
		{
    		logger.log(Level.ERROR, e.getMessage(), e);
		}
//		long startTime	= System.currentTimeMillis();
		List<PatientStub> l = q.getResultList();
//		long endTime	= System.currentTimeMillis();
//		System.out.print("  Query took: "+ (endTime-startTime) + "ms for " + l.size() + " results");
		return l;
	}
	
	
	@SuppressWarnings("unchecked")
	public Collection<PatientIncidentStatisticStub> getPatientIncidentStatistics(
		Integer siteID, Date dateFrom, Date dateTo) throws Exception {
	    try {
        	    if (siteID != null) {
        		return em.createNamedQuery(PatientIncidentStatisticStub.FIND_ALL_PATIENTINCIDENTSTATISTICS_BY_SITE_AND_DATE)
        	    		.setParameter("siteID", siteID)
        	    		.setParameter("dateFrom", dateFrom)
        	    		.setParameter("dateTo", dateTo)
        	    		.getResultList();
        	    } else {
        		return em.createNamedQuery(PatientIncidentStatisticStub.FIND_ALL_PATIENTINCIDENTSTATISTICS)
                		.setParameter("dateFrom", dateFrom)
                		.setParameter("dateTo", dateTo)
                		.getResultList();
        	    }
	    } catch (Exception e) {
    		logger.log(Level.ERROR, e.getMessage(), e);
    		return null;
	    }
	}
	

//	@PostConstruct
//	public void postConstruct () {
//	    instanceNr++;
//	    m_Logger.info("PatientAdminBean postConstruct instances: " + instanceNr);
//	}
//	
//	
//	@PreDestroy
//	public void preDestroy () {
//	    instanceNr--;
//	    m_Logger.info("PatientAdminBean preDestroy instances: " + instanceNr);
//	}
	
	@SuppressWarnings("unchecked")
	public Collection<Patient> getPatientByRisID(String risID, Integer limit) throws Exception {
	    return em.createNamedQuery(Patient.FIND_PATIENT_BY_RISID)
	    	.setParameter("risID", risID)
	    	.setMaxResults(limit)
	    	.getResultList();
	}
	
	
	public BigInteger countPatientToExport(String queryString) throws Exception
	{
		return (BigInteger) em.createNativeQuery(queryString).getSingleResult();
	}
	
	
	@SuppressWarnings("unchecked")
	public Collection<Integer> findPatientIdsToExport(String queryString, Integer limit) throws Exception
	{
		return em.createNativeQuery(queryString).getResultList();
	}
	
	
	@SuppressWarnings("unchecked")
	public Collection<PatientStub> findPatientToExport(String queryString, Integer limit) throws Exception
	{
		Query q = em.createQuery(queryString.toString());
		q.setMaxResults(limit);		
		return q.getResultList();
	}
	
	
	public void formatPatientNames (List<PatientNameFormat> options)
	{
		StringBuilder	query	= new StringBuilder("UPDATE patient.patient SET ");
		long			time;
		
		if (options == null || options.size() == 0)
			return;
		
		// build the query
		for (int i = 0; i < options.size(); i++)
		{
			if (i > 0)
				query.append(", ");
			
			switch (options.get(i))
			{
				case FIRST_NAME_CAPITALIZE:
					query.append("first_name = INITCAP(LOWER(first_name))");
					break;
				
				case FIRST_NAME_TO_UPPER_CASE:
					query.append("first_name = UPPER(first_name)");
					break;
					
				case LAST_NAME_CAPITALIZE:
					query.append("sur_name = INITCAP(LOWER(sur_name)), maiden_name = INITCAP(LOWER(maiden_name))");
					break;
					
				case LAST_NAME_TO_UPPER_CASE:
					query.append("sur_name = UPPER(sur_name), maiden_name = UPPER(maiden_name)");
					break;
			}
		}

		logger.info("Formatting patient names ...");
		time	= System.currentTimeMillis();
		em.createNativeQuery(query.toString()).executeUpdate();
		logger.info("... done formatting patient names ("+((System.currentTimeMillis()-time)/1000)+" sec).");
	}
	
	
	public static PatientAdminInterface getInstance ()
	{
		return (PatientAdminInterface) ManagerFactory.getRemote(PatientAdminBean.class);
	}
	
	
	public void log (Integer level, String message)
	{
		logger.log(Level.toLevel(level), message);
	}
}
