/********************************************************************
 * Copyright © 2005 VHA. All rights reserved
 ********************************************************************/


package gov.va.med.fw.service.transaction;

import java.sql.Timestamp;
import java.util.Date;
import java.util.List;

import org.apache.commons.lang.Validate;

import gov.va.med.fw.cache.TransactionTimestampCacheManager;
import gov.va.med.fw.persistent.DAOException;
import gov.va.med.fw.persistent.DAOOperations;
import gov.va.med.fw.persistent.hibernate.CurrentTimestampAction;
import gov.va.med.fw.service.AbstractComponent;

/**
 * Manages the creation and storage of the "global Transaction timestamp".  The
 * "global Transaction timestamp" is meant to synchronize all database flushes in the same
 * Transaction such that they all use the same event date.  Typically this timestamp will
 * be created and cached when the Transaction is started by the TransactionSynchronizationInitializer
 * class.
 * 
 *  Timestamp retrieval is either from the database (if they CurrentTimestampAction is injected)
 *  or from the application server (no CurrentTimestampAction injection).
 * 
 * Created Nov 3, 2005 2:59:33 PM
 * @author DNS   BOHMEG
 * 
 * @see gov.va.med.fw.service.transaction.TransactionSynchronizationInitializer
 */
public class TransactionTimestampManager extends AbstractComponent {
	private TransactionTimestampCacheManager timestampCacheManager;
	/**
	 * An instance of currentTimestampAction
	 */
	private CurrentTimestampAction currentTimestampAction;
	
	/**
	 * An instance of daoName
	 */
	private String daoName;

	/**
	 * An instance of dao
	 */
	private static DAOOperations dao;
	
	public Timestamp intializeTransactionTimestamp(boolean forceOverride) {
		if(timestampCacheManager.hasTimestamp() && forceOverride)
			timestampCacheManager.removeTimestamp();
		
		Timestamp ts = createNewTransactionTimestamp();
		timestampCacheManager.storeTransactionTimestamp(ts);
		return ts;
	}

	/** If called in a scenario that does not have a Timestamp already created, one
	 * will be created but not stored.  Therefore, subsequent calls in that non-Transactional
	 * context will return a different timestamp every time.
	 * @return
	 */
	public Timestamp getTransactionTimestamp() {
		Timestamp ts = timestampCacheManager.getTransactionTimestamp();
		if(ts == null) {
			logger.debug("There is no Timestamp tied to the current (if any) Transaction. Creating new one");
			// create an unbound ts
			ts = createNewTransactionTimestamp();
		}
		return ts;
	}
	
	
	private Timestamp createNewTransactionTimestamp() {
		Timestamp ts = null;
		if (currentTimestampAction != null) {
			// execute this and store in ts
			try {
				List results = (List) getDao().execute(currentTimestampAction);
				ts = (Timestamp) results.get(0);
				if (logger.isDebugEnabled())
					logger.debug("###Initialized ts (from Database) at JVM time ["
							+ new Date() + "]: using database ts [" + ts + "]");

			}
			catch (DAOException e) {
				logger.error("Can not retrieve current timestamp from database", e);
				RuntimeException e2 = new IllegalStateException(
						"EntityInterceptor is in an illegal state and was unable to retrieve current timestamp from database");
				e2.initCause(e);
				throw e2;
			}
		}
		else {
			ts = new Timestamp(System.currentTimeMillis());
			if (logger.isDebugEnabled())
				logger.debug("###Initialized ts (from JVM) at JVM time ["
						+ new Date() + "]: using jvm ts [" + ts + "]");
		}
		return ts;
	}
	
	
	/**
	 * @return Returns the timestampCacheManager.
	 */
	public TransactionTimestampCacheManager getTimestampCacheManager() {
		return timestampCacheManager;
	}

	/**
	 * @param timestampCacheManager The timestampCacheManager to set.
	 */
	public void setTimestampCacheManager(
			TransactionTimestampCacheManager timestampCacheManager) {
		this.timestampCacheManager = timestampCacheManager;
	}

	/**
	 * @return Returns the currentTimestampAction.
	 */
	public CurrentTimestampAction getCurrentTimestampAction() {
		return currentTimestampAction;
	}

	/**
	 * @param currentTimestampAction The currentTimestampAction to set.
	 */
	public void setCurrentTimestampAction(
			CurrentTimestampAction currentTimestampAction) {
		this.currentTimestampAction = currentTimestampAction;
	}

	/**
	 * @return Returns the daoName.
	 */
	public String getDaoName() {
		return daoName;
	}

	/**
	 * @param daoName The daoName to set.
	 */
	public void setDaoName(String daoName) {
		this.daoName = daoName;
	}

	/**
	 * @return Returns the dao.
	 */
	public DAOOperations getDao() {
		if (dao == null) {
			dao = (DAOOperations) getApplicationContext().getBean(daoName,	DAOOperations.class);
		}
		return dao;
	}	

	public void afterPropertiesSet() {
		Validate.notNull(timestampCacheManager);
		if (currentTimestampAction != null) {
			Validate.notNull( daoName, "daoName property can not be null when getting current time from database");
		}
	}	
}
