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

import java.util.ArrayList;
import java.util.Arrays;
import javax.ejb.Local;
import javax.ejb.Remote;
import javax.naming.Context;
import javax.naming.NameNotFoundException;
import javax.naming.NamingException;
import org.jboss.annotation.ejb.LocalBinding;
import org.jboss.annotation.ejb.RemoteBinding;
import org.jboss.annotation.ejb.RemoteBindings;
import org.jboss.aop.Advisor;
import org.jboss.ejb.LocalImpl;
import org.jboss.ejb.RemoteImpl;
import org.jboss.logging.Logger;

/**
 * Comment
 *
 * @author <a href="mailto:bill@jboss.org">Bill Burke</a>
 * @version $Revision: 1.2.2.4 $
 */
public class ProxyFactoryHelper
{
   private static final Logger log = Logger.getLogger(ProxyFactoryHelper.class);

   public static Context getProxyFactoryContext(Context ctx)
           throws NamingException
   {

      try
      {
         return (Context) ctx.lookup("proxyFactory");
      }
      catch (NameNotFoundException e)
      {
         return ctx.createSubcontext("proxyFactory");
      }
   }

   public static Class[] getLocalInterfaces(Container container)
   {
      Class beanClass = container.getBeanClass();
      Local li = (javax.ejb.Local) ((EJBContainer) container).resolveAnnotation(javax.ejb.Local.class);
      Class[] ri = getRemoteInterfaces(container);
      if (li == null && ri == null && (beanClass.getInterfaces() == null || beanClass.getInterfaces().length == 0))
         throw new RuntimeException("bean class has no local or remote interfaces defined and does not implement at least one business interface");
      if (li == null)
      {
         Class[] intfs = beanClass.getInterfaces();
         ArrayList<Class> locals = new ArrayList<Class>();
         for (Class clazz : intfs)
         {
            if (clazz.isAnnotationPresent(javax.ejb.Local.class))
            {
               locals.add(clazz);
            }
         }
         if (locals.size() > 0)
         {
            intfs = locals.toArray(new Class[locals.size()]);
            li = new LocalImpl(intfs);
            ((Advisor) container).getAnnotations().addClassAnnotation(javax.ejb.Local.class, li);
            return li.value();
         }
      }
      if (li == null)
      {
         if (ri == null)
         {
            ArrayList interfaces = new ArrayList(Arrays.asList(beanClass.getInterfaces()));
            interfaces.remove(java.io.Serializable.class);
            interfaces.remove(java.io.Externalizable.class);
            interfaces.remove(javax.ejb.SessionSynchronization.class);
            interfaces.remove(javax.ejb.TimedObject.class);
            if (interfaces.size() == 0)
               throw new RuntimeException("bean class has no local or remote interfaces defined and does not implement at least one business interface");
            if (interfaces.size() > 1)
               throw new RuntimeException("deployer cannot determine default local interface as there are too many implemented interfaces and no @Remote or @Local defined");
            Class[] rtn = {(Class) interfaces.get(0)};
            li = new LocalImpl(rtn);
            ((EJBContainer) container).getAnnotations().addClassAnnotation(javax.ejb.Local.class, li);
            return rtn;
         }
         else
         {
            return null;
         }
      }
      return li.value();
   }

   public static String getJndiName(Container container, Class businessInterface)
   {
      Advisor advisor = (Advisor) container;
      Class[] remotes = getRemoteInterfaces(container);
      if (remotes != null)
      {
         for (Class clazz : remotes)
         {
            if (clazz.getName().equals(businessInterface.getName()))
            {
               RemoteBindings bindings = (RemoteBindings) advisor.resolveAnnotation(RemoteBindings.class);
               if (bindings == null)
               {
                  RemoteBinding binding = (RemoteBinding) advisor.resolveAnnotation(RemoteBinding.class);
                  if (binding == null)
                     throw new RuntimeException("RemoteBindings should not be null: " + container.getEjbName());

                  return getRemoteJndiName(container, binding);
               }
               return getRemoteJndiName(container, bindings.value()[0]);
            }
         }
      }
      
      return getLocalJndiName(container);
   }

   public static String getLocalJndiName(Container container)
   {
      Advisor advisor = (Advisor) container;
      LocalBinding localBinding = (LocalBinding) advisor
              .resolveAnnotation(LocalBinding.class);
      if (localBinding == null)
      {
         Class[] interfaces = getLocalInterfaces(container);
         if (interfaces.length == 1)
         {
            return interfaces[0].getName();
         } else
         {
            return container.getEjbName();
         }
      }
      else
      {
         return localBinding.jndiBinding();
      }
   }

   public static Class[] getRemoteInterfaces(Container container)
   {
      Remote ri = (Remote) ((Advisor) container).resolveAnnotation(Remote.class);
      if (ri == null)
      {
         Class beanClass = container.getBeanClass();
         Class[] intfs = beanClass.getInterfaces();
         ArrayList<Class> remotes = new ArrayList<Class>();
         for (Class clazz : intfs)
         {
            if (clazz.isAnnotationPresent(Remote.class))
            {
               remotes.add(clazz);
            }
         }
         if (remotes.size() > 0)
         {
            intfs = remotes.toArray(new Class[remotes.size()]);
            ri = new RemoteImpl(intfs);
            ((Advisor) container).getAnnotations().addClassAnnotation(Remote.class, ri);
            return ri.value();
         }

         return null;
      }
      return ri.value();
   }

   public static String getRemoteJndiName(Container container)
   {
      Advisor advisor = (Advisor) container;
      RemoteBinding binding = (RemoteBinding) advisor
              .resolveAnnotation(RemoteBinding.class);

      return getRemoteJndiName(container, binding);
   }

   public static String getRemoteJndiName(Container container, RemoteBinding binding)
   {
      String jndiName = null;
      if (binding == null || binding.jndiBinding() == null || binding.jndiBinding().equals(""))
      {
         jndiName = getDefaultRemoteJndiName(container);
      }
      else
      {
         jndiName = binding.jndiBinding();
      }
       
      return jndiName;
   }

   public static String getDefaultRemoteJndiName(Container container)
   {
      String jndiName = null;
      
      Class[] interfaces = getRemoteInterfaces(container);
      if (interfaces.length == 1)
      {
         jndiName = interfaces[0].getName();
      }
      else
      {
         jndiName = container.getEjbName();
      }
      
      return jndiName;
   }
}
