/*
 * JBoss, Home of Professional Open Source
 *
 * Distributable under LGPL license.
 * See terms of license at gnu.org.
 */

package org.jboss.cache.pojo.interceptors;

import org.jboss.aop.joinpoint.Invocation;
import org.jboss.aop.joinpoint.MethodInvocation;
import org.jboss.cache.Fqn;
import org.jboss.cache.pojo.PojoCacheException;
import org.jboss.cache.pojo.impl.MethodDeclarations;
import org.jboss.cache.pojo.util.MethodCall;

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.List;

/**
 * Interceptor (done via aop advice) for transaction rollback. This is is attached to the
 * operation that needs a corresponding rollback, e.g., attachInterceptor.
 *
 * @author Ben Wang
 * @version $Id: PojoTxUndoInterceptor.java 3937 2007-05-30 06:08:03Z jgreene $
 */
public class PojoTxUndoInterceptor extends AbstractInterceptor
{
   /// Just that AOP requires an extra key.
   public static final String TAG = "PojoCache";

   public Object invoke(Invocation in) throws Throwable
   {
      if (!(in instanceof MethodInvocation))
      {
         throw new IllegalArgumentException("TxUndoInterceptor.invoke(): invocation not MethodInvocation");
      }
      MethodInvocation invocation = (MethodInvocation) in;

      PojoTxSynchronizationHandler handler =
            PojoTxUndoSynchronizationInterceptor.getSynchronizationHandler();

      if (handler == null)
      {
         return invocation.invokeNext();
// TODO handler is null can mean we are not calling the right interceptor stack. Need to revisit.
// E.g., a fresh getObject or find will trigger this.
//         throw new IllegalStateException("PojoTxUndoInterceptor.invoke(): PojoTxSynchronizationHandler is null");
      }

      // Add to the rollback list
      String methodName = invocation.getMethod().getName();
      // TODO Needs to handle Collection interceptor as well.
      if (methodName.equals(MethodDeclarations.attachInterceptor.getName()))
      {
         Method method = MethodDeclarations.undoAttachInterceptor;
         MethodCall mc = new MethodCall(method, invocation.getArguments(), invocation.getTargetObject());
         handler.addToList(mc);
      }
      else if (methodName.equals(MethodDeclarations.detachInterceptor.getName()))
      {
         Method method = MethodDeclarations.undoDetachInterceptor;
         MethodCall mc = new MethodCall(method, invocation.getArguments(), invocation.getTargetObject());
         handler.addToList(mc);
      }
      else if (methodName.equals(MethodDeclarations.inMemorySubstitution.getName()))
      {
         Method method = MethodDeclarations.undoInMemorySubstitution;
         Object obj = invocation.getArguments()[0];
         Field field = (Field) invocation.getArguments()[1];
         Object oldValue = field.get(obj);
         Object[] args = new Object[]{obj, field, oldValue};
         MethodCall mc = new MethodCall(method, args, invocation.getTargetObject());
         handler.addToList(mc);
      }
      else if (methodName.equals(MethodDeclarations.incrementReferenceCount.getName()))
      {
         Method method = MethodDeclarations.undoIncrementReferenceCount;
         Fqn fqn = (Fqn) invocation.getArguments()[0];
         int count = (Integer) invocation.getArguments()[1];
         List referenceList = (List) invocation.getArguments()[2];
         Object[] args = new Object[]{fqn, count, referenceList};
         MethodCall mc = new MethodCall(method, args, invocation.getTargetObject());
         handler.addToList(mc);
      }
      else if (methodName.equals(MethodDeclarations.decrementReferenceCount.getName()))
      {
         Method method = MethodDeclarations.undoDecrementReferenceCount;
         Fqn fqn = (Fqn) invocation.getArguments()[0];
         int count = (Integer) invocation.getArguments()[1];
         List referenceList = (List) invocation.getArguments()[2];
         Object[] args = new Object[]{fqn, count, referenceList};
         MethodCall mc = new MethodCall(method, args, invocation.getTargetObject());
         handler.addToList(mc);
      }
      else
      {
         throw new PojoCacheException("PojoTxUndoInterceptor: invalid invocation name: " + methodName);
      }

      return invocation.invokeNext();
   }

}
