/*
 * Copyright 2012 Red Hat, Inc. and/or its affiliates.
 *
 * Licensed under the Eclipse Public License version 1.0, available at
 * http://www.eclipse.org/legal/epl-v10.html
 */
package org.jboss.forge.furnace.proxy;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.concurrent.Callable;

import org.jboss.forge.furnace.exception.ContainerException;
import org.jboss.forge.furnace.util.ClassLoaders;

/**
 * @author <a href="mailto:lincolnbaxter@gmail.com">Lincoln Baxter, III</a>
 */
public class ClassLoaderInterceptor implements ForgeProxy
{
   private static final ThreadLocal<ClassLoader> currentLoader = new ThreadLocal<>();

   private final ClassLoader loader;
   private final Object delegate;

   public ClassLoaderInterceptor(ClassLoader loader, Object delegate)
   {
      this.loader = loader;
      this.delegate = delegate;
   }

   @Override
   public Object invoke(final Object self, final Method thisMethod, final Method proceed, final Object[] args)
            throws Throwable
   {
      if (Thread.currentThread().isInterrupted())
      {
         throw new ContainerException("Thread.interrupt() requested.");
      }

      Callable<Object> task = new Callable<Object>()
      {
         @Override
         public Object call() throws Exception
         {
            try
            {
               if (thisMethod.getDeclaringClass().getName().equals(ForgeProxy.class.getName()))
               {
                  if (thisMethod.getName().equals("getDelegate"))
                     return ClassLoaderInterceptor.this.getDelegate();
                  if (thisMethod.getName().equals("getHandler"))
                     return ClassLoaderInterceptor.this.getHandler();
               }
            }
            catch (Exception e)
            {
            }

            ClassLoader previousLoader = null;
            Object result;
            try
            {
               previousLoader = setCurrentLoader(loader);

               for (int i = args.length - 1; i >= 0; i--)
               {
                  args[i] = Proxies.unwrap(args[i]);
               }

               result = thisMethod.invoke(delegate, args);
            }
            catch (InvocationTargetException e)
            {
               if (e.getCause() instanceof Exception)
                  throw (Exception) e.getCause();
               throw e;
            }
            finally
            {
               setCurrentLoader(previousLoader);
            }
            return result;
         }
      };

      Object result = ClassLoaders.executeIn(loader, task);

      if (Thread.currentThread().isInterrupted())
      {
         throw new ContainerException("Thread.interrupt() requested.");
      }

      return result;
   }

   public static ClassLoader getCurrentloader()
   {
      return currentLoader.get();
   }

   private static ClassLoader setCurrentLoader(ClassLoader loader)
   {
      ClassLoader previous = currentLoader.get();
      currentLoader.set(loader);
      return previous;
   }

   @Override
   public Object getDelegate() throws Exception
   {
      return delegate;
   }

   @Override
   public Object getHandler() throws Exception
   {
      return this;
   }

}
@@ -594,16 +594,334 @@
 Proxy%0A%7B%0A
+   private static final Method EQUALS_METHOD;%0A%0A   static%0A   %7B%0A      try%0A      %7B%0A         EQUALS_METHOD = Object.class.getMethod(%22equals%22, Object.class);%0A      %7D%0A      catch (NoSuchMethodException %7C SecurityException e)%0A      %7B%0A         throw new RuntimeException(%22Could not reflect Object.equals()%22, e);%0A      %7D%0A   %7D%0A%0A
    priva
@@ -2336,119 +2336,204 @@
     
-for (int i = args.length - 1; i %3E= 0; i--)%0A               %7B%0A                  args%5Bi%5D = Proxies.unwrap(args%5Bi%5D)
+if (thisMethod.equals(EQUALS_METHOD))%0A               %7B%0A                  Object object = args%5B0%5D;%0A                  Object unwrapped = Proxies.unwrap(object);%0A                  args%5B0%5D = unwrapped
 ;%0A  

Revert "FURNACE-129: Unwrapping method arguments before invoking"