/*
 * 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.EntityExistsException;
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 ) {
    Logger.getLogger(getEntityClassName()).
            log(Level.FINE, "Query String =" + queryString +
            " positionalParams: " + positionalParams);

    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 ) {
    Logger.getLogger(getEntityClassName()).
            log(Level.FINE, "Query String =" + queryString +
            " namedParams: " + namedParams.entrySet());

    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 ) {
    em = facadeJPA.getEntityManagerFactory().createEntityManager();
    T entity = null;
    
    try {
      Logger.getLogger(getEntityClassName()).
              log(Level.FINE,
              "lookdig for object id=" + primaryKey + " of class: " + c.getName());
      entity = em.find(c, primaryKey);
    } catch( RuntimeException ex ) {
      Logger.getLogger(getEntityClassName()).log(Level.SEVERE, null, ex);
    } finally {
      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 ORDER by id");
  }

  public T makePersistent( T entity ) throws GenericDAOJPAException {
    em = facadeJPA.getEntityManagerFactory().createEntityManager();
    EntityTransaction tx = null;

    try {
      tx = em.getTransaction();
      tx.begin();
      Logger.getLogger(getEntityClassName()).
              log(Level.FINE, "persisting object: " + entity);

      em.persist(entity);
      tx.commit();
      em.refresh(entity); // re-read the state (after the execution of any possible trigger )
      
    } catch( EntityExistsException ex ) {
      if( tx != null && tx.isActive() ) {
        tx.rollback();
      }
      String msgErr="entity "+ entity + " already exist !";
      Logger.getLogger(getEntityClassName()).log(Level.SEVERE, msgErr, ex);
      throw new GenericDAOJPAException(msgErr, ex);
      
    } catch( RuntimeException ex ) {
      if( tx != null && tx.isActive() ) {
        tx.rollback();
      }
      Logger.getLogger(getEntityClassName()).log(Level.SEVERE, null, ex);
      throw new GenericDAOJPAException(ex);
      
    } finally {
      em.close();
    }
    
    return entity;
  }
  public T updatePersistent( Class<T> c, T entity ) throws GenericDAOJPAException {
    em = facadeJPA.getEntityManagerFactory().createEntityManager();
    EntityTransaction tx = null;
    T updatedEntity = null;
    
    try {
      tx = em.getTransaction();
      tx.begin();
      Logger.getLogger(getEntityClassName()).
              log(Level.FINE, "updating object: " + entity);

      updatedEntity = em.merge(entity);
      tx.commit();
      em.refresh(updatedEntity); // re-read the state (after the execution of any possible trigger )
      
    } catch( RuntimeException ex ) {
      if( tx != null && tx.isActive() ) {
        tx.rollback();
      }
      Logger.getLogger(getEntityClassName()).log(Level.SEVERE, null, ex);
      throw new GenericDAOJPAException(ex);
      
    } finally {
      em.close();      
    }
    
    return updatedEntity;
  }

public boolean deletePersistent( Class<T> c, T entity ) throws GenericDAOJPAException {
    em = facadeJPA.getEntityManagerFactory().createEntityManager();    
    EntityTransaction tx = null;
    boolean objDeleted = false;
    
    // brings entity back to the PersistenceContext 
    T e = em.find(c, entity.getId()); 
    
    if( e != null ) {
      try {
        tx = em.getTransaction();
        tx.begin();
        Logger.getLogger(getEntityClassName()).
                log(Level.FINE, "deleting object: " + e);
        
        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);
        throw new GenericDAOJPAException(ex);

      } finally {
        em.close();
      }
    } else {
      em.close();
      
      String msgErr="entity "+ entity + " was not found in database !";
      Logger.getLogger(getEntityClassName()).log(Level.SEVERE, msgErr);
      throw new GenericDAOJPAException(msgErr);
    }
    
    return objDeleted;
  }


  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;
  protected EntityManager em;
}
