package lu.tudor.santec.gecamed.formeditor.ejb.session.beans;

import java.sql.Timestamp;
import java.util.Calendar;
import java.util.Collection;
import java.util.GregorianCalendar;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Vector;

import javax.annotation.security.RolesAllowed;
import javax.ejb.EJB;
import javax.ejb.Remote;
import javax.ejb.Stateless;
import javax.persistence.EntityManager;
import javax.persistence.NoResultException;
import javax.persistence.PersistenceContext;
import javax.persistence.Query;

import lu.tudor.santec.gecamed.core.ejb.entity.beans.Template;
import lu.tudor.santec.gecamed.core.ejb.session.beans.GECAMedSessionBean;
import lu.tudor.santec.gecamed.core.ejb.session.interfaces.TemplateManagerInterface;
import lu.tudor.santec.gecamed.core.utils.querybuilder.Group;
import lu.tudor.santec.gecamed.core.utils.querybuilder.HibernateCondition;
import lu.tudor.santec.gecamed.core.utils.querybuilder.HibernateOperator;
import lu.tudor.santec.gecamed.core.utils.querybuilder.HibernateQueryFactory;
import lu.tudor.santec.gecamed.core.utils.querybuilder.WhereClause;
import lu.tudor.santec.gecamed.formeditor.ejb.entity.beans.Form;
import lu.tudor.santec.gecamed.formeditor.ejb.entity.beans.FormTemplate;
import lu.tudor.santec.gecamed.formeditor.ejb.session.interfaces.FormManager;
import lu.tudor.santec.gecamed.patient.ejb.entity.beans.IncidentEntry;
import lu.tudor.santec.gecamed.patient.ejb.entity.beans.Patient;
import lu.tudor.santec.gecamed.patient.ejb.session.interfaces.IncidentManager;

import org.apache.log4j.Level;
import org.apache.log4j.Logger;

@Remote ({FormManager.class})
@Stateless
public class FormManagerImpl extends GECAMedSessionBean implements FormManager{
	/**
	 * static logger for this class
	 */
	private static Logger logger = Logger.getLogger(FormManagerImpl.class.getName());
	
	@PersistenceContext (unitName = "gecam")
	private EntityManager em;
	
	@EJB
	private IncidentManager incidentManager;
	
	@EJB
	private TemplateManagerInterface templateManager;
	
	
	/* ======================================== */
	// 		FORM
	/* ======================================== */
	
	
	/* ---------------------------------------- */
	// 		FORM - DELETE
	/* ---------------------------------------- */
	
	@RolesAllowed("gecam")
	public Form saveForm(Form form) 
	{
		try {
			// set last save date to now
			form.setDateLastChanged(new Timestamp(new GregorianCalendar().getTimeInMillis()));

			return (Form)em.merge(form);
		} 
		catch (Exception e) 
		{
			log(Level.INFO, e.getMessage(), e);
			return null;
		}
	}
	
	
	/* ---------------------------------------- */
	// 		FORM DELETE
	/* ---------------------------------------- */
	
	@RolesAllowed("gecam")
	public void deleteForm(Form form) 
	{
		try {
			if (form == null)
				return;
			
			IncidentEntry entry;
			try {
				entry = form.getIncidentEntry();
			} 
			catch (Exception e) 
			{
				entry = null;
			}
			
//			Integer formId = Integer.valueOf(form.getId().intValue());
			if (entry != null && entry.getId() != null) 
			{
				// remove the corresponding incident entry
				// (on delete cascade will make the form be removed)
				incidentManager.removeIncidentEntry(entry);
			} 
			else if (form.getId() != null) 
			{
				// if there is no IncidentEntry constructed, 
				// just remove the form, if stored in the DB
				em.remove(form);
			}
			
			// delete the sub forms as well
//			deleteSubformsOfForm(formId);
		} 
		catch (Exception e) 
		{
			log(Level.ERROR, e.getMessage(), e);
		}
	}

