/*******************************************************************************
 * 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.BufferedOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Collection;
import java.util.Date;
import java.util.Iterator;
import java.util.List;

import javax.ejb.Remote;
import javax.ejb.Stateless;
import javax.persistence.NoResultException;

import lu.tudor.santec.gecamed.core.ejb.session.beans.GECAMedSessionBean;
import lu.tudor.santec.gecamed.core.utils.ServerConfig;
import lu.tudor.santec.gecamed.labo.ejb.entity.beans.Certifier;
import lu.tudor.santec.gecamed.labo.ejb.entity.beans.Laboratory;
import lu.tudor.santec.gecamed.labo.ejb.entity.beans.LaboratoryCertificate;
import lu.tudor.santec.gecamed.labo.ejb.session.interfaces.LaboratoryInterface;
import lu.tudor.santec.gecamed.labo.utils.LaboException;

import org.apache.log4j.Logger;
import org.hibernate.Hibernate;

@Stateless
@Remote (LaboratoryInterface.class)
public class LaboratoryBean extends GECAMedSessionBean implements LaboratoryInterface
	{
//	private static final long serialVersionUID = 1L;
	
	private static final String LABO_FAIL_PATH = ServerConfig.getProperty(ServerConfig.FILE_BASE_DIR) 
			+ File.separator + "labo_fail_dir";
	
	
	private static Logger logger = Logger.getLogger(LaboratoryBean.class.getName());
	
	/**
	 * This object is used in a synchronised block. Be careful not to produce a dead lock.
	 */
	private static String	m_CAChain;
	
	private static DateFormat dateFormatter = new SimpleDateFormat("yyyy-MM-dd_HH_mm_ss");

//---------------------------------------------------------------------------
//***************************************************************************
//* Constants	                                                            *
//***************************************************************************
//---------------------------------------------------------------------------
//---------------------------------------------------------------------------
//***************************************************************************
//* Constructor	                                                            *
//***************************************************************************
//---------------------------------------------------------------------------
//---------------------------------------------------------------------------
//***************************************************************************
//* Primitives	                                                            *
//***************************************************************************
//---------------------------------------------------------------------------

private Laboratory fetchDependency (Laboratory p_Laboratory, Integer p_Dependency)
	{
	switch (p_Dependency)
		{
		case LaboratoryInterface.c_ContactPersonDependency:
			
			if ((p_Laboratory.getContactPersons() != null) && (!Hibernate.isInitialized(p_Laboratory.getContactPersons())) )
				Hibernate.initialize (p_Laboratory.getContactPersons());

			break;		
		
		case LaboratoryInterface.c_AddressDependency:
			
			if ((p_Laboratory.getAddresses() != null) && (!Hibernate.isInitialized(p_Laboratory.getAddresses())) )
				Hibernate.initialize (p_Laboratory.getAddresses());

			break;		

		case LaboratoryInterface.c_CertificateDependency:
			
			if ((p_Laboratory.getAddresses() != null) && (!Hibernate.isInitialized(p_Laboratory.getAddresses())) )
				Hibernate.initialize (p_Laboratory.getAddresses());

			break;		
		}
	
	return 	p_Laboratory;		
	}
	
//---------------------------------------------------------------------------
//***************************************************************************
//* Class Body	                                                            *
//***************************************************************************
//---------------------------------------------------------------------------

public Laboratory getLaboratoryByID (Integer p_ID) throws Exception
	{
	Laboratory l_Laboratory = null;

	try	{
		l_Laboratory = m_EntityManager.find(Laboratory.class, p_ID);
		}
	catch (NoResultException p_Exception)
		{
		l_Laboratory = null;
		}

	return l_Laboratory;
	}
	
//---------------------------------------------------------------------------

public Laboratory getLaboratoryByUCMCode (String p_UCMCode) throws Exception
	{
	Laboratory l_Laboratory = null;

	try	{
		l_Laboratory = (Laboratory) m_EntityManager.createNamedQuery(Laboratory.c_LaboratoryByUCMCode)
									  			   .setParameter(Laboratory.c_UCMCodeParameter, p_UCMCode)
									  			   .getSingleResult();
		}
	catch (NoResultException p_Exception)
		{
		l_Laboratory = null;
		}

	return l_Laboratory;
	}
	
//---------------------------------------------------------------------------

