/*
 * GenericDAOJPA.java
 *
 * Created on 2 de Dezembro de 2006, 03:50
 *
 * To change this template, choose Tools | Template Manager
 * and open the template in the editor.
 */
package com.tecgraf.dao;

import java.util.List;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.persistence.EntityManager;
import javax.persistence.EntityTransaction;
import javax.persistence.Query;

/**
 * Class GenericDAOJPA is an implementation of a class that
 * only allows one instantiation of a subclass. This version
 * requires its subclasses to provide an implementation of
 * a static getInstance() method.
 *
 * @param <ID> 
 * @param <T>
 * @author ismael
 */
public abstract class GenericDAOJPA<T extends IEntityDAOJPA<ID>, ID extends Number>
        implements IGenericDAOJPA<T, ID>
{
    //--------------------------------------------------
    // Query methods
    //--------------------------------------------------
    public List<T> queryPositionalParams(
            String queryString,
            final Object... positionalParams
            )
    {
        EntityManager em = facadeJPA.getEntityManagerFactory().createEntityManager();

        Query query = em.createQuery( queryString );
    
        int i = 0;
        for( Object p : positionalParams )
        {
            query.setParameter(++i, p);
        }
        
        @SuppressWarnings("unchecked")
        List<T> l = query.getResultList();
        
        em.close();
        
        return l;
    }

    public List<T> queryNamedParams(
            String queryString,
            final Map<String, Object> namedParams
            )
    {
        EntityManager em = facadeJPA.getEntityManagerFactory().createEntityManager();

        Query query = em.createQuery( queryString );
        
        for( Map.Entry<String, Object> entry : namedParams.entrySet() )
        {
            query.setParameter( entry.getKey(), entry.getValue() );
        }
        
        @SuppressWarnings("unchecked")
        List<T> l = query.getResultList();

        em.close();
        
        return l;
    }
    
    //--------------------------------------------------
    // CRUD methods
    //--------------------------------------------------
    public T findById(Class<T> c, ID primaryKey)
    {
        Logger.getLogger(getEntityClassName()).
                         log(Level.FINE, "lookdig for object id=" + primaryKey + " of class: " + c.getName());
        
        EntityManager em = facadeJPA.getEntityManagerFactory().createEntityManager();

        T entity = em.find( c, primaryKey );

        em.close();
        
        return entity;
    }

    public List<T> findAll(Class<T> c)
    {
        Logger.getLogger(getEntityClassName()).
                         log(Level.FINE, "finding all objects of class: " + c.getName());
        
        entityName = c.getName().substring( c.getName().lastIndexOf('.') + 1 );
        
        return queryPositionalParams( "SELECT e FROM " + getEntityName() + " e" );
    }

    public T makePersistent(T entity) throws JPADAOException
    {
        Logger.getLogger(getEntityClassName()).
                         log(Level.FINE, "persisting object: " + entity);
        
        EntityManager em = facadeJPA.getEntityManagerFactory().createEntityManager();                
        EntityTransaction tx = em.getTransaction();
        
        try
        {
            tx.begin();
            em.persist( entity );
            tx.commit();
        }
        catch(Exception ex)
        {
            if ( null != tx && tx.isActive() )
            {
                tx.rollback();
            }
            
            em.close();
            
            Logger.getLogger(getEntityClassName()).log( Level.SEVERE, null, ex );
            
            throw new JPADAOException( "error while persisting object " + entity, ex );
        }
        
        em.close();
        
        return entity;
    }

    public boolean deletePersistent(Class<T> c, T entity)
    {
        EntityManager em = facadeJPA.getEntityManagerFactory().createEntityManager();
        
        /* brings entity to the PersistenceContext */
        T e = em.find( c, entity.getId() );
        
        EntityTransaction tx = null;
        boolean objDeleted = false;
        
        if( e != null )
        {
            Logger.getLogger(getEntityClassName()).
                             log(Level.FINE, "deleting object: " + e);
            
            tx = em.getTransaction();
            tx.begin();

            try
            {
                em.remove(e);                
                tx.commit();
                objDeleted = true;
            }
            catch(RuntimeException ex)
            {
                if( tx != null && tx.isActive() )
                {
                    tx.rollback();                  
                }
                
                Logger.getLogger(getEntityClassName()).log(Level.SEVERE, null, ex);                
                objDeleted = false;                
            }
            finally
            {
                em.close();
            }
        }
        else
        {
            em.close();
        }
        
        return objDeleted;   
    }

    public boolean updatePersistent(T entity)
    {   
        @SuppressWarnings("unchecked")
        Class<T> c = (Class<T>) entity.getClass();
        
        if ( null == findById(c, entity.getId() ) )
        {
            return ( false );
        }
        
        Logger.getLogger(getEntityClassName()).
                         log(Level.FINE, "updating object: " + entity);
        
        EntityManager em = facadeJPA.getEntityManagerFactory().createEntityManager();
        EntityTransaction tx = em.getTransaction();
        
        try
        {
            tx.begin();
            /* brings entity to the PersistenceContext */
            em.merge( entity );
            em.flush();
            tx.commit();
        }
        catch(Exception ex)
        {
            if ( null != tx && tx.isActive() )
            {
                tx.rollback();
            }

            Logger.getLogger(getEntityClassName()).log(Level.SEVERE, null, ex);
            
            em.close();
            
            return false;
        }
        
        em.close();
        
        return true;
    }

    public String getEntityClassName()
    {
        return entityClassName;
    }

    public String getEntityName()
    {
        return entityName;
    }
    
    //--------------------------------------------------
    // Protected constructor
    //--------------------------------------------------
    protected GenericDAOJPA(Class<T> c, FacadeJPA f)
    {
        this.facadeJPA = f;
        entityClassName = c.getName();
        entityName = entityClassName.substring(entityClassName.lastIndexOf('.') + 1);
    }

    private String entityClassName;
    private String entityName;
    protected FacadeJPA facadeJPA;
    
}