package com.fp.hbm.helper;

import java.io.Serializable;
import java.sql.Timestamp;

import org.hibernate.HibernateException;
import org.hibernate.Session;
import org.hibernate.Transaction;

import com.fp.common.exception.CommonException;
import com.fp.common.helper.BeanManager;
import com.fp.common.logger.APPLogger;
import com.fp.persistence.commondb.exception.CommondbException;
import com.fp.persistence.commondb.helper.FormatDates;
import com.fp.dto.hb.HibernateBean;
import com.fp.dto.hb.History;

 /**
 * Helper que almacena Threads de session, transaccion, y FinancialSession utilizadas en el proceso de transacciones financieras.
 */
public class PersistenceHelperHQL {
	/**
	 * Almacena una session de Hibernate.
	 */
	private static final ThreadLocal<Session> threadSession =		new ThreadLocal<Session>();
	/**
	 * Almacena una transaccion de Hibernate.
	 */
	private static final ThreadLocal<Transaction> threadTransaction =new ThreadLocal<Transaction>();

	/**
   * Retorna una session de hibernate.
   * @return  s
	 * @throws CommonException
   * @throws Exception
   */
	public static Session getSession() throws CommondbException {
		Session s =  threadSession.get();
		if (s == null) {
			throw new CommondbException("COMMONDB-0004","SESION NO ASOCIADA A UNA TRANSACCION");
		}
		return s;
	}
  public static void closeSession(){
  	try{
  		getSession().close();
  	}catch (Exception e) {
			APPLogger.getLogger().error(e);
		}
  }
	/**
   * Fija una session de hibernate.
   * @param session
   * @throws Exception
   */
	public static void setSession(Session session) {
		threadSession.set(session);
	}

  /**
   * Inicia el proceso de una transaccion de hibernate..
 * @throws CommonException
 * @throws HibernateException
   * @throws Exception
   */
	public static void beginTransaction() throws HibernateException, CommondbException {
		Transaction tx = threadTransaction.get();
		if (tx == null) {
			tx = getSession().beginTransaction();
			threadTransaction.set(tx);
		}

	}

	/**
   * Graba los dml que estan en la transaccion.
	 * @throws CommonException
   * @throws Exception
   */
	public static void commitTransaction() throws CommondbException {
		Transaction tx = threadTransaction.get();
		try {
			if ( tx != null && !tx.wasCommitted() && !tx.wasRolledBack() ){
				tx.commit();
			}else{
				throw new CommondbException("COMMONDB-0005","TRANSACCION YA APLICADA");
			}
		}finally{
			threadTransaction.set(null);
		}
	}
  /**
   * Graba los dml que estan en la transaccion.
   * @throws CommondbException
   */
  public static void flushTransaction() throws CommondbException {
      Session s = getSession();
      s.flush();
  }

  /**
   * rollback de los dml que estan dentro de la transaccion.
   * @throws CommonException
   */
	public static void rollbackTransaction() throws CommondbException {
		Transaction tx = threadTransaction.get();
		try {
			if ( tx != null && !tx.wasCommitted() && !tx.wasRolledBack() ){
				tx.rollback();
			}else{
				throw new CommondbException("COMMONDB-0005","TRANSACCION YA APLICADA");
			}
		}finally{
			threadTransaction.set(null);
		}
	}

	/**
	 * Retorna verdadero si a la transaccion se dio un commit;
	 * @return		boolean
	 * @throws Exception
	 */
	public static boolean isCommitted() throws Exception {
		Transaction tx = threadTransaction.get();
		if ( tx != null && !tx.wasCommitted() ){
			return false;
		}
		return true;
	}

	/**
	 * Retorna verdadero si a la transaccion se dio un commit;
	 * @return		boolean
	 * @throws Exception
	 */
	public static boolean isRolledBack() throws Exception {
		Transaction tx = threadTransaction.get();
		if ( tx != null && !tx.wasRolledBack() ){
			return false;
		}
		return true;
	}

	/**
	 * Metodo que se encarga de grabar un registro en la base de datos.
	 * @param pBean	Bean a grabar en la base de datos.
	 * @throws CommondbException
	 */
	public static void save(HibernateBean pBean) throws CommondbException {
		Session s=PersistenceHelperHQL.getSession();
		s.save(pBean.getClass().getName(),pBean);
	}

	/**
	 * Inserta un registro en la base de datos.
	 * @param pClassName  Nombre de la clase que maneja la persistencia de una tabla.
	 * @param pBean       Bean a insertar.
	 * @throws CommonException
	 * @throws Exception
	 */
	public static void save(String pClassName,HibernateBean pBean) throws CommondbException {
		Session s=PersistenceHelperHQL.getSession();
		s.save(pClassName,pBean);
	}

	/**
	 * Actualiza un registro en la base de datos.
	 * @param pBean   Bean a actualiar.
	 * @throws CommonException
	 * @throws Exception
	 */
	public static void update(HibernateBean pBean) throws CommondbException {
		Session s=PersistenceHelperHQL.getSession();
		s.update(pBean.getClass().getName(),pBean);
	}

