/*******************************************************************************
 * 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
 *******************************************************************************/
/*
 * Author: Johannes Hermen Tudor/Santec
 * Mail: johannes.hermen@tudor.lu
 * Created: Mar 11, 2005
 *
 */
package lu.tudor.santec.gecamed.core.ejb.session.beans;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.InputStream;
import java.util.Collection;
import java.util.Date;
import java.util.HashMap;
import java.util.List;

import javax.annotation.security.RolesAllowed;
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.entity.beans.XSLTemplate;
import lu.tudor.santec.gecamed.core.ejb.session.interfaces.TemplateManagerInterface;
import lu.tudor.santec.gecamed.core.utils.JasperTemplateBean;
import lu.tudor.santec.gecamed.formeditor.ejb.entity.beans.FormTemplate;
import net.sf.jasperreports.engine.JasperCompileManager;
import net.sf.jasperreports.engine.JasperFillManager;
import net.sf.jasperreports.engine.JasperPrint;
import net.sf.jasperreports.engine.JasperReport;
import net.sf.jasperreports.engine.design.JasperDesign;
import net.sf.jasperreports.engine.util.JRLoader;
import net.sf.jasperreports.engine.util.JRProperties;
import net.sf.jasperreports.engine.xml.JRXmlLoader;

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


/**
 *
 * 	This SessionBean class manages the access to the printing templates
 * stores in the database. Templates can be stored per physician or for all users.
 *
 * @author Johannes Hermen johannes.hermen(at)tudor.lu
 * 
 * @Version
 * <br>$Log: TemplateManagerBean.java,v $
 * <br>Revision 1.26  2014-02-13 16:54:55  ferring
 * <br>log message changed
 * <br>
 * <br>Revision 1.25  2014-02-11 12:49:59  ferring
 * <br>Template loading modified
 * <br>
 * <br>Revision 1.24  2013-12-27 18:09:22  donak
 * <br>Cleanup of imports
 * <br>
 * <br>Revision 1.23  2013-07-15 06:18:36  ferring
 * <br>logging changed
 * <br>
 * <br>Revision 1.22  2013-07-02 09:51:46  ferring
 * <br>logging level for FOP and JasperReport set to ERROR
 * <br>
 * <br>Revision 1.21  2013-03-19 08:48:41  ferring
 * <br>Error handling changed
 * <br>
 * <br>Revision 1.20  2013-01-22 14:21:34  ferring
 * <br>iReport templates will now be compiled only once until the server is stopped
 * <br>
 * <br>Revision 1.19  2012-07-03 14:17:46  ferring
 * <br>iReport printing improved
 * <br>
 * <br>Revision 1.18  2011-10-27 10:33:18  ferring
 * <br>XSL template management for forms changed
 * <br>
 * <br>Revision 1.17  2011-10-05 08:28:59  ferring
 * <br>iReport printing changed. Beans added, only one formatter used for everything and reports changed, so that they only use the bean (util) Parameter
 * <br>
 * <br>Revision 1.16  2010-12-29 09:04:59  ferring
 * <br>Option added to print several invoices of same patient and same physician, using the same iReport on one reminder, instead of using one for each.
 * <br>
 * <br>Revision 1.15  2010-07-09 09:27:58  ferring
 * <br>JasperReport configuration changed
 * <br>
 * <br>Revision 1.14  2010-04-08 14:57:14  hermen
 * <br>jasper templates are now compiled on demand from the jrxml files
 * <br>
 * <br>Revision 1.13  2010-04-08 11:01:20  hermen
 * <br>added option to compile report
 * <br>
 * <br>Revision 1.12  2008-12-22 09:40:38  hermen
 * <br>added downloading of template to template settings
 * <br>
 * <br>Revision 1.11  2008-09-25 09:43:07  heinemann
 * <br>fixed copyrights
 * <br>
 * <br>Revision 1.10  2008-01-15 10:44:12  hermen
 * <br>updated Javadoc and refactured code
 * <br>
 * 
 */
@Remote({TemplateManagerInterface.class})
@Stateless
public class TemplateManagerBean implements TemplateManagerInterface 
{
    //~ Static fields/initializers =============================================

//    private static final long serialVersionUID = 1L;
    
    /**
	 * static logger for this class
	 */
	private static Logger logger = Logger.getLogger(TemplateManagerBean.class.getName());
	
	
	static
	{
		// ==============================================================================
		// Needed for the JasperReport version 3.7.3, 
		// because a font wasn't found anymore.
		// (another, available font will be taken.
		//
		// ==============================================================================
    	JRProperties.setProperty("net.sf.jasperreports.awt.ignore.missing.font", true);
    	
    	// set the logging level for jasper reports to ERROR
    	org.apache.log4j.Logger.getLogger("net.sf.jasperreports").setLevel(org.apache.log4j.Level.ERROR);
		// ===============================================================================
	}
	
