class RouterFilter implements Filter {

	@Override
	public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
			throws IOException, ServletException {
		HttpServletRequest httpServletRequest = (HttpServletRequest) request;

		// 如果是 fetch 请求，则不做任何处理
		if(RequestUtil.isFetch(httpServletRequest)) {
			chain.doFilter(request, response);
			return;
		}
		
		String servletPath = httpServletRequest.getServletPath();
		if(StringUtils.isBlank(servletPath)) {
			servletPath = httpServletRequest.getRequestURI();
		}
		
		// websocket 直接过
		String finalServletPath = servletPath;
		if(Arrays.stream(Resources.WS_ENDPOINTS).anyMatch(item -> finalServletPath.startsWith(item))) {
			chain.doFilter(request, response);
			return;
		}
		
		// Single Page Application 单页面应用的路由处理
		System.out.println("===============================================");
		System.out.println("===============================================");
		System.out.println("===============================================");
		System.out.println("servlet path = " + servletPath);
		httpServletRequest.getHeaderNames().asIterator().forEachRemaining(name -> System.out.println(name + " = " + httpServletRequest.getHeader(name)));
		
		System.out.println("url:" + servletPath);
		System.out.println("context path:" + httpServletRequest.getContextPath());
		System.out.println("pathInfo:" + httpServletRequest.getPathInfo());
		
		// 以下情况，不作处理，直接访问
		// 1. 访问首页
		// 2. 访问帮助文档中的图片
		if(servletPath.equals(WebSite.HOME_URL) || servletPath.startsWith("/raw/docs") ) {
			chain.doFilter(request, response);
			return;
		}
		
		String filenameExtension = UriUtils.extractFileExtension(servletPath);
		String referer = httpServletRequest.getHeader("referer");
		// 当按下浏览器的 F5，刷新 Single Page Application 的任一页面时，都跳转到首页
		// 如果是浏览器刷新，则 referer 的值必为 null
		boolean routerMatched = routerTemplates.stream().anyMatch(uriTemplate -> uriTemplate.matches(finalServletPath));
		boolean isStaticResource = isStaticResource(filenameExtension);
		// 能执行到这里，则一定是普通的浏览器请求，而不是 Fetch 请求
		// 而通过浏览器请求的，一律跳转到首页。
		if(!isStaticResource && 
				(routerMatched || needPrependServlet(servletPath))) {
			request.getRequestDispatcher(WebSite.HOME_URL).forward(request, response);
			return;
		}
		
		// 以下代码为转换资源文件的路径
		if(isStaticResource) {
			// 因为 /designer/assets 也需要返回静态资源，需要特殊处理
			if(servletPath.startsWith("/designer/assets")) {
				chain.doFilter(request, response);
				return;
			}
			
			if(referer == null) {
				// 因为访问 js.map 和 css.map 时，referer 的值为 null，但依然需要解析
				if(filenameExtension.equals("map")) {
					String sourceMapName = org.springframework.util.StringUtils.getFilename(servletPath);
					String sourceName = sourceMapName.substring(0, sourceMapName.length() - ".map".length());
					String sourceMapPath = sourceMap.get(sourceName) + ".map";
					request.getRequestDispatcher(sourceMapPath).forward(request, response);
					return;
				}
				
				chain.doFilter(request, response);
				return;
			}
			URI refererUri = URI.create(referer);
			String[] pathes = StringUtils.split(refererUri.getPath(), "/");
			String urlRewrite = Arrays.stream(pathes).reduce(servletPath, (result, item) -> {
				// 避免出现将 ab.js 截为 b.js 的情况
				String removed = "/" + item + "/";
				if(result.startsWith(removed)) {
					return result.substring(removed.length() - 1);
				}
				return result;
			});

			// cache for source map
			if(filenameExtension.equals("css") || filenameExtension.equals("js")) {
				String fileName = org.springframework.util.StringUtils.getFilename(servletPath);
				if(!sourceMap.containsKey(fileName)) {
					sourceMap.put(fileName, urlRewrite);
				}
			}
			
			request.getRequestDispatcher(urlRewrite).forward(request, response);
			return;
		}
		
		chain.doFilter(request, response);
	}

	public  RouterFilter();

}