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

import java.util.Collection;
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.NonUniqueResultException;
import javax.persistence.PersistenceContext;
import javax.persistence.Query;

import lu.tudor.santec.gecamed.address.ejb.entity.beans.Country;
import lu.tudor.santec.gecamed.address.ejb.entity.beans.Locality;
import lu.tudor.santec.gecamed.address.ejb.entity.beans.Zip;
import lu.tudor.santec.gecamed.address.ejb.session.interfaces.AddressManagerInterface;

import org.apache.log4j.Logger;

/**
 * Session bean that manages the access to all address related beans/tables in the db.
 * It allows the retrieval of zip/ocality/coutry etc.... 
 * 
 * @author Johannes Hermen johannes.hermen(at)tudor.lu
 *
 * @Version
 * <br>$Log: AddressManagerBean.java,v $
 * <br>Revision 1.10  2012-11-30 15:34:36  ferring
 * <br>bug in return of SQL fixed
 * <br>
 * <br>Revision 1.9  2012-11-30 08:00:59  ferring
 * <br>postal code added to country and to formatter
 * <br>Short patient title corrected and long patient title added
 * <br>
 * <br>Revision 1.8  2012-02-28 15:08:18  hermen
 * <br>fixed order of country query
 * <br>
 * <br>Revision 1.7  2008-09-25 09:42:27  heinemann
 * <br>fixed copyrights
 * <br>
 * <br>Revision 1.6  2008-06-30 08:16:45  hermen
 * <br>added address autocompletion by locality
 * <br>
 * <br>Revision 1.5  2008-01-15 09:29:39  hermen
 * <br>updated Javadoc and refactured code
 * <br>
 * <br>Revision 1.4  2007-12-12 15:10:23  hermen
 * <br>updated Javadoc and refactured code
 * <br>
 *
 */
@Remote({AddressManagerInterface.class})
@Stateless
public class AddressManagerBean implements AddressManagerInterface 
{
    //~ Static fields/initializers =============================================

    private static final long serialVersionUID = 1L;
    
    /** the logger Object for this class */
	private static Logger logger = Logger.getLogger(AddressManagerBean.class.getName());
    
	
    @PersistenceContext (unitName="gecam")
	EntityManager em;
	
    //~ Methods ================================================================

    /* (non-Javadoc)
     * @see lu.tudor.santec.gecamed.address.ejb.session.interfaces.AddressManagerInterface#searchLocality(java.lang.String)
     */
    @SuppressWarnings("unchecked")
	@RolesAllowed("gecam")
    public Collection<Locality> searchLocality(String name) throws Exception
    {
    	Query q = em.createNamedQuery ("findAllLocalityByName");
    	q.setParameter("name",name + "%");
    	Collection l = q.getResultList();
		return l;
	}
    
    /* (non-Javadoc)
     * @see lu.tudor.santec.gecamed.address.ejb.session.interfaces.AddressManagerInterface#getZipByLocality(java.lang.Integer)
     */
    @SuppressWarnings("unchecked")
	@RolesAllowed("gecam")
    public Integer[] getZipByLocality(Integer localityId) throws Exception
    {
        Query q = em.createQuery(
        		"SELECT DISTINCT zip.zip " + 
        		"FROM Zip AS zip " + 
        		"WHERE zip.localityId=:localityId")
        	.setParameter("localityId", localityId);
        Collection l = q.getResultList();
		return (Integer[]) l.toArray(new Integer[0]);
	}
    
    /* (non-Javadoc)
     * @see lu.tudor.santec.gecamed.address.ejb.session.interfaces.AddressManagerInterface#getStreetByZip(java.lang.Integer)
     */
    @SuppressWarnings("unchecked")
	@RolesAllowed("gecam")
    public String[] getStreetByZip(Integer zip) throws Exception
    {
        Query q = em.createQuery(
        		"SELECT DISTINCT zip.street " + 
        		"FROM Zip AS zip WHERE zip.zip=:zip " +   
        		"ORDER BY zip.street")
        	.setParameter("zip", zip);
        Collection l = q.getResultList();
		return (String[]) l.toArray(new String[0]);
	}
    
    /* (non-Javadoc)
     * @see lu.tudor.santec.gecamed.address.ejb.session.interfaces.AddressManagerInterface#getCountries()
     */
    @SuppressWarnings("unchecked")
	@RolesAllowed("gecam")
    public String[] getCountries() throws Exception
    {
    	Query q = em.createQuery(
    			"SELECT DISTINCT country.name " + 
    			"FROM Country AS country " + 
    			"ORDER BY NAME");
        Collection l = q.getResultList();
		return (String[]) l.toArray(new String[0]);
	}
    
    /* (non-Javadoc)
     * @see lu.tudor.santec.gecamed.address.ejb.session.interfaces.AddressManagerInterface#getLocalities()
     */
    @SuppressWarnings("unchecked")
	@RolesAllowed("gecam")
    public Locality[] getLocalities() throws Exception
    {
    	Collection l = em.createNamedQuery ("findAllLocality").getResultList();
    	return (Locality[]) l.toArray(new Locality[0]);
	}
    
    /* (non-Javadoc)
     * @see lu.tudor.santec.gecamed.address.ejb.session.interfaces.AddressManagerInterface#getLocality(java.lang.Integer)
     */
	@RolesAllowed("gecam")
    public Locality getLocality(Integer id) throws Exception
    {
    	return em.find(Locality.class, id);
	}