    @PersistenceContext (unitName="gecam")
	EntityManager em;
	
    //~ Methods ================================================================
    
    /* (non-Javadoc)
	 * @see lu.tudor.santec.gecamed.core.ejb.session.beans.ListManagerInterface#getTitles()
	 */
//    @SuppressWarnings("unchecked")
//	@RolesAllowed("gecam")
//    public String[] getTemplateTypes() throws Exception
//    {
//        Collection <TemplateType> 	l_Types;
//    	Iterator <TemplateType>		l_TypeIterator;
//    	String[]					l_TypeNames = null;
//        int							l_Index = 0;
//    	
//    	l_Types = em.createNamedQuery("findAllTemplateTypes").getResultList();
//    	if (l_Types != null)
//    		{
//    		l_TypeNames = new String [l_Types.size()];
//    		l_TypeIterator = l_Types.iterator();
//    		while (l_TypeIterator.hasNext())
//    			{
//    			l_TypeNames [l_Index++] = l_TypeIterator.next().getValue();
//    			}
//     		}
//    	
//    	return l_TypeNames;
//	}
    

	@SuppressWarnings("unchecked")
	@RolesAllowed("gecam")
	public Collection<Template> getTemplatesByType (String templateType) {
		try {
			return em.createNamedQuery("getTemplatesByType")
					.setParameter("type", templateType)
					.getResultList();
		} catch (Exception e) {
			logger.log(Level.ERROR, e.getMessage(), e);
			return null;
		}
	}

	/* (non-Javadoc)
	 * @see lu.tudor.santec.gecamed.core.ejb.session.interfaces.TemplateManagerInterface#getTemplateByID (java.lang.Integer)
	 */
	 
    @RolesAllowed("gecam")
	public Template getTemplateByID (Integer p_TemplateID) throws Exception 
		{
		Template l_Template;
    	
    	try {
			l_Template =  em.find (Template.class, p_TemplateID);
    		}
		catch (NoResultException p_Exception)	
			{
			l_Template = null;
			} 
		return l_Template;
		}

	/* (non-Javadoc)
	 * @see lu.tudor.santec.gecamed.core.ejb.session.interfaces.TemplateManagerInterface#getTemplatesByPhysicianID(java.lang.Integer)
	 */

	@SuppressWarnings("unchecked")
	public Collection<Template> getTemplatesByPhysicianID(Integer physicianID) throws Exception {
		try {
			return em.createNamedQuery("findTemplateByPhysicianID")
			.setParameter("physicianID", physicianID)
			.getResultList();			
		} catch (Exception e) {
			return null;
		}
	}
    
	/* (non-Javadoc)
	 * @see lu.tudor.santec.gecamed.core.ejb.session.interfaces.TemplateManagerInterface#findTemplateByTypeAndPhysicianID(java.lang.String, java.lang.Integer)
	 */
	@SuppressWarnings("unchecked")
	public Collection<Template> getTemplatesByTypeAndPhysicianID (String templateType, Integer physicianID) throws Exception {
		try {
			return (Collection<Template>) em.createNamedQuery("findTemplateByTypeAndPhysicianID")
			.setParameter("type", templateType)
			.setParameter("physicianID", physicianID)
			.getResultList();		
		} catch (NoResultException e) {
			return null;
		}
	}

	/* (non-Javadoc)
	 * @see lu.tudor.santec.gecamed.core.ejb.session.interfaces.TemplateManagerInterface#findTemplateByTypeAndPhysicianID(java.lang.String, java.lang.Integer)
	 */
	public Template findTemplateByTypeAndPhysicianID(String templateType, Integer physicianID) throws Exception {
		try {
			return (Template) em.createNamedQuery("getTemplateByTypePhysicianAndExpiry")
			.setParameter("type", templateType)
			.setParameter("physicianID", physicianID)
			.setParameter("dateOfInterest", new Date ())
			.setMaxResults(1)
			.getSingleResult();			
		} catch (Exception e) {
			return null;
		}
	}

