/*
 * JBoss, the OpenSource J2EE webOS
 *
 * Distributable under LGPL license.
 * See terms of license at gnu.org.
 */
package org.jboss.ejb3.injection;

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;
import javax.annotation.EJB;
import javax.annotation.EJBs;
import javax.naming.Context;
import javax.naming.LinkRef;
import org.jboss.aop.Advisor;
import org.jboss.ejb3.Container;
import org.jboss.logging.Logger;
import org.jboss.ejb3.EjbLinkResolver;
import org.jboss.naming.Util;

/**
 * Searches bean class for all @Inject and create Injectors
 *
 * @author <a href="mailto:bill@jboss.org">Bill Burke</a>
 * @version $Revision: 1.2.2.5 $
 */
public class EJBHandler
{
   private static final Logger log = Logger.getLogger(EJBHandler.class);
   
   public static List loadInjectors(Container container) throws Exception
   {
      Class clazz = container.getBeanClass();
      ArrayList list = new ArrayList();
      Context ctx = Util.createSubcontext(container.getInitialContext(), Container.ENC_CTX_NAME + "/env/ejb");
      loadClassLevelEnc(clazz, container, ctx);
      loadMethodInjectors(clazz, container, list, ctx, container.getInitialContext());
      loadFieldInjectors(clazz, container, list, ctx, container.getInitialContext());
      return list;
   }

   public static String getJndiName(EJB ref, EjbLinkResolver resolver, Class memberType)
   {
      if (ref.beanName().equals("") && memberType == null) throw new RuntimeException("not enough information for @EJB.  Please fill out the beanName and businessInterface attributes");

      Class businessInterface = memberType;
      if (!ref.businessInterface().getName().equals(Object.class.getName())) businessInterface = ref.businessInterface();

      if (ref.beanName().equals(""))
      {
         return businessInterface.getName();
      }

      return resolver.getEjbJndiName(ref.beanName(), businessInterface);
   }

   private static void loadClassLevelEnc(Class clazz, Container container, Context ctx) throws Exception
   {
      EJBs ref = (EJBs) ((Advisor) container).resolveAnnotation(EJBs.class);
      if (ref != null)
      {
         EJB[] ejbs = ref.value();
      
         for (int i = 0; i < ejbs.length; i++)
         {
            String jndiName = getJndiName(ejbs[i], container.getEjbLinkResolver(), null);

            String encName = ejbs[i].name();
            if (encName == null || encName.equals(""))
            {
               throw new RuntimeException("JBoss requires the name of the @EJB in the @EJBs: " + clazz);
            }
 
            Util.bind(ctx,
                      encName,
                      new LinkRef(jndiName));
         }
      }
   }

   private static void loadMethodInjectors(Class clazz, Container container, ArrayList list, Context ctx, Context initialContext) throws Exception
   {
      Method[] methods = clazz.getMethods();
      for (int i = 0; i < methods.length; i++)
      {
         EJB ref = (EJB) ((Advisor) container).resolveAnnotation(methods[i], EJB.class);
         if (ref != null)
         {
            if (!methods[i].getName().startsWith("set")) throw new RuntimeException("@EJB can only be used with a set method: " + methods[i]);
            String jndiName = getJndiName(ref, container.getEjbLinkResolver(), methods[i].getParameterTypes()[0]);
            String encName = ref.name();
            if (encName == null || encName.equals(""))
            {
               encName = methods[i].getName().substring(3);
            }
            Util.bind(ctx,
                      encName,
                      new LinkRef(jndiName));
            list.add(new JndiMethodInjector(methods[i], Container.ENC_CTX_NAME + "/env/ejb" + "/" + encName, initialContext));
         }
      }
   }

   private static void loadFieldInjectors(Class clazz, Container container, ArrayList list, Context ctx, Context initialContext) throws Exception
   {
      if (clazz == null || clazz.equals(Object.class)) return;
      loadFieldInjectors(clazz.getSuperclass(), container, list, ctx, initialContext);
      Field[] fields = clazz.getDeclaredFields();
      for (int i = 0; i < fields.length; i++)
      {
         EJB ref = (EJB) ((Advisor) container).resolveAnnotation(fields[i], EJB.class);
         if (ref != null)
         {
            String jndiName = getJndiName(ref, container.getEjbLinkResolver(), fields[i].getType());
            String encName = ref.name();
            if (encName == null || encName.equals(""))
            {
               encName = fields[i].getName();
            }
            Util.bind(ctx,
                      encName,
                      new LinkRef(jndiName));

            list.add(new JndiFieldInjector(fields[i], Container.ENC_CTX_NAME + "/env/ejb" + "/" + encName, initialContext));
         }
      }
   }
}
