package org.jboss.resteasy.cdi;

import java.lang.reflect.Type;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;

import javax.enterprise.context.spi.CreationalContext;
import javax.enterprise.inject.spi.Bean;
import javax.enterprise.inject.spi.BeanManager;
import javax.ws.rs.WebApplicationException;

import org.jboss.resteasy.logging.Logger;
import org.jboss.resteasy.spi.ApplicationException;
import org.jboss.resteasy.spi.ConstructorInjector;
import org.jboss.resteasy.spi.Failure;
import org.jboss.resteasy.spi.HttpRequest;
import org.jboss.resteasy.spi.HttpResponse;

/**
 * This ConstructorInjector implementation uses CDI's BeanManager to obtain
 * a contextual instance of a bean.
 * 
 * @author Jozef Hartinger
 *
 */
public class CdiConstructorInjector implements ConstructorInjector
{
   private BeanManager manager;
   private Type type;
   private static final Logger log = Logger.getLogger(CdiConstructorInjector.class);

   public CdiConstructorInjector(Type type, BeanManager manager)
   {
      this.type = type;
      this.manager = manager;
   }

   public Object construct()
   {
      Set<Bean<?>> beans = manager.getBeans(type);
      
      if (beans.size() > 1)
      {
         Set<Bean<?>> modifiableBeans = new HashSet<Bean<?>>();
         modifiableBeans.addAll(beans);
         // Ambiguous dependency may occur if a resource has subclasses
         // Therefore we remove those beans
         for (Iterator<Bean<?>> iterator = modifiableBeans.iterator(); iterator.hasNext();)
         {
            Bean<?> bean = iterator.next();
            if (!bean.getBeanClass().equals(type) && !bean.isAlternative())
            {
               // remove Beans that have clazz in their type closure but not as a base class
               iterator.remove(); 
            }
         }
         beans = modifiableBeans;
      }
      
      log.debug("Beans found for {0} : {1}", type, beans);
      
      Bean<?> bean = manager.resolve(beans);
      CreationalContext<?> context = manager.createCreationalContext(bean);
      return manager.getReference(bean, type, context);
   }

   public Object construct(HttpRequest request, HttpResponse response) throws Failure, WebApplicationException, ApplicationException
   {
      return construct();
   }

   public Object[] injectableArguments()
   {
      return new Object[0];
   }

   public Object[] injectableArguments(HttpRequest request, HttpResponse response) throws Failure
   {
      return injectableArguments();
   }
}
