package com.jware.base.web.struts;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import javax.servlet.ServletContext;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.struts.action.Action;
import org.apache.struts.action.ActionErrors;
import org.apache.struts.action.ActionForm;
import org.apache.struts.action.ActionForward;
import org.apache.struts.action.ActionMapping;
import org.apache.struts.action.ActionMessage;

import com.jware.base.error.JwareApplicationException;

/**
 * MOCHA汎用Action
 * 
 * @author $Id: PartsBaseAction.java,v 1.1 2010/01/13 11:52:01 clmg Exp $
 */
public abstract class PartsBaseAction extends Action implements IMochaConstants {

    /**
     * Commons Logging instance.
     */
    protected Log log = LogFactory.getLog(this.getClass().getName());

	private static final int MAX_HISTORY = 64;

	private static ServletContext context = null;

	private static boolean initialized = false;

	protected Class clazz_ = this.getClass();

	protected HashMap methods_ = new HashMap();

	protected Class[] types_ =
		{ ActionMapping.class, ActionForm.class, HttpServletRequest.class, HttpServletResponse.class, ActionErrors.class };

	/**
	 * Struts本家Actionより実行されるexecuteメソッド
	 * 
	 * @param   mapping   ActionMapping 
	 * @param   form      ActionForm
	 * @param   request   HttpServletRequest 
	 * @param   response  HttpServletResponse
	 * @throws  Exception
	 * @return  ActionForward
	 */
	public ActionForward execute(
		ActionMapping mapping,
		ActionForm form,
		HttpServletRequest request,
		HttpServletResponse response)
		throws Exception {


		ActionErrors validateErrors = getErrors( request );
		if( validateErrors == null ){
			validateErrors = new ActionErrors();
		}
		
		//ActionErrors
		ActionErrors errors = new ActionErrors();

		//遷移先
		ActionForward forward = null;

		HttpSession session = request.getSession();
		Object loginInfo = session.getAttribute(LOGIN_INFO);
		if (loginInfo == null) {
			ActionForward loginForward = mapping.findForward(FORWARD_LOGIN);
			if (loginForward != null) {
				log.info("Login Action Forward :=" + loginForward.getPath());
				return loginForward;
			} else {
				log.info("Login Action Forward not exsit......");
			}
		}

		//Callするメソッド名の取得
		String parameter = mapping.getParameter();

		String methodName = null;
		if (parameter != null) {
			methodName = request.getParameter(parameter);
		}

		try {
			if (parameter == null || methodName == null) {

				forward = doExecute(mapping, form, request, response, errors);

			} else {
				Method method = null;
				try {
					method = getMethod(methodName);
					Object[] args = { mapping, form, request, response, errors };
					forward = (ActionForward) method.invoke(this, args);
				} catch (NoSuchMethodException e) {
					log.error("method doesnt exist :" + methodName, e);
					return mapping.findForward(FORWARD_FAILURE);
				} catch (ClassCastException e) {
					log.error("return value is invalid:" + methodName, e);
					return mapping.findForward(FORWARD_FAILURE);
				} catch (IllegalAccessException e) {
					log.error("method doesnt exist :" + methodName, e);
					return mapping.findForward(FORWARD_FAILURE);
				} catch (InvocationTargetException e) {
					throw e.getTargetException();
				}
			}

		} catch (JwareApplicationException e) {
			if (log.isDebugEnabled()) {
				log.info(e);
			}

			forward = getForwardWithExceptions(mapping, form, request, response, e, errors);

		} catch (Throwable ex) {

			if (log.isDebugEnabled()) {
				log.info(ex);
			}

			ex.printStackTrace();

			forward = mapping.findForward(FORWARD_FAILURE);

		}

		if (isDisplayTagExporting(mapping, request, response)) {
			
			ActionForward exportForward = mapping.findForward(FORWARD_EXPORT);
			if (exportForward != null) {
				forward = exportForward;
			}

		}

		// post action using browser forwarding to convert to get action
		
		//Errors
		addErrors(request, errors);

		return forward;

	}

	
	/**
	 * 
	 * 
	 * @param   mapping  ActionMapping 
	 * @param   form     ActionForm
	 * @param  request   HttpServletRequest 
	 * @param  response HttpServletResponse
	 * @param	errors	ActionErrors
	 * @return  ActionForward
	 */
	abstract public ActionForward doExecute(
		ActionMapping mapping,
		ActionForm baseForm,
		HttpServletRequest request,
		HttpServletResponse response,
		ActionErrors errors)
		throws JwareApplicationException;

	/**
	 * 
	 * ExceptionをActionErrorに変換します。
	 * @param request
	 * @param mapping
	 * @param ex
	 * @return
	 */
	protected ActionForward getForwardWithExceptions(
			ActionMapping mapping,
			ActionForm form,
			HttpServletRequest request,
			HttpServletResponse response,
			JwareApplicationException ex,
			ActionErrors errors) {

		ActionForward forward = null;

		convertExceptionsToActionErrors(errors, ex);

		String inputForward = mapping.getInput();
		ActionForward errorForward = mapping.findForward(FORWARD_ERROR);

		if (inputForward != null) {

			if( forward == null ){
				forward = mapping.getInputForward();
			}
			
		} else {
			forward = errorForward;
		}

		//Exception		

		List exceptions = ex.getExceptions();

		if (exceptions != null && !exceptions.isEmpty()) {
			for (Iterator ite = exceptions.iterator(); ite.hasNext();) {
				JwareApplicationException childException = (JwareApplicationException) ite.next();
				convertExceptionsToActionErrors(errors, childException);

			}
		}

		return forward;

	}

	
	protected void convertExceptionsToActionErrors(ActionErrors errors, JwareApplicationException ex) {

		ActionMessage message = null;

		String errCode = ex.getMessageKey();

		Object[] args = ex.getMessageBundleValues();

		if (args != null && args.length > 0) {
			message = new ActionMessage("error.code." + errCode, args);
		} else {
			message = new ActionMessage("error.code." + errCode);
		}

		errors.add(ActionErrors.GLOBAL_MESSAGE, message);

	}

	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;
		}

	}

	/**
	 * ServletContext
	 * @return
	 */
	public static ServletContext getContext() {
		return context;
	}

	/**
	 * Struts本家Actionより実行されるexecuteメソッド
	 * 
	 * @param   mapping   ActionMapping 
	 * @param   form      ActionForm
	 * @param   request   HttpServletRequest 
	 * @param   response  HttpServletResponse
	 * @throws  Exception
	 * @return  ActionForward
	 */
	public boolean isDisplayTagExporting(ActionMapping mapping, HttpServletRequest request, HttpServletResponse response) {

		boolean exporting = false;

		Enumeration params = request.getParameterNames();
		while (params.hasMoreElements()) {
			String paramName = (String) params.nextElement();
			if (paramName.indexOf("d-") == 0 && paramName.indexOf("-e") > 0) {
				exporting = true;
				break;
			}
		}

		return exporting;
	}

	protected void setActionMessages(ActionErrors errors, String key ) {
		
		setActionMessages(errors, key, null);
		
	}
	
	protected void setActionMessages(ActionErrors errors, String key, Object[] args ) {

		ActionMessage message = null;

		if (args != null && args.length > 0) {
			message = new ActionMessage(key, args );
		} else {
			message = new ActionMessage( key );
		}

		if( message != null ){
			errors.add(ActionErrors.GLOBAL_MESSAGE, message);
		}

	}

	
}