    /* (non-Javadoc)
     * @see lu.tudor.santec.gecamed.address.ejb.session.interfaces.AddressManagerInterface#getTypes()
     */
    @SuppressWarnings("unchecked")
	@RolesAllowed("gecam")
    public String[] getTypes() throws Exception
    {
        Query q = em.createQuery(
        		"SELECT DISTINCT type.value " + 
        		"FROM AddressType AS type");
        Collection l = q.getResultList();
		return (String[]) l.toArray(new String[0]);
	}

	/* (non-Javadoc)
	 * @see lu.tudor.santec.gecamed.address.ejb.session.interfaces.AddressManagerInterface#getZips()
	 */
	@SuppressWarnings("unchecked")
	public Zip[] getZips() throws Exception {
		Collection l = em.createNamedQuery ("findAllZip").getResultList();
    	return (Zip[]) l.toArray(new Zip[0]);
	}

	/* (non-Javadoc)
	 * @see lu.tudor.santec.gecamed.address.ejb.session.interfaces.AddressManagerInterface#getZipByCode(java.lang.Integer)
	 */
	@SuppressWarnings("unchecked")
	public Zip[] getZipByCode(Integer zipcode) {
		Collection<Zip> zips = em.createNamedQuery ("findAllZipByZip").setParameter("zip", zipcode).getResultList();
		return zips.toArray(new Zip[0]);
		
	}


    /* (non-Javadoc)
     * @see lu.tudor.santec.gecamed.address.ejb.session.interfaces.AddressManagerInterface#getStreetByZip(java.lang.Integer)
     */
    @SuppressWarnings("unchecked")
	@RolesAllowed("gecam")
    public String[] getStreetLocalityID(Integer id) throws Exception
    {
        Query q = em.createQuery(
        		"SELECT DISTINCT zip.street " + 
        		"FROM Zip AS zip " + 
        		"WHERE zip.localityId=:localityId " + 
        		"ORDER BY zip.street")
        	.setParameter("localityId", id);
        Collection l = q.getResultList();
		return (String[]) l.toArray(new String[0]);
    }

	
	public Zip getZipByLocalityAndStreet (Locality currentLocality, String streetName, String streetNumber) throws Exception
	{
		Query q;
		
		if (streetName == null)
			streetName = "";
		
		if (streetNumber == null || streetNumber.trim().length() == 0)
		{
			// if the street number is not set, don'
			q = em.createQuery(
					"SELECT DISTINCT OBJECT(o) " + 
					"FROM Zip o " + 
					"WHERE o.localityId=:localityID " + 
					"AND o.street LIKE :street " + 
					"AND o.parity = 'N' " + 
					"AND o.first = 0 " +
					"AND o.last = 0 " + 
					"ORDER BY o.zip")
				.setParameter("localityID", currentLocality.getId())
				.setParameter("street", streetName);
			
			try
			{
				return (Zip) q.getSingleResult();
			}
			catch (NoResultException e)
			{
				/* No result for this query: 
				 * Can happen, no need to worry
				 */
//				System.out.println("no nr - no result");
				return null;
			}
			catch (NonUniqueResultException e)
			{
				/* Multiple results for this query: 
				 * Can happen, no need to worry.
				 * Return null, because the ZIP code returned only for unique results.
				 */
//				System.out.println("no nr - no unique result");
				return null;
			}
		}
		else
		{
			int		nr		= Integer.parseInt(streetNumber);
			String	parity	= (nr % 2) == 0 ? "E" : "O";
			
			
			q = em.createQuery(
					"SELECT DISTINCT OBJECT(o) " + 
					"FROM Zip o " + 
					"WHERE o.localityId=:localityID " + 
					"AND o.street LIKE :street " + 
					"AND (" + 
					"     o.parity = 'N' " +
					"  OR o.parity = :parity " +
					") " + 
					"AND ( " + 
					"     (o.first <= :nr AND o.last >= :nr) " + 
					"  OR (o.first = 0    AND o.last = 0) " + 
					") " + 
					"ORDER BY o.zip")
				.setParameter("localityID", currentLocality.getId())
				.setParameter("street", streetName)
				.setParameter("nr", nr)
				.setParameter("parity", parity);
			
			try
			{
				return (Zip) q.getSingleResult();
			}
			catch (NoResultException e)
			{
				/* No result for this query: 
				 * Can happen, no need to worry
				 */
//				System.out.println("nr set - no result");
				return null;
			}
			catch (NonUniqueResultException e)
			{
				/* Multiple results for this query: 
				 * This shouldn't happen -> log it
				 * Return null, because the ZIP code returned only for unique results.
				 */
//				System.out.println("nr set - no unique result");
				logger.warn("A query for the ZIP code with street number returned mutliple results. That shouldn't happen.", e);
				return null;
			}
		}
	}
    
    
    public String getPostalCodeFromCountry (String countryName)
    {
    	@SuppressWarnings("unchecked")
		List<Country> countries	= em.createNamedQuery("getCountryByName")
    			.setParameter("name", countryName)
    			.setMaxResults(1)
    			.getResultList();
    	
    	if (countries != null && countries.size() > 0)
    		 return countries.get(0).getPostalCode();
    	else return null;
    }
    
    
    public String getAlpha3CodeFromCountry (String countryName)
    {
    	try
    	{
	    	Country country	= (Country) em.createNamedQuery("getCountryByName")
	    			.setParameter("name", countryName)
	    			.setMaxResults(1)
	    			.getSingleResult();
	    	
	    	return country.getAlpha3Code();
    	}
    	catch (NoResultException e)
    	{
    		logger.warn("Country \""+countryName+"\" not found");
    		return null;
    	}
    }
}
