package com.gfi.resolvers;

import java.util.List;

import javax.servlet.ServletRequestWrapper;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;

import org.apache.commons.fileupload.FileUpload;
import org.apache.commons.fileupload.FileUploadBase;
import org.apache.commons.fileupload.FileUploadException;
import org.apache.commons.fileupload.servlet.ServletFileUpload;
import org.springframework.util.MultiValueMap;
import org.springframework.web.multipart.MaxUploadSizeExceededException;
import org.springframework.web.multipart.MultipartException;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.multipart.MultipartHttpServletRequest;
import org.springframework.web.multipart.commons.CommonsFileUploadSupport;
import org.springframework.web.multipart.commons.CommonsMultipartResolver;
import org.springframework.web.multipart.support.DefaultMultipartHttpServletRequest;

import com.gfi.constants.Constants;

/**
 * Resolver para que funcione correctamente la subida de ficheros, cuando la
 * request Multipart se ha encapsulado dentro otro wrapper (metodo 'PUT' en
 * I.Explorer), de manera que sus campos 'File' no están visibles.<br/>
 * 
 * <b>Solución:</b><br/>
 * Buscar los campos file en las request Multipart encapsuladas, y trasladarlos
 * a la request visible.<br/>
 * Para utilizarlo en UDA:
 * <ul>
 * <li>Cambiar en el fichero mvc-config.xml el bean 'multipartResolver', para
 * que pase a implementar esta clase en vez de la de x38.</li>
 * <li>Agregar al fichero mvc-config.xml el bean 'iframeXHREmulationFilter' si
 * no está ya incluido.</li>
 * <li>En el fichero web.xml, asegurarse que los siguientes filtros existen y se
 * encuentran en este orden: <small>
 * <ol>
 * <li>udaFilter</li>
 * <li>multipartFilter</li>
 * <li>httpMethodFilter</li>
 * <li>iframeXHREmulationFilter</li>
 * <li>springSecurityFilterChain</li>
 * <li>characterEncodingFilter</li>
 * </ul>
 * </small></li>
 * 
 *  
 * 
 */
public class MultipartResolver extends CommonsMultipartResolver {

	/*
	 * Permitir peticiones multipart que tengan métodos diferentes a 'POST'.
	 */
	@Override
	public boolean isMultipart(HttpServletRequest request) {
		return ((request != null) && ((request.getContentType() != null) && (request
				.getContentType().toLowerCase().startsWith("multipart/"))));
	}

	/*
	 * Se cambia el encoding por defecto para que utilice UTF-8 en las
	 * peticiones multipart
	 * 
	 * @see org.springframework.web.multipart.commons.CommonsFileUploadSupport#
	 * getDefaultEncoding()
	 */
	@Override
	protected String getDefaultEncoding() {
		String encoding = this.getFileUpload().getHeaderEncoding();
		if (encoding == null) {
			encoding = Constants.UTF8;
		}
		return encoding;
	}

	@Override
	@SuppressWarnings(value = { "rawtypes", "unchecked" })
	protected CommonsFileUploadSupport.MultipartParsingResult parseRequest(
			HttpServletRequest request) throws MultipartException {
		String encoding = this.determineEncoding(request);
		FileUpload fileUpload = this.prepareFileUpload(encoding);
		try {
			List fileItems = ((ServletFileUpload) fileUpload)
					.parseRequest(request);
			MultipartParsingResult result = this.parseFileItems(fileItems,
					encoding);
			// XXX[JBHG] Cambio para que recoja ficheros de request
			// encapsuladas:
			this.searchInAllWrappedRequest(request, result.getMultipartFiles());
			return result;
		} catch (FileUploadBase.SizeLimitExceededException ex) {
			throw new MaxUploadSizeExceededException(fileUpload.getSizeMax(),
					ex);
		} catch (FileUploadException ex) {
			throw new MultipartException(
					"Could not parse multipart servlet request", ex);
		}
	}

	/**
	 * Si la request es multipart, agrega todos sus campos file al parametro
	 * <i>fileMap</i>. Si la request es un wrapper, se aplica recursividad sobre
	 * esta misma funcion, pasándole la request encapsulada.
	 * 
	 * @param request
	 *            HttpServletRequest
	 * @param fileMap
	 *            MultiValueMap
	 * @throws FileUploadException
	 *             e
	 */
	private void searchInAllWrappedRequest(HttpServletRequest request,
			MultiValueMap<String, MultipartFile> fileMap)
			throws FileUploadException {
		if (request instanceof MultipartHttpServletRequest) {
			DefaultMultipartHttpServletRequest reqMulti = (DefaultMultipartHttpServletRequest) request;
			fileMap.putAll(reqMulti.getMultiFileMap());
		} else if (request instanceof ServletRequestWrapper) {
			this.searchInAllWrappedRequest(
					(HttpServletRequest) ((HttpServletRequestWrapper) request)
							.getRequest(), fileMap);
		}
	}
}