package org.qi4j.runtime.injection.provider;

import java.lang.annotation.Annotation;
import java.lang.reflect.AnnotatedElement;
import java.lang.reflect.Method;
import org.qi4j.runtime.composite.Resolution;
import org.qi4j.runtime.composite.CompositeMethodModel;
import org.qi4j.runtime.injection.DependencyModel;
import org.qi4j.runtime.injection.InjectionContext;
import org.qi4j.runtime.injection.InjectionProvider;
import org.qi4j.runtime.injection.InjectionProviderFactory;

/**
 * TODO
 */
public final class InvocationInjectionProviderFactory
    implements InjectionProviderFactory
{
    public InjectionProvider newInjectionProvider( Resolution resolution, DependencyModel dependencyModel ) throws InvalidInjectionException
    {
        Class injectionClass = dependencyModel.injectionClass();
        if( injectionClass.equals( Method.class ) ||
            injectionClass.equals( AnnotatedElement.class ) ||
            Annotation.class.isAssignableFrom( injectionClass ) )
        {
            return new InvocationDependencyResolution( resolution, dependencyModel );
        }
        else
        {
            throw new InvalidInjectionException( "Invalid injection type " + injectionClass + " in " + dependencyModel.injectedClass().getName() );
        }
    }

    private class InvocationDependencyResolution implements InjectionProvider
    {
        private final Resolution resolution;
        private final DependencyModel dependencyModel;

        public InvocationDependencyResolution( Resolution resolution, DependencyModel dependencyModel )
        {
            this.resolution = resolution;
            this.dependencyModel = dependencyModel;
        }

        public Object provideInjection( InjectionContext context ) throws InjectionProviderException
        {
            Class injectedClass = dependencyModel.injectedClass();
            final CompositeMethodModel methodModel = resolution.method();
            if( injectedClass.equals( Method.class ) )
            {
                // This needs to be updated to handle Apply and annotation aggregation correctly
                return methodModel.method();
            }

            final AnnotatedElement method = methodModel.annotatedElement();
            if( injectedClass.equals( AnnotatedElement.class ) )
            {
                return method;
            }
            final Annotation annotation = method.getAnnotation( injectedClass );
            if ( annotation !=null)
            {
                return annotation;
            }
            if (dependencyModel.injectionType() instanceof Class<?>) {
                return method.getAnnotation( (Class<Annotation>) dependencyModel.injectionType() );
            }
            return null;
        }
    }
}
@@ -155,16 +155,47 @@
 Method;%0A
+import java.lang.reflect.Type;%0A
 import o
@@ -220,26 +220,36 @@
 mposite.
-Resolution
+CompositeMethodModel
 ;%0Aimport
@@ -276,36 +276,26 @@
 mposite.
-CompositeMethodModel
+Resolution
 ;%0Aimport
@@ -1824,32 +1824,38 @@
   %7B%0A            
+final 
 Class injectedCl
@@ -1885,32 +1885,104 @@
 njectedClass();%0A
+            final Type injectionType = dependencyModel.injectionType();%0A
             fina
@@ -2031,32 +2031,33 @@
 ution.method();%0A
+%0A
             if( 
@@ -2054,39 +2054,39 @@
       if( inject
-edClass
+ionType
 .equals( Method.
@@ -2458,32 +2458,33 @@
 ;%0A            %7D%0A
+%0A
             fina
@@ -2562,17 +2562,16 @@
       if
- 
 ( annota
@@ -2577,20 +2577,22 @@
 ation !=
+ 
 null
+ 
 )%0A      
@@ -2640,32 +2640,33 @@
 ;%0A            %7D%0A
+%0A
             if (
@@ -2667,26 +2667,10 @@
   if
+(
  
-(dependencyModel.
 inje
@@ -2678,18 +2678,16 @@
 tionType
-()
  instanc
@@ -2698,17 +2698,30 @@
 Class%3C?%3E
-)
+ )%0A           
  %7B%0A     
@@ -2780,32 +2780,16 @@
 ation%3E) 
-dependencyModel.
 injectio
@@ -2793,18 +2793,16 @@
 tionType
-()
  );%0A    

Handle injection of "@Invocation Method"