package com.tandbergtv.watchpoint.pmm.dao.hibernate;

import org.apache.log4j.Logger;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.Transaction;

/**
 * Class that maintains contextual information required by the application to
 * make calls to Hibernate
 * 
 * @author Vijay Silva
 */
public final class HibernateContext {
	private static Logger log = Logger.getLogger(HibernateContext.class);

	private static HibernateContext context = new HibernateContext();

	private SessionFactory sessionFactory;

	private boolean active = false;

	/*
	 * Default Constructor
	 */
	private HibernateContext() {
	}

	/*
	 * Helper method to initialize the Context
	 */
	private void initialize() {
		if (this.active) {
			log
					.info("The Hibernate Context is already initialized, skipping initialization.");
			return;
		}

		log.debug("Initializing the Hibernate Context...");

		try {
			this.initializeSessionFactory();
		} catch (HibernateContextException hce) {
			throw hce;
		} catch (Exception ex) {
			String msg = "Failed to load the Hibernate Context";
			throw new HibernateContextException(msg, ex);
		}

		this.active = true;

		log.info("Successfully initialized the Hibernate Context.");
	}

	/*
	 * Method to initialize the Session Factory
	 */
	private void initializeSessionFactory() {
		log.debug("Creating the Session Factory to use...");

		this.sessionFactory = ApplicationContextHelper.getInstance()
				.getSessionFactory();

		log.debug("Successfully created the Session Factory.");
	}

	/**
	 * Method to get the current context, or re-initialize the context if it is
	 * not active.
	 * 
	 * @return The HibernateContext
	 */
	public static synchronized HibernateContext getContext() {
		if (context == null) {
			context = new HibernateContext();
		}

		return context;
	}

	/**
	 * Returns the SessionFactory.
	 * 
	 * @return SessionFactory
	 */
	public synchronized SessionFactory getSessionFactory() {
		if (!this.active) {
			String msg = "The Hibernate Context is not initialized, cannot get the SessionFactory.";
			throw new IllegalStateException(msg);
		}

		return this.sessionFactory;
	}

	/**
	 * Gets the thread context current session
	 * 
	 * @return the current thread's session
	 */
	public Session getCurrentSession() {
		return getSessionFactory().getCurrentSession();
	}

	/**
	 * Begins a transaction on the current session.
	 * 
	 * @see HibernateContext#getCurrentSession()
	 * @see org.hibernate.Session#beginTransaction()
	 */
	public void beginTransaction() {
		getCurrentSession().beginTransaction();
	}

	/**
	 * Commits the transaction on the current session.
	 * 
	 * @see HibernateContext#getCurrentSession()
	 * @see Transaction#commit()
	 */
	public void commitTransaction() {
		getCurrentSession().getTransaction().commit();
	}

	/**
	 * Rolls back the transaction on the current session.
	 * 
	 * @see HibernateContext#getCurrentSession()
	 * @see Transaction#rollback()
	 */
	public void rollbackTransaction() {
		getCurrentSession().getTransaction().rollback();
	}

	/**
	 * Gets a new session
	 * 
	 * @return a new session
	 */
	public Session openSession() {
		return getSessionFactory().openSession();
	}

	/**
	 * Method to initialize the Hibernate Context, if it has not been previously
	 * initialized.
	 * 
	 */
	public synchronized void initializeContext() {
		this.initialize();
	}

	/**
	 * Closes the Context ensuring that all sessions and ended.
	 */
	public synchronized void closeContext() {
		if (!this.active) {
			log.info("The Hibernate Context is already closed.");
			return;
		}

		log.debug("Closing the Hibernate Context...");

		try {
			if (sessionFactory != null)
				sessionFactory.close();
		} catch (Exception ex) {
			String msg = "Failed to close the Hibernate Context.";
			throw new HibernateContextException(msg, ex);
		} finally {
			this.sessionFactory = null;
			this.active = false;
		}

		log.info("Successfully closed the Hibernate Context.");
	}
}
