package com.crackwillow.struts.action;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Enumeration;
import java.util.HashMap;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.struts.action.Action;
import org.apache.struts.action.ActionForm;
import org.apache.struts.action.ActionForward;
import org.apache.struts.action.ActionMapping;
import org.apache.struts.util.MessageResources;

public abstract class DispatchAction
    extends Action {
  protected           Class            clazz    = this.getClass();
  protected static    MessageResources messages = MessageResources.getMessageResources ("org.apache.struts.actions.LocalStrings");
  protected           HashMap          methods  = new HashMap();
  protected           Class[]          types    = { ActionMapping.class,
                                                    ActionForm.class,
                                                    HttpServletRequest.class,
                                                    HttpServletResponse.class };

  public ActionForward execute(ActionMapping mapping,
                               ActionForm form,
                               HttpServletRequest request,
                               HttpServletResponse response)
      throws Exception {
    String name = getMethodName(request,mapping);

    if ("execute".equals(name) || "perform".equals(name)){
      // Prevent recursive calls
      String message = messages.getMessage("dispatch.recursive", mapping.getPath());
      throw new ServletException(message);
    }

    return dispatchMethod(mapping, form, request, response, name);
  }

  protected ActionForward dispatchMethod(ActionMapping mapping,
                                         ActionForm form,
                                         HttpServletRequest request,
                                         HttpServletResponse response,
                                         String name)
      throws Exception {

    if (name == null) {
      return this.unspecified(mapping, form, request, response);
    }

    Method method = null;

    try {
      method = getMethod(name);
    } catch(NoSuchMethodException nsme) {
      String message = messages.getMessage("dispatch.method", mapping.getPath(), name);
      throw nsme;
    }

    ActionForward forward = null;

    try {
      Object args[] = {mapping, form, request, response};
      forward = (ActionForward) method.invoke(this, args);
    } catch(ClassCastException cce) {
      String message = messages.getMessage("dispatch.return", mapping.getPath(), name);
      throw cce;
    } catch(IllegalAccessException iae) {
      String message = messages.getMessage("dispatch.error", mapping.getPath(), name);
      throw iae;

    } catch(InvocationTargetException ite) {
      Throwable t = ite.getTargetException();

      if (t instanceof Exception) {
        throw ((Exception) t);
      } else {
        String message = messages.getMessage("dispatch.error", mapping.getPath(), name);
        throw new ServletException(t);
      }
    }
    return (forward);
  }

  protected static String getMethodName(HttpServletRequest request,  ActionMapping mapping) {
    String methodName  = null;
    String buttonValue = null;
    String paramProperty = mapping.getParameter();
    if((paramProperty != null)) {
      methodName = paramProperty.substring(0,paramProperty.indexOf('.'));
    } else {
      Enumeration enum = request.getParameterNames();
      while(enum.hasMoreElements()) {
        buttonValue = (String)enum.nextElement();
        if(buttonValue.indexOf(".dispatch") >= 0) {
          methodName = buttonValue;
          break;
        }
      }
    }
    return methodName.substring(0,methodName.indexOf('.'));
  }

  protected Method getMethod(String name)
      throws NoSuchMethodException {
    synchronized(methods) {
      Method method = (Method) methods.get(name);

      if (method == null) {
        method = clazz.getMethod(name, types);
        methods.put(name, method);
      }

      return (method);
    }
  }

  protected ActionForward unspecified(ActionMapping mapping,
                                      ActionForm form,
                                      HttpServletRequest request,
                                      HttpServletResponse response)
      throws Exception {
    String message = messages.getMessage( "dispatch.parameter", mapping.getPath(), getMethodName(request,mapping));

    throw new ServletException(message);
  }

  protected ActionForward cancelled(ActionMapping mapping,
                                    ActionForm form,
                                    HttpServletRequest request,
                                    HttpServletResponse response)
      throws Exception {

    return null;
  }
}