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.LinkedList;
import java.util.List;
import java.util.Map;

import javax.servlet.ServletContext;
import javax.servlet.http.Cookie;
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: BaseAction.java,v 1.1 2010/01/13 11:52:02 clmg Exp $
 */
public abstract class BaseAction 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;
			}

		} else {

			setHistoryBackUrl(mapping, form, request, response, forward);

		}

		// post action using browser forwarding to convert to get action
		ActionForward postTOGetForward = mapping.findForward(FORWARD_POST_TO_GET);
		if( "POST".equalsIgnoreCase(request.getMethod()) ){
			if( postTOGetForward != null 
				&&  errors.isEmpty() 
				&&  validateErrors.isEmpty() ){
			    if( forward.getPath().indexOf( ".do" ) > 0 ) {
			        String forwardPath = forward.getPath();
			        while( forwardPath.indexOf("/") >= 0 ) {
			            forwardPath = forwardPath.substring(forwardPath.indexOf("/")+1);
			        } 
		            request.setAttribute( FORWARD_POST_TO_GET, forwardPath );
		            
			        forward = postTOGetForward; 
			        
			    }
			    
			} else if( !validateErrors.isEmpty() && form == null &&
					( forward.getPath().indexOf( ".do" ) > 0 ) ){
				
				ActionForward prevForward = getPrevForward(mapping, form, request, response, 1);
				if( prevForward != null ){
					forward = prevForward;
				}
			}
		}
		
		//Errors
		addErrors(request, errors);

		return forward;

	}

	protected void setHistoryBackUrl(
		ActionMapping mapping,
		ActionForm baseForm,
		HttpServletRequest request,
		HttpServletResponse response,
		ActionForward forward) {

		HttpSession session = request.getSession();

		LinkedList historyList = (LinkedList) session.getAttribute(ACCESS_HISTORY);
		if (historyList == null) {
			historyList = new LinkedList();
		}

		AccessHistory accessHistory = new AccessHistory();

		String uri = request.getRequestURI();
		if (uri != null) {
			accessHistory.setUri(uri);
			if (log.isDebugEnabled()) {
				log.debug("Action URI = " + uri);
			}
		} else {
			accessHistory.setUri(null);
			if (log.isDebugEnabled()) {
				log.debug("Action URI = NULL");
			}
		}

		Cookie[] cookies = request.getCookies();
		if (cookies != null) {
			for (int i = 0; i < cookies.length; i++) {
				Cookie cookie = cookies[i];
				if ("PREVURI".equalsIgnoreCase(cookie.getName())) {
					accessHistory.setPrevUri(cookie.getValue());
					if (log.isDebugEnabled()) {
						log.debug("Action PREVURI = " + accessHistory.getPrevUri());
					}
				}
			}
		}
		Enumeration parameterNames = request.getParameterNames();
		Map parameterMap = new HashMap();
		List parameterNameList = new LinkedList();
		while (parameterNames.hasMoreElements()) {
			String parameterName = (String) parameterNames.nextElement();
			parameterNameList.add(parameterName);
			if (log.isDebugEnabled()) {
				log.debug("parameterName = " + parameterName);
			}
			String[] parameterValues = request.getParameterValues(parameterName);
			parameterMap.put(parameterName, parameterValues);
			if (log.isDebugEnabled()) {
				for (int j = 0; j < parameterValues.length; j++) {
					log.debug("parameterValues[" + j + "] = " + parameterValues[j]);
				}
			}
		}
		accessHistory.setParameterNames(parameterNameList);
		accessHistory.setParameterMap(parameterMap);

		accessHistory.setForm(baseForm);
		accessHistory.setForward(forward);

		Enumeration requestNames = request.getAttributeNames();
		List requestNameList = new LinkedList();
		Map requestMap = new HashMap();
		while (requestNames.hasMoreElements()) {
			String requestName = (String) requestNames.nextElement();
			requestNameList.add(requestName);
			requestMap.put(requestName, request.getAttribute(requestName));
		}
		accessHistory.setRequestNames(requestNameList);
		accessHistory.setRequestMap(requestMap);

		Enumeration sessionNames = session.getAttributeNames();
		List sessionNameList = new LinkedList();
		Map sessionMap = new HashMap();
		while (sessionNames.hasMoreElements()) {
			String sessionName = (String) sessionNames.nextElement();
			if (!ACCESS_HISTORY.equals(sessionName)) {
				sessionNameList.add(sessionName);
				sessionMap.put(sessionName, session.getAttribute(sessionName));
			}
		}
		accessHistory.setSessionNames(sessionNameList);
		accessHistory.setSessionMap(sessionMap);

		int insertPoint = 0;
		while (insertPoint < historyList.size()) {
			AccessHistory tempHistory = (AccessHistory) historyList.get(insertPoint);

			if (accessHistory.getPrevUri() != null
				&& accessHistory.getPrevUri().indexOf(tempHistory.getForward().getPath()) >= 0) {
				break;
			}
			insertPoint++;
		}
		if (insertPoint >= historyList.size()) {
			insertPoint = 0;
		}
		if (accessHistory.getForward() != null) {
			historyList.add(insertPoint, accessHistory);
		}

		while (historyList.size() > MAX_HISTORY) {
			historyList.remove(MAX_HISTORY);
		}

		session.setAttribute(ACCESS_HISTORY, historyList);

	}
	
	/**
	 * 
	 * 
	 * @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) {

			forward = mapping.getInputForward();
			if( forward == null ){
				forward = getPrevForward(mapping, form, request, response, 0);
			}
			
		} 
		if( forward == null ) {
			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 ActionForward getPrevForward(ActionMapping mapping, 
			ActionForm form,
			HttpServletRequest request,
			HttpServletResponse response, 
			int skipNumber)
	{
		
		//遷移�?
		ActionForward forward = null;

		HttpSession session = request.getSession(); 
		LinkedList historyList = (LinkedList)session.getAttribute(ACCESS_HISTORY);
		AccessHistory accessHistory = null;
		if( log.isDebugEnabled() ){
			log.debug("history List Size = " + historyList.size());
		}
		
		if( historyList != null && historyList.size() > 1 ){
			
			if( historyList.size() > skipNumber+1 ){

				int currentPoint = skipNumber;
				accessHistory = (AccessHistory)historyList.get(currentPoint);
				while( skipNumber > 0 ){
					historyList.remove( 0 );
					skipNumber--;
				}
			} 
			
			if( log.isDebugEnabled() ){
				log.debug("accessHistory URL = " + accessHistory.getUri());
			}

			forward = accessHistory.getForward();

			if( log.isDebugEnabled() ){
				log.debug("history set forward path = " + forward.getPath() );
			}
			
			form = accessHistory.getForm();

			List requestNameList = accessHistory.getRequestNames();
			Map requestMap = accessHistory.getRequestMap();
			for( int i=0; i<requestNameList.size(); i++ ){
				String requestName = (String)requestNameList.get(i);
				request.setAttribute(requestName, requestMap.get(requestName));
			}

			List sessionNameList = accessHistory.getSessionNames();
			Map sessionMap = accessHistory.getSessionMap();
			for( int i=0; i<sessionNameList.size(); i++ ){
				String sessionName = (String)sessionNameList.get(i);
				session.setAttribute(sessionName, sessionMap.get(sessionName));
			}

			List parameterNameList = accessHistory.getParameterNames();
			Map parameterMap = accessHistory.getParameterMap();
			if( log.isDebugEnabled() ){
				log.debug("1. Parameter Map Size = " + parameterMap.size() );
			}
				
			JwareHttpServletRequestWrapper requestWrapper = new JwareHttpServletRequestWrapper(request);
			requestWrapper.setRequestURI( accessHistory.getUri() );
			
			for( int i=0; i<parameterNameList.size(); i++ ){
				String parameterName = (String)parameterNameList.get(i);
				if( log.isDebugEnabled() ){
					log.debug(" Parameter Name = " + parameterName );
				}
				String[] parameterValues = (String[])parameterMap.get(parameterName);
				for(int j=0; j<parameterValues.length; j++){
					log.debug("parameterValues["+j+"] = " + parameterValues[j]);
				}
				requestWrapper.setParameterValues( parameterName, parameterValues );				
			}
				
			request = (HttpServletRequest)requestWrapper;

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

	}

	
}