package eu.dnetlib.r2d2.accesstime;

import java.util.Date;

import javax.annotation.Resource;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.annotation.Required;
import org.springframework.stereotype.Component;

import eu.dnetlib.miscutils.cache.Cache;
import eu.dnetlib.r2d2.neo4j.Neo4jBean;
import eu.dnetlib.r2d2.neo4j.dao.AccessTimeDao;
import eu.dnetlib.r2d2.neo4j.dao.IdBroker;
import eu.dnetlib.r2d2.neo4j.domain.AccessTime;

public class Toucher {
	private static final Log log = LogFactory.getLog(Toucher.class); // NOPMD by marko on 11/24/08 5:02 PM

	@Resource
	AsyncAccessToucher asyncAccess;

	@Resource
	AsyncModificationToucher asyncModification;

	@Resource
	private AccessTimeDao accessTimeDao;

	@Resource
	private IdBroker idBroker;

	private Cache<String, AccessTime> cache;

	public void touch(final Neo4jBean bean) {
		touch(bean.getId());
	}

	public void touch(final String beanId) {
		log.debug("Touching: " + beanId);

		final AccessTime access = getAccessTime(beanId, true);
		asyncAccess.enqueue(beanId, access);
	}

	public void modified(final Neo4jBean bean) {
		if (bean != null)
			modified(bean.getId());
		else
			log.warn("touching modification stamp for null bean");
	}

	/**
	 * We can assume that this method is called within a RW transaction so we don't neet to enqueue it to the async.
	 * 
	 * @param beanId
	 */
	public void modified(String beanId) {
		AccessTime cachedAccess = getAccessTime(beanId, true);
		AccessTime access = accessTimeDao.getBean(cachedAccess.getId());
		if (access != null) {

			access.setLastModificationDate(System.currentTimeMillis());

			cache.put(beanId, access);
			accessTimeDao.saveBean(access);

		} else {
			log.info("Didn't store modification time because access bean is not yet created (delayed creation)");
		}
	}

	public AccessTime ensureAccesTime(final Neo4jBean bean) {
		return getAccessTime(bean.getId());
	}

	/**
	 * Ensure that the access
	 * 
	 * @param beanId
	 * @return
	 */

	public AccessTime getAccessTime(final String beanId) {
		return getAccessTime(beanId, false);
	}

	public AccessTime getAccessTime(final String beanId, final boolean touch) {
		final AccessTime access = getAccessTimeFromWriteThroughCache(beanId);

		if (touch) {
			access.setLastAccessDate(System.currentTimeMillis());
			cache.put(beanId, access);
		}

		return access;
	}

	public AccessTime getAccessTimeFromWriteThroughCache(final String beanId) {
		if (cache.containsKey(beanId)) {
			final AccessTime time = cache.get(beanId);
			if (log.isDebugEnabled())
				log.debug("Returning a cached accesstime (write-through) entry for " + beanId + " since the entry might not be flushed on db. " + time
						+ " Access: " + new Date(time.getLastAccessDate()) + " Modified " + new Date(time.getLastModificationDate()));
			return time;
		}

		AccessTime access = getAccessTimeFromStorage(beanId);
		cache.put(beanId, access);
		return access;
	}

	public AccessTime getAccessTimeFromStorage(final String beanId) {
		AccessTime access;
		try {
			access = accessTimeDao.getAccessTimeForBean(beanId);
			if (access != null) {
				log.debug("access time for " + beanId + " was found in the db");
				return access;
			}
		} catch (final java.util.NoSuchElementException e) {
			log.warn("got exception while getting access time for bean: " + beanId);
		}

		log.debug("access time for " + beanId + " was not found in the db, creating it");

		access = accessTimeDao.newBean();
		access.setId(idBroker.generateId());
		access.setLastAccessDate(System.currentTimeMillis());
		access.setLastModificationDate(System.currentTimeMillis());
		access.setCreationDate(System.currentTimeMillis());

		cache.put(beanId, access);
//		asyncModification.enqueueNewAccess(beanId, access);

		return access;
	}

	public Cache<String, AccessTime> getCache() {
		return cache;
	}

	@Required
	public void setCache(final Cache<String, AccessTime> cache) {
		this.cache = cache;
	}

}