	public void deleteSubformsOfForm(Integer formId)
	{
		try {
			Collection<Form> subforms = getSubformsOfForm(formId);
			if (subforms != null)
				for (Form form : subforms)
					deleteForm(form);
		} 
		catch (Exception e) 
		{
			log(Level.ERROR, e.getMessage(), e);
		}
	}
	
	
	/* ---------------------------------------- */
	// 		FORM - GET
	/* ---------------------------------------- */
	
	@RolesAllowed("gecam")
	public Form getForm(Integer formId) 
	{
		return (Form)em.find(Form.class, formId);
	}

	@SuppressWarnings("unchecked")
	@RolesAllowed("gecam")
	public Collection<Form> getFormsOfPatient (Integer patientId) 
	{
		Query q = em.createNamedQuery(Form.GET_FORMS_OF_PATIENT);
		q.setParameter("patientId", patientId);
		return (Collection<Form>)q.getResultList();
	}

	@RolesAllowed("gecam")
	public Form getFormByIncidentEntry (Integer entryId) 
	{
		Query q = em.createNamedQuery(Form.GET_FORM_BY_IENTRY_ID);
		q.setParameter("entryId", entryId);
		
		try {
			return (Form)q.getSingleResult();
		} catch (NoResultException e) {
			return null;
		}
	}

	@SuppressWarnings("unchecked")
	@RolesAllowed("gecam")
	public List<Patient> getPatientsWithFormsOfTemplate(Integer templateId) 
	{
		Query q = em.createNamedQuery(Form.GET_PATIENTS_OF_TEMPLATE);
		q.setParameter("templateId", templateId);
		return (List<Patient>) q.getResultList();
	}
	
	@SuppressWarnings("unchecked")
	@RolesAllowed("gecam")
	public Collection<Form> getSubformsOfForm(Integer formId)
	{
		return (Collection<Form>)em.createNamedQuery(Form.GET_SUBFORMS_OF_FORM)
				.setParameter("formId", formId)
				.getResultList();
	}
	
	
	
	
	/* ======================================== */
	// 		FORM TEMPLATE
	/* ======================================== */

	
	/* ---------------------------------------- */
	// 		FORM TEMPLATE - SAVE
	/* ---------------------------------------- */
	
	private FormTemplate saveTemplate (FormTemplate template) 
	{
		try {
			if (template.getId() != null)
				deleteLastJasperTemplate(template, em.find(FormTemplate.class, template.getId()));
			
			return (FormTemplate)em.merge(template);
		} 
		catch (Exception e) 
		{
			log(Level.ERROR, e.getMessage(), e);
		}
		return null;
	}
	

	@RolesAllowed("gecam")
	public FormTemplate saveTemplateWithoutIReport (FormTemplate template)
	{
		FormTemplate	persistentTemplate;
		Template		jasperTemplate;
		
		if (template.getId() != null)
		{	
			persistentTemplate	= em.find(FormTemplate.class, template.getId());
			jasperTemplate		= persistentTemplate.getJasperTemplate();
			template.setJasperTemplate(jasperTemplate);
		}
		
		
		return saveTemplate(template);
	}
	
	
//	@RolesAllowed("gecam")
//	public FormTemplate saveTemplateWithIReport (FormTemplate template)
//	{
//		return saveTemplate(template);
//	}
	
	
	/* ---------------------------------------- */
	// 		FORM TEMPLATE - DELETE
	/* ---------------------------------------- */
	
	@RolesAllowed("gecam")
	public void deleteTemplate(FormTemplate template) 
	{
		deleteTemplate(template.getId());
	}
	
	@RolesAllowed("gecam")
	public void deleteTemplate(Integer templateId) 
	{
		if (templateId == null) 
		{
			log(Level.WARN, "Could not delete the FormTemplate. Template ID was null.");
			return;
		}
		
		try {
			FormTemplate oldTemplate = em.find(FormTemplate.class, templateId);
			
			try {
				deleteLastJasperTemplate(null, oldTemplate);
			} catch (Exception e) {
				log(Level.ERROR, e.getMessage(), e);
			}
			
			em.remove(oldTemplate);
		} 
		catch (Exception e) 
		{
			log(Level.ERROR, e.getMessage(), e);
		}
	}
	
	