	/**
	 * Actualiza un registro en la base de datos.
	 * @param pClassName  Nombre de la clase que maneja la persistencia de una tabla.
	 * @param pBean       Bean a actualiar.
	 * @throws CommonException
	 * @throws Exception
	 */
	public static void update(String pClassName,HibernateBean pBean) throws CommondbException {
		Session s=PersistenceHelperHQL.getSession();
		s.update(pClassName,pBean);
	}

	/**
	 * Actualiza o inserta  un registro en la base de datos.
	 * @param pBean   Bean a actualiar o insertar.
	 * @throws CommonException
	 * @throws Exception
	 */
	public static void saveOrUpdate(HibernateBean pBean) throws CommondbException,Exception {
		Session s=PersistenceHelperHQL.getSession();
		HibernateBean bean = manageHistory(pBean);
		s.saveOrUpdate(pBean.getClass().getName(),bean);
	}
  
	public static void persist(HibernateBean pBean) throws CommondbException {
		Session s=PersistenceHelperHQL.getSession();
		s.persist(pBean);
	}

	/**
	 * Actualiza o inserta  un registro en la base de datos.
	 * @param pClassName    Nombre de la clase que maneja la persistencia de una tabla.
	 * @param pBean         Bean a actualiar o insertar.
	 * @throws CommonException
	 * @throws Exception
	 */
	public static void saveOrUpdate(String pClassName,HibernateBean pBean) throws CommondbException,Exception {
		Session s=PersistenceHelperHQL.getSession();
		HibernateBean bean = manageHistory(pBean);
		s.saveOrUpdate(pClassName,bean);
	}

	/**
	 * Caduca registro persistente.
	 * @param pBean   Bean a caducar.
	 * @throws CommonException
	 * @throws Exception
	 */
	public static void delete(HibernateBean pBean) throws CommondbException {
		Session s=PersistenceHelperHQL.getSession();
		s.delete(pBean.getClass().getName(),pBean);
	}

	@SuppressWarnings("unchecked")
	public static HibernateBean get(Class pClass,Serializable pKey) throws CommondbException{
		Session s=PersistenceHelperHQL.getSession();
		return (HibernateBean)s.get(pClass, pKey);
	}
	
	/**
	 * Metodo que se encarga del manejo de la historia de registros en la base, si estos manejan date from date to. 
	 * @param pBean  Bena a almacenar en la base de datos.
	 * @return HibernateBean
	 * @throws Exception
	 */
    private static HibernateBean manageHistory(HibernateBean pBean) throws Exception {
        Session s = PersistenceHelperHQL.getSession();
        Object id = BeanManager.getBeanAttributeValue(pBean, "pk");
        HibernateBean oldbean = null;
        HibernateBean newbean = pBean;
        if ((id instanceof History) ) {
        	History pk = (History)id;
        	if(pk.getDateto() != null){
        		s.evict(pBean);
        		newbean = (HibernateBean)pBean.cloneMe();
        		oldbean = (HibernateBean) s.get(pBean.getClass(), (Serializable) id);
        		s.evict(oldbean);
        		id = BeanManager.getBeanAttributeValue(newbean, "pk");
        		pk = (History)id;
            	if(pk.getDateto().compareTo(FormatDates.getDefaultExpiryTimestamp()) < 0){
            		return pBean;
            	}
        	}    
            Timestamp t = FormatDates.getInstance().getDataBaseTimestamp();            
            if (oldbean != null && !BeanManager.compareObject(pBean, oldbean) ) {
            	expireRecord(oldbean,t);
            }
            Object tbean = BeanManager.getBeanAttributeValue(newbean, "datefrom");
            //si el valor anterior de fdesde es null le asigan un valor.
            if(tbean == null){
            	BeanManager.setBeanAttributeValue(newbean, "datefrom", t);	
            }            
            BeanManager.setBeanAttributeValue(pk, "dateto", FormatDates.getDefaultExpiryTimestamp());
            setpartition(pk, pk.getDateto());
        }
        return newbean;
    }
	
    /**
     * Metodo que expira un registro en la base de datos.
     * @param pOldBean Objeto que contiene el registro de una tabla a expirar. 
     * @param pTimestamp Fecha con la cual se expira un registro. 
     * @throws Exception
     */
    private static void expireRecord(HibernateBean pOldBean,Timestamp pTimestamp) throws Exception {
    	Session s = PersistenceHelperHQL.getSession();
    	History pk = (History)BeanManager.getBeanAttributeValue(pOldBean, "pk");
        pk.setDateto(pTimestamp);
        setpartition(pk, pTimestamp);
        s.save(pOldBean.getClass().getName(),pOldBean);    	
    }

    /**
     * Metodo fija la particion en un registro, la particion es yyyymm del campo dateto. 
     * @param pHistory  Pk de un registro.
     * @param pTimestamp fecha con la cual se caduca un registro. 
     * @throws Exception
     */
    private static void setpartition(History pHistory,Timestamp pTimestamp) throws Exception {
        try {
            BeanManager.setBeanAttributeValue(pHistory, "partition", FormatDates.getPartition(pTimestamp));
        } catch (Exception e1) {
        }    	
    }
}