public Laboratory	fetchLazyDependency (Laboratory p_Laboratory, Integer p_Dependency) throws Exception
	{
	if ((p_Laboratory == null) || 
	    (p_Dependency == null) || 
	    !p_Laboratory.isPersistent()) return p_Laboratory;

	p_Laboratory = m_EntityManager.find (Laboratory.class, p_Laboratory.getId());
	p_Laboratory = this.fetchDependency(p_Laboratory, p_Dependency);
	
	return 	p_Laboratory;
	}

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

public Laboratory	fetchLazyDependencies (Laboratory p_Laboratory, Collection<Integer> p_Dependencies) throws Exception
	{
	Iterator <Integer>	l_DependencyIterator;
		
	if ((p_Laboratory == null) || 
	    (p_Dependencies == null) || 
	    !p_Laboratory.isPersistent()) return p_Laboratory;

	p_Laboratory = m_EntityManager.find (Laboratory.class, p_Laboratory.getId());
	
	l_DependencyIterator = p_Dependencies.iterator();
	while (l_DependencyIterator.hasNext())
		{
		p_Laboratory = this.fetchDependency(p_Laboratory, l_DependencyIterator.next());
		}
	
	return 	p_Laboratory;
	}

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

public Laboratory saveLaboratory (Laboratory p_Laboratory) throws Exception
	{
	if (p_Laboratory == null) return p_Laboratory;
	
	p_Laboratory = m_EntityManager.merge(p_Laboratory);
	
	return p_Laboratory;
	}

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

public void deleteLaboratory (Laboratory p_Laboratory) throws Exception
	{
	if ((p_Laboratory == null) || !p_Laboratory.isPersistent()) return;
	
	p_Laboratory = m_EntityManager.find (Laboratory.class, p_Laboratory.getId());
	if (p_Laboratory != null) m_EntityManager.remove (p_Laboratory);
	}

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

@SuppressWarnings("unchecked")
public Collection <String> getAllCertificateLabels () throws Exception
	{
	Collection <String> l_Labels = null;

	try	{
		l_Labels = m_EntityManager.createNamedQuery(LaboratoryCertificate.c_AllCertificateLabels)
								  .getResultList();
		}
	catch (NoResultException p_Exception)
		{
		l_Labels = null;
		}

	return l_Labels;
	}
	
//---------------------------------------------------------------------------

public LaboratoryCertificate getCertificateByLabel (String	p_Label) throws Exception
	{
	LaboratoryCertificate l_Certificate = null;

	if (p_Label == null) return l_Certificate;
	
	try	{
		l_Certificate = (LaboratoryCertificate) m_EntityManager.createNamedQuery(LaboratoryCertificate.c_CertificateByLabel)
									 						   .setParameter (LaboratoryCertificate.c_LabelParameter,p_Label)
									 						   .setMaxResults (1)
									 						   .getSingleResult();
		}
	catch (NoResultException p_Exception)
		{
		l_Certificate = null;
		}

	return l_Certificate;
	}
	
//---------------------------------------------------------------------------

public LaboratoryCertificate saveCertificate (LaboratoryCertificate p_Certificate) throws Exception
	{
	if (p_Certificate == null) return p_Certificate;
	
	p_Certificate = m_EntityManager.merge(p_Certificate);
	
	return p_Certificate;
	}

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

public void deleteCertificate (LaboratoryCertificate p_Certificate) throws Exception
	{
	if (p_Certificate == null) return;
	
	if (p_Certificate.isPersistent())
		{
		p_Certificate = m_EntityManager.find (LaboratoryCertificate.class, p_Certificate.getId());
		m_EntityManager.remove(p_Certificate);
		}
	}

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

public synchronized String getCAChain () throws LaboException
{
	Boolean	isChainSet	= null;
	
	
	if (m_CAChain != null)
	{
		isChainSet	= isChainSet();
		
		if (!isChainSet.booleanValue())
			m_CAChain	= null;
	}
	
	if (m_CAChain == null)
		m_CAChain	= loadChainString();
	
	return m_CAChain;
}

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

private Boolean isChainSet()
{
	return ((Long) m_EntityManager.createNamedQuery(Certifier.CHECK_CERTIFICATE_CHAIN)
			.getSingleResult()) > 0;
}

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

