package eu.dnetlib.r2d2.neo4j.util;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

import org.apache.log4j.Logger;
import org.neo4j.graphdb.GraphDatabaseService;
import org.neo4j.graphdb.Transaction;

import eu.dnetlib.r2d2.neo4j.BeanDao;
import eu.dnetlib.r2d2.neo4j.Neo4jBean;

public class TransactionHandlingProxy<B extends Neo4jBean> implements java.lang.reflect.InvocationHandler {
	private static Logger logger = Logger.getLogger(DebugProxy.class);

	private BeanDao<B> obj = null;
	private GraphDatabaseService graphDb = null;

	public static <B extends Neo4jBean> Object newInstance(BeanDao<B> obj, GraphDatabaseService graphDb) {
		logger.debug("Returning new proxy for object " + obj);
		
		return java.lang.reflect.Proxy.newProxyInstance(obj.getClass()
				.getClassLoader(), obj.getClass().getInterfaces(),
				new TransactionHandlingProxy<B>(obj, graphDb));
	}

	private TransactionHandlingProxy(BeanDao<B> obj, GraphDatabaseService graphDb) {
		this.obj = obj;
		this.graphDb = graphDb;
	}

	public Object invoke(Object proxy, Method m, Object[] args)
			throws Throwable {
		Object result = null;
		Transaction tx = null;
		
		try {
			logger.debug("Begining transaction");
			tx = graphDb.beginTx();
			
			result = m.invoke(obj, args);
		} catch (InvocationTargetException e) {
			logger.error("Error invoking target method", e);
			tx.failure();
			throw e.getCause();
		} catch (Exception e) {
			logger.error("Unexpected invocation exception", e);
			tx.failure();
			throw new RuntimeException("Unexpected invocation exception", e);
		} finally {
			logger.debug("Finishing transaction");
			tx.success();
			tx.finish();
		}
		
		return result;
	}
}