	/* (non-Javadoc)
	 * @see lu.tudor.santec.gecamed.core.ejb.session.interfaces.TemplateManagerInterface#getTemplateByTypePhysicianAndExpiry(java.lang.String, java.lang.Integer, java.util.Date)
	 */
	public Template getTemplateByTypePhysicianAndExpiry (String templateType, Integer physicianID, Date dateOfInterest) throws Exception {
		try {
			return (Template) em.createNamedQuery("getTemplateByTypePhysicianAndExpiry")
			.setParameter("type", templateType)
			.setParameter("physicianID", physicianID)
			.setParameter("dateOfInterest", dateOfInterest)
			.setMaxResults(1)
			.getSingleResult();			
		} catch (Exception e) {
			return null;
		}
	}
	
	
	/* (non-Javadoc)
	 * @see lu.tudor.santec.gecamed.core.ejb.session.interfaces.TemplateManagerInterface#saveTemplate(lu.tudor.santec.gecamed.core.ejb.entity.beans.Template)
	 */
	public Template saveTemplate(Template template) throws Exception {
		// compile the template if not allready done
		if (template.getJasper() == null) {
			ByteArrayOutputStream bous = new ByteArrayOutputStream();
			JasperCompileManager.compileReportToStream(
					new ByteArrayInputStream(template.getJrxml()), bous);
			template.setJasper(bous.toByteArray());
			logger.info("Compiled Template: " + template.getName());
		}
		// save the template
		return em.merge(template);
	}
	
	
	public FormTemplate saveTemplate(Template iReport, Integer formTemplateId) throws Exception 
	{
		FormTemplate	formTemplate	= em.find(FormTemplate.class, formTemplateId);
		
		if (iReport == null)
		{
			// delete the template
			Template jasperTemplate = formTemplate.getJasperTemplate();
			
			if (jasperTemplate != null && jasperTemplate.isPersistent())
				deleteTemplate(jasperTemplate.getId());
		}
		else
		{
			// save the template
			iReport		= saveTemplate(iReport);
		}
		
		// set the saved iReport for the FormTemplate and save it
		formTemplate.setJasperTemplate(iReport);
		formTemplate	= em.merge(formTemplate);
		
		return formTemplate;
	}
	

	/* (non-Javadoc)
	 * @see lu.tudor.santec.gecamed.core.ejb.session.interfaces.TemplateManagerInterface#deleteTemplate(java.lang.Integer, java.lang.String)
	 */
	public void deleteTemplate(Integer physicianID, String type) throws Exception {
		Template t = findTemplateByTypeAndPhysicianID(type, physicianID);
		if (t != null) {
			em.remove(t);
		}	
	}

	/* (non-Javadoc)
	 * @see lu.tudor.santec.gecamed.core.ejb.session.interfaces.TemplateManagerInterface#deleteTemplate(lu.tudor.santec.gecamed.core.ejb.entity.beans.Template)
	 */
	public void deleteTemplate(Template template) throws Exception {
		if (template != null) 
			template = em.find(Template.class, template.getId());
		if (template != null) {
			em.remove(template);
		}	
	}
	
//	/* (non-Javadoc)
//	 * @see lu.tudor.santec.gecamed.core.ejb.session.interfaces.TemplateManagerInterface#getTemplate(java.lang.String, java.lang.Integer)
//	 */
//	private Template getTemplate(String type, Integer physicianID) throws Exception {
//		Template t = findTemplateByTypeAndPhysicianID(type, physicianID);
//		if (t == null) {
//			t = findTemplateByTypeAndPhysicianID(type, Template.OFFICE);
//		}
//		return t;
//	}