	/* ---------------------------------------- */
	// 		FORM TEMPLATE - GET
	/* ---------------------------------------- */
	
	@SuppressWarnings("unchecked")
	@RolesAllowed("gecam")
	public Collection<FormTemplate> getAllTemplates() 
	{
		Query q = em.createNamedQuery(FormTemplate.GET_ALL_TEMPLATES);
		Collection<FormTemplate> l = q.getResultList();
		return l;
	}

	@SuppressWarnings("unchecked")
	@RolesAllowed("gecam")
	public Collection<FormTemplate> getAllTemplatesExceptSubformOnly() 
	{
		Query q = em.createNamedQuery(FormTemplate.GER_TEMPLATES_EXCEPT_SUBFORM_ONLY);
		Collection<FormTemplate> l = q.getResultList();
		return l;
	}
	
	@RolesAllowed("gecam")
	public FormTemplate getTemplate(Integer templateId)
	{
		return em.find(FormTemplate.class, templateId);
	}
	
	/**
	 * Get a Collection of FormTemplates with the specified name and version
	 * 
	 * @param name
	 * the name of the template
	 * @param version
	 * the version number of the template (if null -> version will be ignored)
	 * @return 
	 * returns all templates, that fit to the name and version
	 */
	@SuppressWarnings("unchecked")
	@RolesAllowed("gecam")
	public Collection<FormTemplate> getTemplatesByName(String name, String version) 
	{
		Query q = em.createNamedQuery(FormTemplate.GET_TEMPLATES_BY_NAME);
		q.setParameter("name", name);
		if (version != null) {
			q.setParameter("version", version);
		} else {
			q.setParameter("version", "%");
		}
		return (Collection<FormTemplate>)q.getResultList();
	}

	
	/* ================================== */
	/* ==========  FormValue:  ========== */
	/* ================================== */
	
//	@SuppressWarnings("unchecked")
//	@RolesAllowed("gecam")
//	public List<Object[]> getValuesOfComponent(String componentKey, 
//			Calendar sinceWhen, Integer limit, Integer templateId, Integer patientId) {
//		/* ======================================== */
//		Query q;
//		if (sinceWhen == null) {
//			/* ---------------------------------------- */
//			q = em.createNamedQuery(FormValue.VALUES_AND_DATE_OF_COMPONENT);
//			/* ---------------------------------------- */
//		} else {
//			/* ---------------------------------------- */
//			q = em.createNamedQuery(FormValue.VALUES_AND_DATE_OF_COMPONENT_SINCE_DATE);
//			q.setParameter("date", sinceWhen.getTime());
//			/* ---------------------------------------- */
//		}
//		q.setParameter("patientId", patientId);
//		q.setParameter("templateId", templateId);
//		q.setParameter("key", componentKey);
//		
//		if (limit != null && limit > 0) {
//			/* ---------------------------------------- */
//			q.setMaxResults(limit);
//			/* ---------------------------------------- */
//		}
//		
//		return (List<Object[]>) q.getResultList();
//		/* ======================================== */
//	}
	
