/**
 * License Agreement.
 *
 * Rich Faces - Natural Ajax for Java Server Faces (JSF)
 *
 * Copyright (C) 2007 Exadel, Inc.
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License version 2.1 as published by the Free Software Foundation.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301  USA
 */

package org.ajax4jsf;

import java.io.IOException;
import java.io.PrintWriter;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;

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 javax.servlet.http.HttpSession;

import org.ajax4jsf.request.MultipartRequest;
import org.ajax4jsf.webapp.ConfigurableFilter;

/**
 * Proxy for resource/ajax xml parsing filter.
 * 
 * @author asmirnov@exadel.com (latest modification by $Author: alexsmirnov $)
 * @version $Revision: 1.1.2.1 $ $Date: 2007/01/09 18:58:29 $
 * 
 */
public class Filter extends ConfigurableFilter {

	/** Multipart request start */
	public static final String MULTIPART = "multipart/";

	/** Session bean name where multipart requests map will be stored */
	public static final String REQUESTS_SESSIONS_BEAN_NAME = "_richfaces_upload_sessions";

	/** Session bean name where progress bar's percent map will be stored */
	public static final String PERCENT_BEAN_NAME = "_richfaces_upload_percents";

	/**
	 * Request parameter that indicates if multipart request forced by rich file
	 * upload component
	 */
	public static final String UPLOAD_FILES_ID = "_richfaces_upload_uid";

	/** Session bean name to store max files count allowed to upload */
	public static final String UPLOADED_COUNTER = "_richfaces_uploaded_file_counter";

	/**
	 * Flag indicating whether a temporary file should be used to cache the
	 * uploaded file
	 */
	private boolean createTempFiles = false;

	/**
	 * The maximum size of a file upload request. 0 means no limit.
	 */
	private int maxRequestSize = 0;

	/***************************************************************************
	 * Method catches upload files request. Request parameter
	 * <b>org.ajax4jsf.Filter.UPLOAD_FILES_ID</b> indicates if request
	 * generated by rich-upload component. If it was detected custom parsing
	 * request should be done. Processing information about percent of
	 * completion and file size will be put into session scope. In other case
	 * super filter's method will be invoked for request processing.
	 * 
	 * @param request
	 * @param response
	 * @param chain
	 */
	@Override
	public void doFilter(ServletRequest request, ServletResponse response,
			FilterChain chain) throws IOException, ServletException {
		if (!(response instanceof HttpServletResponse)) {
			chain.doFilter(request, response);
			return;
		}

		HttpServletRequest httpRequest = (HttpServletRequest) request;
		String uid = httpRequest.getParameter(UPLOAD_FILES_ID);
		if (uid != null) {
			if (isMultipartRequest(httpRequest)) {
				MultipartRequest multipartRequest = new MultipartRequest(
						httpRequest, createTempFiles, maxRequestSize, uid);

				Map<String, MultipartRequest> sessionsMap = null;
				Map<String, Object> percentMap = null;
				try {
					if (isFileSizeRestricted(request, maxRequestSize)) {
						printResponse(response,
								"<html id=\"_richfaces_file_upload_size_restricted\"></html>");
					} else if (!checkFileCount(httpRequest)) {
						printResponse(response,
								"<html id=\"_richfaces_file_upload_forbidden\"></html>");
					} else {
						HttpSession session = httpRequest.getSession();
						synchronized (session) {
							sessionsMap = (Map<String, MultipartRequest>) session
									.getAttribute(REQUESTS_SESSIONS_BEAN_NAME);
							percentMap = (Map<String, Object>) session
									.getAttribute(PERCENT_BEAN_NAME);
							if (sessionsMap == null) {
								sessionsMap = Collections
										.synchronizedMap(new HashMap<String, MultipartRequest>());
								session.setAttribute(
										REQUESTS_SESSIONS_BEAN_NAME,
										sessionsMap);
							}
							if (percentMap == null) {
								percentMap = new HashMap<String, Object>();
								session.setAttribute(PERCENT_BEAN_NAME,
										percentMap);
							}
						}
						percentMap.put(uid, 0); // associate percent value with
						// file
						// entry uid
						sessionsMap.put(uid, multipartRequest);

						if (multipartRequest.parseRequest()) {
							super.doFilter(multipartRequest, response, chain);
						} else {
							printResponse(response,
									"<html id=\"_richfaces_file_upload_stopped\"></html>");
						}
					}
				} finally {
					if (sessionsMap != null) {
						sessionsMap.remove(uid);
						percentMap.remove(uid);
					}
				}
			} else {
				if ("stop".equals(httpRequest.getParameter("action"))) {
					HttpSession session = httpRequest.getSession();
					Map<String, MultipartRequest> sessions = (Map<String, MultipartRequest>) session
							.getAttribute(REQUESTS_SESSIONS_BEAN_NAME);

					if (sessions != null) {
						MultipartRequest multipartRequest = sessions.get(uid);
						if (multipartRequest != null) {
							multipartRequest.stop();
							HttpServletResponse httpResponse = (HttpServletResponse) response;
							httpResponse
									.setStatus(HttpServletResponse.SC_NO_CONTENT);
							httpResponse.getOutputStream().close();
						}
					}
				} else {
					super.doFilter(request, response, chain);
				}
			}
		} else {
			super.doFilter(request, response, chain);
		}
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see org.ajax4jsf.webapp.BaseFilter#init(javax.servlet.FilterConfig)
	 */
	@Override
	public void init(FilterConfig filterConfig) throws ServletException {
		super.init(filterConfig);
		String param = filterConfig.getInitParameter("createTempFiles");
		if (param != null) {
			this.createTempFiles = Boolean.parseBoolean(param);
		} else {
			this.createTempFiles = true;
		}
		param = filterConfig.getInitParameter("maxRequestSize");
		if (param != null) {
			this.maxRequestSize = Integer.parseInt(param);
		}
	}

	private boolean isMultipartRequest(HttpServletRequest request) {
		if (!"post".equals(request.getMethod().toLowerCase())) {
			return false;
		}

		String contentType = request.getContentType();
		if (contentType == null) {
			return false;
		}

		if (contentType.toLowerCase().startsWith(MULTIPART)) {
			return true;
		}

		return false;
	}

	private boolean isFileSizeRestricted(ServletRequest request, int maxSize) {
		if (maxSize != 0 && request.getContentLength() > maxSize) {
			return true;
		}
		return false;
	}

	private boolean checkFileCount(HttpServletRequest request) {
		HttpSession session = request.getSession();
		Map<String, Integer> map = (Map<String, Integer>) session
				.getAttribute(UPLOADED_COUNTER);
		if (map != null) {
			String id = request.getParameter("id");
			if (id != null) {
				Integer i = map.get(id);
				if (i != null && i == 0) {
					return false;
				}
			}
		}
		return true;
	}

	private void printResponse(ServletResponse response, String message)
			throws IOException {
		HttpServletResponse httpResponse = (HttpServletResponse) response;
		httpResponse.setStatus(HttpServletResponse.SC_OK);
		httpResponse.setContentType("text/html");
		PrintWriter writer = httpResponse.getWriter();
		writer.write(message);
		writer.close();
	}

}
