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

import java.lang.reflect.Method;
import org.jboss.aop.Advisor;
import org.jboss.logging.Logger;

/**
 * @author <a href="mailto:kabir.khan@jboss.org">Kabir Khan</a>
 * @version $Revision: 1.2.2.3 $
 */
public class CallbackResolver
{
   private static final Logger log = Logger.getLogger(CallbackResolver.class);
   
   public static Callback resolveCallback(Class beanClass, Class annotation, Class listener) throws Exception
   {
      Callback callback = null;
      Method[] methods = beanClass.getDeclaredMethods();

      for (int i = 0; i < methods.length; i++)
      {
         if (methods[i].getAnnotation(annotation) != null)
         {
            if (callback == null)
            {
               callback = new BeanCallback(methods[i]);
               Class returnType = methods[i].getReturnType();
               Class[] args = methods[i].getParameterTypes();
               if (!annotation.equals(javax.ejb.Timeout.class) && (returnType != Void.TYPE || args.length != 0))
               {
                  throw new RuntimeException("Callback methods annotated on the bean class must return void and take no arguments: " + annotation.getName() + " - " + methods[i]);
               }
            }
            else
            {
               throw new RuntimeException("You can only annotate one callback method with "
                                          + annotation.getName() + " in bean class: " + beanClass.getName());
            }
         }
      }

      if (listener != null)
      {
         try
         {
            methods = listener.getDeclaredMethods();
            for (int i = 0; i < methods.length; i++)
            {
               if (methods[i].getAnnotation(annotation) != null)
               {
                  if (callback == null)
                  {
                     callback = new ListenerCallback(methods[i], listener.newInstance());
                     Class returnType = methods[i].getReturnType();
                     Class[] args = methods[i].getParameterTypes();
                     if (returnType != Void.TYPE || args.length != 1)
                     {
                        throw new RuntimeException("Callback methods annotated in a listener bean class must return void and take one argument: " + annotation.getName() + " - " + methods[i]);
                     }
                  }
                  else
                  {
                     throw new RuntimeException("You can only annotate one callback method with "
                                                + annotation.getName() + " in bean class: " + beanClass.getName() + " and callback listener: "
                                                + listener.getName());
                  }
               }
            }
         }
         catch (Exception e)
         {
            throw new RuntimeException(e.getCause());
         }
      }

      return callback;
   }

   public static Callback resolveCallback(Advisor beanAdvisor, Class beanClass, Class annotation, Class listener) throws Exception
   {

      if (listener != null)
         log.info("  !! listener " + listener.getName());
      Callback callback = null;
      Method[] methods = beanClass.getDeclaredMethods();

      for (int i = 0; i < methods.length; i++)
      {
         if ((methods[i].getAnnotation(annotation) != null) || (beanAdvisor.resolveAnnotation(methods[i], annotation) != null))
         {
            if (callback == null)
            {
               callback = new BeanCallback(methods[i]);
               Class returnType = methods[i].getReturnType();
               Class[] args = methods[i].getParameterTypes();
               if (!annotation.equals(javax.ejb.Timeout.class) && (returnType != Void.TYPE || args.length != 0))
               {
                  throw new RuntimeException("Callback methods annotated on the bean class must return void and take no arguments: " + annotation.getName() + " - " + methods[i]);
               }
            }
            else
            {
               throw new RuntimeException("You can only annotate one callback method with "
                                          + annotation.getName() + " in bean class: " + beanClass.getName());
            }
         }
      }

      if (listener != null)
      {
         try
         {
            methods = listener.getDeclaredMethods();
            for (int i = 0; i < methods.length; i++)
            {
               if (methods[i].getAnnotation(annotation) != null)
               {
                  if (callback == null)
                  {
                     callback = new ListenerCallback(methods[i], listener.newInstance());
                     Class returnType = methods[i].getReturnType();
                     Class[] args = methods[i].getParameterTypes();
                     if (returnType != Void.TYPE || args.length != 1)
                     {
                        throw new RuntimeException("Callback methods annotated in a listener bean class must return void and take one argument: " + annotation.getName() + " - " + methods[i]);
                     }
                  }
                  else
                  {
                     throw new RuntimeException("You can only annotate one callback method with "
                                                + annotation.getName() + " in bean class: " + beanClass.getName() + " and callback listener: "
                                                + listener.getName());
                  }
               }
            }
         }
         catch (Exception e)
         {
            throw new RuntimeException(e.getCause());
         }
      }
      
      try {
         if (callback == null && javax.ejb.TimedObject.class.isAssignableFrom(beanClass) && annotation.equals(javax.ejb.Timeout.class))
         {
            Class[] params = new Class[]{javax.ejb.Timer.class};
            Method method = beanClass.getMethod("ejbTimeout", params);
            callback = new BeanCallback(method);
         }
      } 
      catch (Exception e)
      {
         e.printStackTrace();
      }
      
      return callback;
   }
}
