/**
 * Copyright (c) 2000-present Liferay, Inc. All rights reserved.
 *
 * The contents of this file are subject to the terms of the Liferay Enterprise
 * Subscription License ("License"). You may not use this file except in
 * compliance with the License. You can obtain a copy of the License by
 * contacting Liferay, Inc. See the License for the specific language governing
 * permissions and limitations under the License, including but not limited to
 * distribution rights of the Software.
 *
 *
 *
 */

package com.liferay.portal.vulcan.internal.jaxrs.message.body;

import com.fasterxml.jackson.databind.ObjectMapper;

import com.liferay.portal.vulcan.multipart.BinaryFile;
import com.liferay.portal.vulcan.multipart.MultipartBody;

import java.io.InputStream;

import java.lang.annotation.Annotation;
import java.lang.reflect.Type;

import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.servlet.http.HttpServletRequest;

import javax.ws.rs.BadRequestException;
import javax.ws.rs.Consumes;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.MultivaluedMap;
import javax.ws.rs.ext.ContextResolver;
import javax.ws.rs.ext.MessageBodyReader;
import javax.ws.rs.ext.Provider;
import javax.ws.rs.ext.Providers;

import org.apache.commons.fileupload.FileItem;
import org.apache.commons.fileupload.FileUploadBase;
import org.apache.commons.fileupload.disk.DiskFileItemFactory;
import org.apache.commons.fileupload.servlet.ServletFileUpload;
import org.apache.commons.fileupload.util.Streams;

/**
 * @author Alejandro Hernández
 * @author Javier Gamarra
 */
@Consumes(MediaType.MULTIPART_FORM_DATA)
@Provider
public class MultipartBodyMessageBodyReader
	implements MessageBodyReader<MultipartBody> {

	public MultipartBodyMessageBodyReader(long fileMaxSize) {
		_fileMaxSize = fileMaxSize;
	}

	@Override
	public boolean isReadable(
		Class<?> clazz, Type genericType, Annotation[] annotations,
		MediaType mediaType) {

		return true;
	}

	@Override
	public MultipartBody readFrom(
		Class<MultipartBody> clazz, Type genericType, Annotation[] annotations,
		MediaType mediaType, MultivaluedMap<String, String> multivaluedMap,
		InputStream inputStream) {

		ContextResolver<ObjectMapper> contextResolver =
			_providers.getContextResolver(
				ObjectMapper.class, MediaType.MULTIPART_FORM_DATA_TYPE);

		try {
			Map<String, BinaryFile> binaryFiles = new HashMap<>();
			Map<String, String> values = new HashMap<>();

			ServletFileUpload servletFileUpload = new ServletFileUpload(
				new DiskFileItemFactory());

			servletFileUpload.setFileSizeMax(_fileMaxSize);
			servletFileUpload.setSizeMax(_fileMaxSize);

			List<FileItem> fileItems = servletFileUpload.parseRequest(
				_httpServletRequest);

			for (FileItem fileItem : fileItems) {
				String name = fileItem.getFieldName();

				if (fileItem.isFormField()) {
					values.put(
						name, Streams.asString(fileItem.getInputStream()));
				}
				else {
					BinaryFile binaryFile = new BinaryFile(
						fileItem.getContentType(), fileItem.getName(),
						fileItem.getInputStream(), fileItem.getSize());

					binaryFiles.put(name, binaryFile);
				}
			}

			return MultipartBody.of(
				binaryFiles, contextResolver::getContext, values);
		}
		catch (FileUploadBase.SizeLimitExceededException
					sizeLimitExceededException) {

			throw new BadRequestException(
				"Please enter a file with a valid file size no larger than " +
					_fileMaxSize,
				sizeLimitExceededException);
		}
		catch (Exception e) {
			throw new BadRequestException(
				"Request body is not a valid multipart form", e);
		}
	}

	private final long _fileMaxSize;

	@Context
	private HttpServletRequest _httpServletRequest;

	@Context
	private ObjectMapper _objectMapper;

	@Context
	private Providers _providers;

}