private String loadChainString () throws LaboException
{
	Certifier		certificateChain;
	StringBuilder	certificateBuffer;
	List<?>			certificates;
	String			data;
	
	
	try
	{
		certificateChain	= (Certifier) m_EntityManager
				.createNamedQuery(Certifier.GET_CERTIFICATE_BY_LABEL)
				.setParameter("label", Certifier.CHAIN_LABEL)
				.getSingleResult();
	}
	catch (NoResultException e)
	{
		certificateChain	= new Certifier();
		certificateChain.setLabel(Certifier.CHAIN_LABEL);
	}
	
	
	if (certificateChain.getCertificate() == null)
	{
		certificateBuffer	= new StringBuilder();
		certificates		= m_EntityManager
				.createNamedQuery(Certifier.GET_ALL_CERTIFICATES)
				.getResultList();
		
		for (Object certificate : certificates)
		{
			data	= ((Certifier)certificate).getCertificate();
			
			if (data != null && data.trim().length() > 0)
			{
				certificateBuffer.append(data)
						.append("\n");
			}
		}
		
		certificateChain.setCertificate(certificateBuffer.toString());
		certificateChain.setLastModified(new Date());
		certificateChain	= m_EntityManager.merge(certificateChain);
	}
	
	return certificateChain.getCertificate();
}


public Certifier getCertifierByLabel (String label)
{
	try
	{
		return (Certifier) m_EntityManager.createNamedQuery(Certifier.GET_CERTIFICATE_BY_LABEL)
				.setParameter("label", label)
				.getSingleResult();
	}
	catch (NoResultException e)
	{
		return null;
	}
}


public Certifier getOutdatedCertifier (String label, Date lastModified)
{
	Certifier	certifier;
	
	
	certifier	= getCertifierByLabel(label);
	
	if (certifier == null)
	{
		certifier	= new Certifier();
		certifier.setLabel(label);
	}
	else if (certifier.getLastModified().after(lastModified))
	{
		certifier	= null;
	}
	
	return certifier;
}


public Certifier saveCertifier(Certifier c)
{
	c.setLastModified(new Date());
	return m_EntityManager.merge(c);
}


/**
 * Creates a file with the given data and the given name plus the date at start<br>
 * and a log file containing the log message and the stack trace of the cause, <br>
 * as far as available.
 * 
 * @param fileData The byte of the file to save
 * @param fileName The name of the file to save
 * @param logMessage The log message to save in the log file
 * @param cause The cause to get the stack trace from for the log file
 * @return The full pathname of the file saved.
 */
public String saveFailedResultImport (byte[] fileData, String fileName, String logMessage, Throwable cause)
{
	File	laboFailDir = new File(LABO_FAIL_PATH);
	File	failedResultFile;
	File	failedResultLogFile;
	String	failedResultPath;
	
	
	if (!laboFailDir.exists() || !laboFailDir.isDirectory())
	{
		laboFailDir.delete();
		laboFailDir.mkdirs();
	}
	
	failedResultPath	= new StringBuilder()
			.append(LABO_FAIL_PATH)
			.append(File.separator)
			.append(dateFormatter.format(new Date()))
			.append("_")
			.append(fileName)
			.toString();
	failedResultFile	= new File(failedResultPath);
	failedResultLogFile	= new File(failedResultPath + ".log");
	
	try
	{
		failedResultFile.createNewFile();
		OutputStream os = new BufferedOutputStream(new FileOutputStream(failedResultFile));
		os.write(fileData);
		os.close();
		
		logger.info("Failed result saved in file: " + failedResultFile.getAbsolutePath());
	}
	catch (IOException e)
	{
		logger.error("Error when trying to save failed result file.", e);
		return null;
	}
	
	if (logMessage != null || cause != null)
	{
		try
		{
			failedResultLogFile.createNewFile();
			OutputStream os = new BufferedOutputStream(new FileOutputStream(failedResultLogFile));
			
			if (logMessage != null)
			{
				os.write(logMessage.getBytes("UTF8"));
				os.write(System.getProperty("line.separator").getBytes());
			}
			
			if (cause != null)
			{
				StringWriter writer = new StringWriter();
				cause.printStackTrace(new PrintWriter(writer));
				os.write(writer.toString().getBytes("UTF8"));
			}
			os.close();
		}
		catch (IOException e)
		{
			logger.error("Error when trying to save failed result file.", e);
		}
	}
	
	return failedResultFile.getAbsolutePath();
}

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

	}
