package it.mice.voila.runtime.springmvc.interceptors;

import it.mice.voila.runtime.springmvc.bean.PathStack;
import it.mice.voila.runtime.springmvc.bean.PathStackElement;
import it.mice.voila.runtime.springmvc.controller.ManageController;
import it.mice.voila.runtime.springmvc.util.SpringMvcUtil;

import java.util.ArrayList;
import java.util.Collection;

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

import org.apache.commons.lang.StringUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;

/**
 * Spring mvc interceptor that mantain the path stack chain for go back facility.
 */
public class PathStackMantainerInterceptor extends HandlerInterceptorAdapter {
	public static final String NEW_FLOW_PARAM_NAME = "newFlow";
	public static final String NO_STACK_PARAM_NAME = "noStack";
	public static final String FORCE_STACK_PARAM_NAME = "forceStack";
	public static final String INCLUDE_QUERY_STRING_PARAM_NAME = "includeQueryString";
	/**
	 * default logger.
	 */
	Log log = LogFactory.getLog(PathStackMantainerInterceptor.class);

	/* (non-Javadoc)
	 * @see org.springframework.web.servlet.handler.HandlerInterceptorAdapter#preHandle(javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse, java.lang.Object)
	 */
	@Override
	public boolean preHandle(HttpServletRequest request,
			HttpServletResponse response, Object handler) throws Exception {

		if (request.getMethod().equals("GET")) {
			PathStack stack = SpringMvcUtil.getStack();
			String lastPath = request.getRequestURI();
			
			cleanupPathStack(stack, lastPath);
		}
		
		return true;
	}

	/* (non-Javadoc)
	 * @see org.springframework.web.servlet.handler.HandlerInterceptorAdapter#afterCompletion(javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse, java.lang.Object, java.lang.Exception)
	 */
	@Override
	public void afterCompletion(HttpServletRequest request,
			HttpServletResponse response, Object handler, Exception ex)
			throws Exception {

		Object handlerBean = SpringMvcUtil.getHandlerBean(handler);

		String forceStack = (String)SpringMvcUtil.getRequestParameterOrAttribute(FORCE_STACK_PARAM_NAME);
		String noStack = (String)SpringMvcUtil.getRequestParameterOrAttribute(NO_STACK_PARAM_NAME);
		if ("true".equals(noStack)) {
			if (log.isDebugEnabled()) {
				log.debug("No stack parameter found into request...skipping saving just executed operation in the stack...");
			}
			return;
		}
		if (!(handlerBean instanceof ManageController) && !"true".equals(forceStack)) {
			if (log.isDebugEnabled()) {
				log.debug("Handler is not a ManageController...skipping saving just executed operation in the stack...");
			}
			return;
		}
		if (!request.getMethod().equals("GET") && !"true".equals(forceStack)) {
			if (log.isDebugEnabled()) {
				log.debug("Handler is a ManageController but the request method is not GET...skipping saving just executed operation in the stack...");
			}
			return;
		}
//		if (FlashMap.getMessages().hasErrors()) {
//			if (log.isDebugEnabled()) {
//				log.debug("Handler is a ManageController but the operation returned errors...skipping saving just executed operation in the stack...");
//			}
//			return;
//		}
		if (response.getContentType() == null || !response.getContentType().startsWith("text/html")) {
			if (log.isDebugEnabled()) {
				log.debug("Response content type is not text/html...skipping saving just executed operation in the stack...");
			}
			return;
		}

		PathStack stack = SpringMvcUtil.getStack();
		String lastPath = request.getRequestURI();
		
		String includeQueryString = (String)SpringMvcUtil.getRequestParameterOrAttribute(INCLUDE_QUERY_STRING_PARAM_NAME);
		String queryString = null;
		if ("true".equals(includeQueryString)) {
			//queryString = cleanUpQueryString(request.getQueryString());
			queryString = request.getQueryString();
		}
		String lastStackPath = stack.empty() ? null : stack.lastElement().getUrl();
		if (lastPath != null) {
			if (log.isDebugEnabled()) {
				log.debug("Handler is a ManageController and everything is ok, saving path " + lastPath + " into the stack...");
			}
			String newFlow = (String)SpringMvcUtil.getRequestParameterOrAttribute(NEW_FLOW_PARAM_NAME);
			boolean startNewFlow = ("true".equals(newFlow) && request.getMethod().equals("GET"));
			if (startNewFlow && !stack.isEmpty()) {
				stack.lastElement().setNewFlow(startNewFlow);
			}
			String headerKey = (String)request.getAttribute("headerTitle");
			if (lastPath.equals(lastStackPath)) {
				if (log.isDebugEnabled()) {
					log.debug("The last saved path is the same of the current operation...removing the old one and replace with the new one...");
				}
				stack.pop();
			}
			stack.push(new PathStackElement(lastPath, queryString, headerKey, false));
		}
		super.afterCompletion(request, response, handler, ex);
	}

	private String cleanUpQueryString(String queryString) {
		if (queryString == null) {
			return null;
		}
		Collection<String> result = new ArrayList<String>();
		String[] queryStrings = queryString.split("&");
		for (int i = 0; i < queryStrings.length; i++) {
			String[] paramParts = queryStrings[i].split("=");
			if (!NEW_FLOW_PARAM_NAME.equals(paramParts[0]) && !NO_STACK_PARAM_NAME.equals(paramParts[0]) && !FORCE_STACK_PARAM_NAME.equals(paramParts[0]) && !INCLUDE_QUERY_STRING_PARAM_NAME.equals(paramParts[0])) {
				result.add(queryStrings[i]);
			}
		}
		if (result.size() == 0) {
			return null;
		}
		return StringUtils.join(result, "&");
	}

	private void cleanupPathStack(PathStack stack, String lastPath) {
		boolean removeNext = false;
		String prevHeaderKey = null;
		for (int c = 0; c < stack.size(); c++) {
			PathStackElement pathStackElement = stack.elementAt(c);
			if (!removeNext && (StringUtils.equals(pathStackElement.getUrl(),lastPath) || StringUtils.equals(pathStackElement.getHeaderKey(), prevHeaderKey))) {
				removeNext = true;
			}
			if (removeNext) {
				stack.remove(c--);
			}
			prevHeaderKey = pathStackElement.getHeaderKey();
		}
	}
}