	/* (non-Javadoc)
	 * @see lu.tudor.santec.gecamed.core.ejb.session.interfaces.TemplateManagerInterface#getTemplate(java.lang.String, java.lang.Integer)
	 */
	private Template getTemplate (String type, Integer physicianID, Date dateOfInterest) throws Exception 
		{
		Template t = getTemplateByTypePhysicianAndExpiry (type, physicianID, dateOfInterest);
		if (t == null) {
			t = getTemplateByTypePhysicianAndExpiry(type, Template.OFFICE, dateOfInterest);
		}
		return t;
	}
	
	
	/* (non-Javadoc)
	 * @see lu.tudor.santec.gecamed.core.ejb.session.interfaces.TemplateManagerInterface#createJasperPrint(net.sf.jasperreports.engine.JasperReport, java.util.HashMap)
	 */
	public JasperPrint createJasperPrint(JasperReport report, HashMap<String, Object> parameters) throws Exception {
		 return JasperFillManager.fillReport(report, parameters);
	}
	
	
//	public String getDefaultTemplatePath (String templateName)
//	{
//		try
//		{
//			TemplateType tmpl = (TemplateType) em.createNamedQuery("findTemplateTypesByName")
//					.setParameter("name", templateName)
//					.getSingleResult();
//			return tmpl.getTemplateFilePath();
//		}
//		catch (Exception e)
//		{
//			logger.error("Error while trying to find the template name", e);
//		}
//		return null;
//	}
	
	
	@RolesAllowed("gecam")
	public JasperReport findTemplate (String templateBeanName)
	{
		try
		{
			return findTemplate(JasperTemplateBean.getTemplateBean(templateBeanName));
		}
		catch (Exception e)
		{
			logger.log(Level.WARN, "error finding template", e);
			return null;
		}
	}
	
	
	@RolesAllowed("gecam")
	public JasperReport findTemplate (String templateBeanName, Integer physicianId, Date dateOfInterest)
	{
		JasperReport			jasperReport	= null;
		Template				template		= null;
		JasperTemplateBean		templateBean	= JasperTemplateBean.getTemplateBean(templateBeanName);
		InputStream				is;
		ByteArrayOutputStream	baos;
		JasperDesign			design;
		
		
		if (templateBean == null)
		{
			// if the JasperTemplateBean wasn't found, loading the report is not possible
			try
			{
				throw new Exception();
			}
			catch (Exception e)
			{
				logger.log(Level.ERROR, "Cannot find a JasperTemplateBean, stored under the name \"" + templateBeanName + "\".", e);
				return null;
			}
		}
		
		try
		{
			if (templateBean.getType() != null)
			{
				// if type is != null, there might be a template in the database
				try
				{
					template = getTemplate(templateBean.getType(), physicianId, dateOfInterest);
				}
				catch (Exception e)
				{
					logger.log(Level.ERROR, "", e);
				}
			}
			// else load a default template
			
			if (template != null)
			{
				// a template was found in the DB
				if (template.getJasper() == null)
				{
					// if the template was not compiled stored in the DB, do it now
					// shouldn't happen normally
					is = null;
					try
					{
						is = new ByteArrayInputStream(template.getJrxml());
						design = JRXmlLoader.load(is);
					}
					finally 
					{
						if (is != null) is.close();
					}
					
					jasperReport = JasperCompileManager.compileReport(design);
					baos = null;
					try
					{
						baos = new ByteArrayOutputStream();
						JasperCompileManager.compileReportToStream(new ByteArrayInputStream(template.getJrxml()), baos);
						template.setJasper(baos.toByteArray());
					}
					finally
					{
						if (baos != null) baos.close();
					}
				}
				
				// load the JasperReport object from the bytes
				is = null;
				try
				{
					is = new ByteArrayInputStream(template.getJasper());
					jasperReport = (JasperReport) JRLoader.loadObject(is);
				}
				catch (Exception e)
				{
					throw e;
				}
				finally 
				{
					if (is != null) is.close();
				}
			}
			else
			{
				// template not found in DB for the specified parameters - load the default template
				jasperReport = findTemplate(templateBean);
			}
		}
		catch (Exception e)
		{
			logger.log(Level.WARN, "error finding template", e);
		}
		
		return jasperReport;
	}
	
	
	private JasperReport findTemplate (JasperTemplateBean templateBean) throws Exception
	{
		InputStream		is = null;
		JasperDesign	design;
		
		
		if (templateBean.getReport() == null)
		{
			// compile the default report
			try
			{
				is		= TemplateManagerBean.class.getResourceAsStream(templateBean.getResourcePath());
				design	= (JasperDesign) JRXmlLoader.load(is);
				templateBean.setReport(compileReport(design));
			}
			catch (Exception e)
			{
				throw new Exception("Error while compiling default template.", e);
			}
			finally
			{
				if (is != null) is.close();
			}
		}
		return templateBean.getReport();
	}
	
	
	private JasperReport compileReport(JasperDesign jasperDesign) throws Exception
	{
		// compile the template
		try
		{
			logger.info("compiling JasperDesign: " + jasperDesign.getName());
			return JasperCompileManager.compileReport(jasperDesign);
		}
		catch (Exception e)
		{
			logger.warn("compiling JasperDesign failed!", e);
			throw e;
		}
	}


	@SuppressWarnings("unchecked")
	public List<XSLTemplate> getXSLTemplates(String referenceType, Integer referenceId, Integer physicianId) throws Exception
	{
		List<XSLTemplate> templates = null;
		String queryName;
		
		if (physicianId == null)
			queryName = XSLTemplate.GET_TEMPLATES_FOR_OFFICE;
		else
			queryName = XSLTemplate.GET_TEMPLATES_FOR_PHYSICIAN;
		
		try
		{
			Query q = em.createNamedQuery(queryName)
					.setParameter("referenceType", 	referenceType)
					.setParameter("referenceId", 	referenceId);
			if (physicianId != null)
				q.setParameter("physicianId", 		physicianId);
			
			templates = q.getResultList();
		}
		catch (Exception e)
		{
			logger.error(e.getMessage(), e);
		}
		
		return templates;
	}


	public XSLTemplate saveTemplate(XSLTemplate template) throws Exception
	{
		return em.merge(template);
	}


	public void deleteTemplate(XSLTemplate template) throws Exception
	{
		if (template != null)
		{
			template = em.find(XSLTemplate.class, template.getId());
			if (template != null)
				em.remove(template);
		}
	}


	public void deleteTemplate(Integer templateId) throws Exception
	{
		if (templateId != null)
		{
			XSLTemplate template = em.find(XSLTemplate.class, templateId);
			if (template != null)
				em.remove(template);
		}
	}
}
