package org.jbehave.core.configuration.pico;

import java.util.ArrayList;
import java.util.List;

import org.jbehave.core.annotations.pico.UsingPico;
import org.jbehave.core.configuration.AnnotationBuilder;
import org.jbehave.core.configuration.AnnotationFinder;
import org.jbehave.core.configuration.AnnotationMonitor;
import org.jbehave.core.configuration.Configuration;
import org.jbehave.core.configuration.AnnotationRequired;
import org.jbehave.core.steps.CandidateSteps;
import org.jbehave.core.steps.InjectableStepsFactory;
import org.jbehave.core.steps.ParameterConverters;
import org.jbehave.core.steps.ParameterConverters.ParameterConverter;
import org.jbehave.core.steps.pico.PicoStepsFactory;
import org.picocontainer.MutablePicoContainer;
import org.picocontainer.PicoContainer;
import org.picocontainer.injectors.AbstractInjector.AmbiguousComponentResolutionException;

/**
 * Extends {@link AnnotationBuilder} to provide PicoContainer-based 
 * dependency injection if {@link UsingPico} annotation is present.
 * 
 * @author Cristiano Gavião
 * @author Mauro Talevi
 */
public class PicoAnnotationBuilder extends AnnotationBuilder {

    private PicoContainer container;

    public PicoAnnotationBuilder(Class<?> annotatedClass) {
        super(annotatedClass);
    }

    public PicoAnnotationBuilder(Class<?> annotatedClass, AnnotationMonitor annotationMonitor) {
        super(annotatedClass, annotationMonitor);
    }

    @SuppressWarnings({ "unchecked" })
    public Configuration buildConfiguration() throws AnnotationRequired {
        AnnotationFinder finder = annotationFinder();
        if (finder.isAnnotationPresent(UsingPico.class)) {
            List<Class> moduleClasses = finder.getAnnotatedValues(UsingPico.class, Class.class, "modules");
            List<PicoModule> modules = new ArrayList<PicoModule>();
            for (Class<PicoModule> moduleClass : moduleClasses) {
                try {
                    modules.add(moduleClass.newInstance());
                } catch (Exception e) {
                    annotationMonitor().elementCreationFailed(moduleClass, e);
                }
            }
            if ( modules.size() > 0 ){
                container = picoContainerFor(modules);                
            }
        } else {
            annotationMonitor().annotationNotFound(UsingPico.class, annotatedClass());
        }
        return super.buildConfiguration();
    }

    @SuppressWarnings("unchecked")
    private PicoContainer picoContainerFor(List<PicoModule> modules) {
        MutablePicoContainer container = instanceOf(MutablePicoContainer.class, annotationFinder().getAnnotatedValue(UsingPico.class, Class.class, "container"));
        for (PicoModule module : modules) {
            module.configure(container);
        }
        return container;
    }

    @Override
    public List<CandidateSteps> buildCandidateSteps(Configuration configuration) {
        List<CandidateSteps> steps = super.buildCandidateSteps(configuration);
        if (container != null) {
             InjectableStepsFactory factory = new PicoStepsFactory(configuration, container);
             steps.addAll(0, factory.createCandidateSteps());
        }
        return steps;
    }

    @Override
    protected ParameterConverters parameterConverters(AnnotationFinder annotationFinder) {
        ParameterConverters converters = super.parameterConverters(annotationFinder);
        if (container != null) {
            return converters.addConverters(container.getComponents(ParameterConverter.class));
        }
        return converters;
    }

    @Override
    protected <T, V extends T> T instanceOf(final Class<T> type, final Class<V> ofClass) {
        if (container != null) {
            T instance = null;            
            try {
                instance = container.getComponent(type);
            } catch (AmbiguousComponentResolutionException e) {
                instance = container.getComponent(ofClass);
            }
            if ( instance != null ){
                return instance;
            }
        }
        return super.instanceOf(type, ofClass);
    }

}

@@ -2200,17 +2200,23 @@
 ainer = 
-p
+createP
 icoConta
@@ -2219,19 +2219,16 @@
 ontainer
-For
 (modules
@@ -2366,32 +2366,32 @@
 s());%0A        %7D%0A
+
         return s
@@ -2428,404 +2428,8 @@
  %7D%0A%0A
-    @SuppressWarnings(%22unchecked%22)%0A    private PicoContainer picoContainerFor(List%3CPicoModule%3E modules) %7B%0A        MutablePicoContainer container = instanceOf(MutablePicoContainer.class, annotationFinder().getAnnotatedValue(UsingPico.class, Class.class, %22container%22));%0A        for (PicoModule module : modules) %7B%0A            module.configure(container);%0A        %7D%0A        return container;%0A    %7D%0A%0A
     
@@ -3735,10 +3735,459 @@
 %0A    %7D%0A%0A
+    @SuppressWarnings(%22unchecked%22)%0A    protected PicoContainer createPicoContainer(List%3CPicoModule%3E modules) %7B%0A        Class containerClass = annotationFinder().getAnnotatedValue(UsingPico.class, Class.class, %22container%22);%0A        MutablePicoContainer container = instanceOf(MutablePicoContainer.class, containerClass);%0A        for (PicoModule module : modules) %7B%0A            module.configure(container);%0A        %7D%0A        return container;%0A    %7D%0A%0A%0A
 %7D%0A

Allow override of container creation method for Pico, in line with Guice and Spring.