package org.minager.core;

import java.io.IOException;
import java.util.List;
import java.util.regex.Pattern;

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

import org.minager.core.security.Route;
import org.minager.core.utils.StringUtils;
import org.springframework.http.HttpMethod;

/**
 * Base class for all filters of the application.<br/>
 * <br/>
 * The children classes have to implements the method
 * {@link AbstractFilter#getClass()} to set the URLs filtered (with all or some
 * http methods), and the method
 * {@link AbstractFilter#filter(HttpServletRequest, ServletResponse, FilterChain)}
 * to define the filter processing.
 * 
 * @author Takiguchi
 *
 */
public abstract class AbstractFilter implements Filter {

	/** Regex url path prefix for method {@link this#isRequestFiltered(String)}. */
	private static final String PREFIX_URL_PATH = "https?:\\/\\/.*(:\\d{0,5})?";

	@Override
	public void init(FilterConfig filterConfig) throws ServletException {
		// Do nothing
	}
	
	/**
	 * Returns the list of routes which will be processed by the filter.
	 * 
	 * @return The routes.
	 */
	protected abstract List<Route> getRoutes();
	
	/**
	 * Filter actions for its processing.
	 * 
	 * @param request
	 *            The http request.
	 * @param response
	 *            The response.
	 * @param chain
	 *            The chain.
	 */
	protected abstract void filter(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws IOException, ServletException;
	
	@Override
	public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
			throws IOException, ServletException {
		HttpServletRequest httpRequest = (HttpServletRequest) request;
		
		if(isRequestFiltered(httpRequest.getRequestURL().toString(), httpRequest.getMethod())) {
			filter(httpRequest, (HttpServletResponse) response, chain);
		} else {
			chain.doFilter(request, response);
		}
	}
	
	/**
	 * Check if the url is allowed with the given method in parameters.
	 * 
	 * @param pRequestUrl
	 *            The url request.
	 * @param pRequestMethod
	 *            The http method of the request.
	 * @return {@code true} if the url is allowed with the method, {@code false}
	 *         otherwise.
	 */
	boolean isRequestFiltered(final String pRequestUrl, final String pRequestMethod) {
		boolean result = false;
		
		for(final Route route : getRoutes()) {
			/*
			 * Check urls matching, and if the method of the route isn't set, all methods
			 * are allowed. Otherwise, we check the methods too.
			 */
			if(Pattern.matches(StringUtils.concat(PREFIX_URL_PATH, route.getUrl()), pRequestUrl)) {
				if(!route.getMethod().isPresent() || isMethodFiltered(route, pRequestMethod)) {
					result = true;
					break;
				}
			}
		}
		
		return result;
	}
	
	/**
	 * Checks if the route do filter the method in parameters.
	 * 
	 * @param pRoute
	 *            The registered route.
	 * @param pRequestMethod
	 *            The http method to check with the registered route.
	 */
	boolean isMethodFiltered(final Route pRoute, final String pRequestMethod) {
		boolean result = false;
		
		if(pRoute.getMethod().isPresent()) {
			for(final HttpMethod routeMethod : pRoute.getMethod().get()) {
				if(routeMethod.name().equals(pRequestMethod)) {
					result = true;
					break;
				}
			}
		}
		
		return result;
	}
	
	@Override
	public void destroy() {
		// Do nothing
	}
}
