/**
 * 
 */
package org.msh.tb.application;

import org.jboss.seam.Component;
import org.jboss.seam.annotations.Name;
import org.jboss.seam.annotations.intercept.BypassInterceptors;
import org.jboss.seam.transaction.UserTransaction;

import javax.persistence.EntityManager;

/**
 * Simple SEAM component (but may be used as an ordinary object) to manage 
 * database transaction life cycle, with methods to start, commit or roll back it.
 * 
 * Transactions can be nested. If a call to begin a transaction is called and there is an on-going transaction,
 * no new transaction is started but a counter is incremented. The counter is
 * decremented as the transaction is commited, so when its value is 0, 
 * the transaction is really commited. 
 * 
 * @author Ricardo Memoria
 *
 */
@Name(TransactionManager.COMP_NAME)
@BypassInterceptors
public class TransactionManager {
	protected static final String COMP_NAME = "transactionManager";
	
	private UserTransaction transaction;
	private EntityManager entityManager;

	/**
	 * Incremented when a call to {@link TransactionManager#begin()} is made and
	 * decremented when a call to {@link TransactionManager#commit()} is made.
	 * Just when the value is 0 that the commit and roll back transaction is really called,
	 * so transaction can be started and committed in a nest way 
	 */
	private int beginTxCounter;

	
	/**
	 * Return true if there is an on-going transaction
	 * @return boolean value
	 */
	public boolean isActive() {
		return beginTxCounter > 0;
	}

	/**
	 * Return the transaction in use by the task
	 * @return
	 */
	protected UserTransaction getTransaction() {
		if (transaction == null)
			transaction = (UserTransaction)Component.getInstance("org.jboss.seam.transaction.transaction");
		return transaction;
	}


	/**
	 * Return the {@link EntityManager} instance in use
	 * @return
	 */
	public EntityManager getEntityManager() {
		if (entityManager == null)
			entityManager = (EntityManager)Component.getInstance("entityManager");
		return entityManager;
	}


	/**
	 * Start a new transaction
	 */
	public void begin() {
		// transaction was already started ?
		if (beginTxCounter > 0)
			return;

		try {
			getTransaction().begin();
			getEntityManager().joinTransaction();

			// increment counter
			beginTxCounter++;
		} catch (Exception e) {
			e.printStackTrace();
			throw new RuntimeException(e);
		}
	}


	/**
	 * Commit a transaction that is under progress 
	 */
	public void commit() {
		if (beginTxCounter == 0)
			throw new RuntimeException("No transaction in progress");

		beginTxCounter--;
		
		// transaction was already started in some other part?
		if (beginTxCounter > 0)
			return;
		
		try {
			getTransaction().commit();
		} catch (Exception e) {
			e.printStackTrace();
			throw new RuntimeException(e);
		}
	}


	/**
	 * Roll back a transaction that is under progress 
	 */
	public void rollback() {
		// there is any on-going transaction ?
		if (beginTxCounter == 0)
			return;

		beginTxCounter = 0;

		try {
			getTransaction().rollback();
		} catch (Exception e) {
			e.printStackTrace();
			throw new RuntimeException(e);
		}
	}

	
	/**
	 * Return a reference to the component from the container
	 * @return instance of {@link TransactionManager} 
	 */
	public static TransactionManager instance() {
		return (TransactionManager)Component.getInstance(COMP_NAME);
	}
}
