package br.com.caelum.vraptor4.interceptor;

import java.lang.annotation.Annotation;
import java.lang.reflect.AnnotatedElement;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;

import org.jboss.weld.resources.DefaultReflectionCache;

import br.com.caelum.vraptor4.AroundCall;

import com.google.common.base.Function;
import com.google.common.base.Predicate;
import com.google.common.base.Predicates;
import com.google.common.collect.Collections2;
import com.google.common.collect.Lists;
import com.thoughtworks.xstream.converters.reflection.PureJavaReflectionProvider;

import net.vidageek.mirror.dsl.Mirror;
import net.vidageek.mirror.list.dsl.Matcher;
import net.vidageek.mirror.list.dsl.MirrorList;
import net.vidageek.mirror.provider.AnnotatedElementReflectionProvider;
import net.vidageek.mirror.provider.ClassReflectionProvider;
import net.vidageek.mirror.provider.ConstructorBypassingReflectionProvider;
import net.vidageek.mirror.provider.ConstructorReflectionProvider;
import net.vidageek.mirror.provider.FieldReflectionProvider;
import net.vidageek.mirror.provider.GenericTypeAccessor;
import net.vidageek.mirror.provider.MethodReflectionProvider;
import net.vidageek.mirror.provider.ParameterizedElementReflectionProvider;
import net.vidageek.mirror.provider.ProxyReflectionProvider;
import net.vidageek.mirror.provider.ReflectionProvider;
import net.vidageek.mirror.provider.java.DefaultMirrorReflectionProvider;
import net.vidageek.mirror.provider.java.PureJavaClassReflectionProvider;
import net.vidageek.mirror.proxy.dsl.MethodInterceptor;

public class StepInvoker {
	
	private class InvokeMatcher implements Matcher<Method> {

		private Class<? extends Annotation> step;

		public InvokeMatcher(Class<? extends Annotation> step) {
			this.step = step;
		}

		@Override
		public boolean accepts(Method element) {
			if(element.getDeclaringClass().getName().contains("$")){
				return false;
			}
			return element.isAnnotationPresent(this.step);
		}

	}
	

	public Object tryToInvoke(Object interceptor,Class<? extends Annotation> step,SignatureAcceptor acceptor,Object... params) {
		Method stepMethod = findMethod(step, interceptor);
		if(stepMethod==null){
			return null;
		}

		
		if(!acceptor.accepts(stepMethod)){			
			throw new IllegalArgumentException(acceptor.errorMessage());
		}
		Object returnObject = createMirror().on(interceptor).invoke().method(stepMethod).withArgs(params);
		if(stepMethod.getReturnType().equals(void.class)){
			return new VoidReturn();
		}
		return returnObject;				
	}


	public Method findMethod(Class<? extends Annotation> step,Object interceptor) {		
		MirrorList<Method> possibleMethods = createMirror().on(interceptor.getClass()).reflectAll().methods().matching(new InvokeMatcher(step));
		if(possibleMethods.size() > 1){
			throw new IllegalStateException("You should not have more than one @"+step.getSimpleName()+" annotated method");
		}		
		if(possibleMethods.isEmpty()){
			return null;
		}
		Method stepMethod = possibleMethods.get(0);
		return stepMethod;
	}


	private Mirror createMirror() {
		return new Mirror();
	}
		
}

@@ -107,651 +107,14 @@
 ect.
-AnnotatedElement;%0Aimport java.lang.reflect.Constructor;%0Aimport java.lang.reflect.Field;%0Aimport java.lang.reflect.Method;%0Aimport java.util.ArrayList;%0Aimport java.util.Arrays;%0Aimport java.util.Collection;%0Aimport java.util.HashSet;%0Aimport java.util.List;%0A%0Aimport org.jboss.weld.resources.DefaultReflectionCache;%0A%0Aimport br.com.caelum.vraptor4.AroundCall;%0A%0Aimport com.google.common.base.Function;%0Aimport com.google.common.base.Predicate;%0Aimport com.google.common.base.Predicates;%0Aimport com.google.common.collect.Collections2;%0Aimport com.google.common.collect.Lists;%0Aimport com.thoughtworks.xstream.converters.reflection.PureJavaReflectionProvider
+Method
 ;%0A%0Ai
@@ -248,875 +248,162 @@
 st;%0A
+%0A
 import 
-net.vidageek.mirror.provider.AnnotatedElementReflectionProvider;%0Aimport net.vidageek.mirror.provider.ClassReflectionProvider;%0Aimport net.vidageek.mirror.provider.ConstructorBypassingReflectionProvider;%0Aimport net.vidageek.mirror.provider.ConstructorReflectionProvider;%0Aimport net.vidageek.mirror.provider.FieldReflectionProvider;%0Aimport net.vidageek.mirror.provider.GenericTypeAccessor;%0Aimport net.vidageek.mirror.provider.MethodReflectionProvider;%0Aimport net.vidageek.mirror.provider.ParameterizedElementReflectionProvider;%0Aimport net.vidageek.mirror.provider.ProxyReflectionProvider;%0Aimport net.vidageek.mirror.provider.ReflectionProvider;%0Aimport net.vidageek.mirror.provider.java.DefaultMirrorReflectionProvider;%0Aimport net.vidageek.mirror.provider.java.PureJavaClassReflectionProvider;%0Aimport net.vidageek.mirror.proxy.dsl.MethodInterceptor;%0A%0Apublic class 
+org.slf4j.Logger;%0Aimport org.slf4j.LoggerFactory;%0A%0Apublic class StepInvoker %7B%0A%09%0A%09private static final Logger logger = LoggerFactory%0A%09%09%09.getLogger(
 Step
@@ -401,34 +401,40 @@
 gger(StepInvoker
- %7B
+.class);
 %0A%09%0A%09private clas
@@ -724,16 +724,92 @@
 (%22$%22))%7B%0A
+%09%09%09%09logger.debug(%22Ignoring possible proxy %7B%7D%22,element.getDeclaringClass());%0A
 %09%09%09%09retu

debug message for class with $ in the name