	@SuppressWarnings("unchecked")
	public Vector<Object[]> getValuesOfComponent(Calendar sinceWhen, Integer limit, 
			Integer templateId, Integer patientId, String ... keyRefs) 
	{
		/* **********  CREATE THE WHERE CLAUSE  ********** */
		WhereClause c = new WhereClause ();
	    c.setOperator(HibernateOperator.c_AndOperator);
	    
	    // f.templateId = :templateId
	    c.addCondition(new HibernateCondition("f", "templateId", 
	    		HibernateOperator.c_EqualOperator, "templateId", templateId));
	    // f.patientId = :patientId
	    c.addCondition(new HibernateCondition("f", "patientId", 
	    		HibernateOperator.c_EqualOperator, "patientId", patientId));
	    
	    Group g = new Group();
	    g.setOperator(HibernateOperator.c_OrOperator);
	    
	    // build the search map, to assign the  keys to the object indices 
	    HashMap<String, Integer[]> search = new HashMap<String, Integer[]>();
	    for (int i = 0; i < keyRefs.length; i++) {
	    	/* ---------------------------------------- */
	    	g.addCondition(new HibernateCondition("v", "key", 
	    			HibernateOperator.c_EqualOperator, "key"+i, keyRefs[i]));
	    	
	    	// put it to position i+1, because at position 0 will be the date
	    	Integer[] indexes = search.put(keyRefs[i], new Integer[]{i+1});
	    	if (indexes != null) {
	    		Integer[] newIndexes = new Integer[indexes.length+1];
	    		System.arraycopy(indexes, 0, newIndexes, 0, indexes.length);
	    		newIndexes[indexes.length] = i+1;
	    		search.put(keyRefs[i], newIndexes);
	    	}
	    	/* ---------------------------------------- */
	    }	    	
		c.addGroup(g);
		
		if (sinceWhen != null) {
		    c.addCondition(new HibernateCondition("f", "dateCreated", 
		    		HibernateOperator.c_GreaterOrEqualOperator, "dateCreated", sinceWhen.getTime()));
		}
		/* **************************************** */
		
		Query q;
		LinkedHashMap<Integer, Object[]> hash = new LinkedHashMap<Integer, Object[]>();
		try {
			/* ---------------------------------------- */
			q = HibernateQueryFactory.buildQueryFromNestedWhereClause (
					em, "SELECT DISTINCT f.dateCreated, v.formId, v.key, v.value FROM Form f, FormValue v " +
							"$WHERECLAUSE AND v.formId = f.id ORDER BY f.dateCreated DESC", c);
			
			if (limit != null && limit > 0) {
				q.setMaxResults(limit);
			}
			
			List<Object[]> result = q.getResultList();
			
			/* put the creation date and the values of the referenced components into an array 
			 * and these arrays into a list
			 */
			for (Object[] o : result) {
				/* ---------------------------------------- */
				Integer formId = (Integer) o[1];
				Object[] data = hash.get(formId);
				if (data == null) {
					// for this form, no data exists yet -> create a new object array
					data = new Object[keyRefs.length+1];
//					Calendar cal = new GregorianCalendar();
//					cal.setTime((Date)o[0]);
					// set the date of creation at position 0
					data[0] = o[0];
					hash.put(formId, data);
				}
				
				String key = (String) o[2];
				String value = (String) o[3];
				
				for (Integer index : search.get(key)) {
					data[index] = value;
				} 
				/* ---------------------------------------- */
			}
			/* ---------------------------------------- */
		} catch (Exception e) {
			logger.log(Level.ERROR, e.getMessage(), e);
		}
	    
		return new Vector<Object[]>(hash.values());
	}
	
	
	/* ==================================== */
	/* ==========  HELP-METHODS  ========== */
	/* ==================================== */
	
	@SuppressWarnings("unchecked")
	private void deleteLastJasperTemplate (FormTemplate newTemplate, FormTemplate oldTemplate) {
		/* ======================================== */
		if (oldTemplate != null
				&& oldTemplate.getJasperTemplate() != null
				&& (newTemplate == null
						|| newTemplate.getJasperTemplate() == null)) {
			/* ---------------------------------------- */
			/*
			 * To the old FormTemplate a JasperTemplate was assigned to,
			 * but to the new FormTemplate, there won't be a JasperTemplate
			 * be assigned to, respectively there is no new FormTemplate. 
			 */
			
			// look how many FormTemplates with the same JasperTemplate-ID exist 
			Query q = em.createNamedQuery(FormTemplate.GET_TEMPLATES_WITH_JASPER_ID);
			q.setParameter("jasperId", oldTemplate.getJasperTemplate().getId());
			List<FormTemplate> l = q.getResultList();
			
			if (l != null && l.size() <= 1) {
				/* 
				 * If this is the last FormTemplate with this JasperTemplate-ID,
				 * delete the Template
				 */ 
				try {
					/* ---------------------------------------- */
					templateManager.deleteTemplate(oldTemplate.getJasperTemplate());
					/* ---------------------------------------- */
				} catch (Exception e) {
					log(Level.ERROR, e.getMessage(), e);
				}
			}
			/* ---------------------------------------- */
		}
		/* ======================================== */
	}
	/* ======================================== */
}
