package jp.co.headwaters.webappos.controller.filter;

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.RequestDispatcher;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import jp.co.headwaters.webappos.controller.ControllerConstants;
import jp.co.headwaters.webappos.controller.cache.UrlPatternCache;
import jp.co.headwaters.webappos.controller.cache.bean.UrlPatternBean;
import jp.co.headwaters.webappos.controller.utils.MessageUtils;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

public class RoutingFilter implements Filter {

	private static final Log _logger = LogFactory.getLog(RoutingFilter.class);
	private UrlPatternCache _urlPatternCache;

	@Override
	public void init(FilterConfig config) throws ServletException {
		this._urlPatternCache = UrlPatternCache.getInstance();
		if (this._urlPatternCache == null || this._urlPatternCache.getUrlPatternMap().size() == 0) {
			throw new ServletException(MessageUtils.getString("err.000")); //$NON-NLS-1$
		}
	}

	@Override
	public void destroy() {
		// nothing
	}

	@Override
	public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
			throws IOException, ServletException {
		HttpServletRequest req = (HttpServletRequest) request;
		HttpServletResponse res = (HttpServletResponse) response;
		String uri = req.getRequestURI();
		_logger.debug(uri);

		String extension = getExtension(uri);
		if (extension != null) {
			chain.doFilter(request, response);
			return;
		}

		// append slash
		if (!uri.endsWith(ControllerConstants.PATH_DELIMITER)) {
			uri += ControllerConstants.PATH_DELIMITER;
		}

		// check uri pattern
		String regixUri = null;
		boolean isMatch = false;
		Map<String, UrlPatternBean> urlPatternMap = this._urlPatternCache.getUrlPatternMap();
		for (Entry<String, UrlPatternBean> urlPattern : urlPatternMap.entrySet()) {
			Pattern pattern = Pattern.compile(urlPattern.getValue().getPattern());
			Matcher matcher = pattern.matcher(uri);
			if (matcher.find()) {
				isMatch = true;
				regixUri = urlPattern.getKey();
				break;
			}
		}
		if (!isMatch) {
			res.sendError(HttpServletResponse.SC_NOT_FOUND);
			return;
		}

		// get parameter name
		String[] params = null;
		List<String> paramNames = new ArrayList<String>();
		Pattern pattern = Pattern.compile(ControllerConstants.REGEX_REQUEST_PARAM);
		Matcher matcher = pattern.matcher(regixUri);
		while (matcher.find()) {
			params = matcher.group().split(ControllerConstants.PATH_DELIMITER);
			paramNames.add(params[0]);
		}

		// forward
		UrlPatternBean urlpattern = urlPatternMap.get(regixUri);
		request.setAttribute(ControllerConstants.ATTR_NAME_RESULT_NAME, urlpattern.getResultName());
		String forwardUri = getForwardUri(urlpattern.getActionName(), paramNames, uri);
		RequestDispatcher rd = request.getRequestDispatcher(forwardUri);
		rd.forward(request, response); // 転送する
	}

	private static String getExtension(String uri) {
		if (uri == null)
			return null;
		int point = uri.lastIndexOf("."); //$NON-NLS-1$
		if (point != -1) {
			return uri.substring(point + 1);
		}
		return null;
	}

	private static String getForwardUri(String actionName, List<String> paramNames, String uri){
		StringBuilder sb = new StringBuilder();
		sb.append(ControllerConstants.PATH_DELIMITER);
		sb.append(actionName);

		StringBuilder params = new StringBuilder();
		// append query string
		if (paramNames.size() > 0) {
			String param = null;
			int beginIndex = 0;
			int endIndex = 0;
			for (String paramName : paramNames) {
				beginIndex = uri.indexOf(paramName) + paramName.length() + 1;
				endIndex = uri.indexOf(ControllerConstants.PATH_DELIMITER, beginIndex);
				param = uri.substring(beginIndex, endIndex);
				if (param != null && param.length() > 0){
					if (params.toString().length() > 0) {
						params.append("&"); //$NON-NLS-1$
					}
					params.append(paramName);
					params.append("="); //$NON-NLS-1$
					params.append(param);
				}
			}
		}

		if (params.toString().length() > 0){
			sb.append("?"); //$NON-NLS-1$
			sb.append(params.toString());
		}
		return sb.toString();
	}
}
