package uws.service.request;

/*
 * This file is part of UWSLibrary.
 *
 * UWSLibrary is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * UWSLibrary 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 UWSLibrary.  If not, see <http://www.gnu.org/licenses/>.
 *
 * Copyright 2014 - Astronomisches Rechen Institut (ARI)
 */

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;

import javax.servlet.http.HttpServletRequest;

import uws.UWSException;
import uws.service.UWS;
import uws.service.file.UWSFileManager;

/**
 * This parser merely copies the whole HTTP request content inside a file.
 * It names this file: "JDL" (Job Description Language).
 *
 * <p>
 * 	The created file is stored in the temporary upload directory
 * 	({@link UWSFileManager#getTmpDirectory()}. This directory is supposed to be
 * 	emptied regularly in case it is forgotten at any moment by the service
 * 	implementation to delete unused request files.
 * </p>
 *
 * <p>
 * 	The size of the JDL is limited by the static attribute {@link #SIZE_LIMIT}
 * 	before the creation of the file. Its default value is:
 * 	{@link #DEFAULT_SIZE_LIMIT}={@value #DEFAULT_SIZE_LIMIT} bytes.
 * </p>
 *
 * @author Gr&eacute;gory Mantelet (CDS;ARI)
 * @version 4.4 (08/2018)
 * @since 4.1
 */
public class NoEncodingParser implements RequestParser {

	/** Default maximum allowed size for an HTTP request content: 2 MiB. */
	public static final int DEFAULT_SIZE_LIMIT = 2 * 1024 * 1024;

	/** <p>Maximum allowed size for an HTTP request content. Over this limit, an exception is thrown and the request is aborted.</p>
	 * <p><i>Note:
	 * 	The default value is {@link #DEFAULT_SIZE_LIMIT} (= {@value #DEFAULT_SIZE_LIMIT} MiB).
	 * </i></p>
	 * <p><i>Note:
	 * 	This limit is expressed in bytes and can not be negative.
	 *  Its smallest possible value is 0. If the set value is though negative,
	 *  it will be ignored and {@link #DEFAULT_SIZE_LIMIT} will be used instead.
	 * </i></p> */
	public static int SIZE_LIMIT = DEFAULT_SIZE_LIMIT;

	/** File manager to use to create {@link UploadFile} instances.
	 * It is required by this new object to execute open, move and delete operations whenever it could be asked. */
	protected final UWSFileManager fileManager;

	/**
	 * Build the request parser.
	 *
	 * @param fileManager	A file manager. <b>MUST NOT be NULL</b>
	 */
	public NoEncodingParser(final UWSFileManager fileManager){
		if (fileManager == null)
			throw new NullPointerException("Missing file manager => can not create a SingleDataParser!");
		this.fileManager = fileManager;
	}

	@Override
	public Map<String, Object> parse(final HttpServletRequest request) throws UWSException{
		// Check the request size:
		if (request.getContentLength() <= 0)
			return new HashMap<String, Object>();
		else if (request.getContentLength() > (SIZE_LIMIT < 0 ? DEFAULT_SIZE_LIMIT : SIZE_LIMIT))
			throw new UWSException("JDL too big (>" + SIZE_LIMIT + " bytes) => Request rejected! You should see with the service administrator to extend this limit.");

		// Build the parameter name:
		String paramName;
		if (request.getMethod() != null && request.getMethod().equalsIgnoreCase("put")){
			paramName = request.getRequestURI();
			if (paramName.lastIndexOf('/') + 1 > 0)
				paramName = paramName.substring(paramName.lastIndexOf('/') + 1);
		}else
			paramName = "JDL";

		// Build the file by copy of the whole request body:
		Object reqID = request.getAttribute(UWS.REQ_ATTRIBUTE_ID);
		if (reqID == null || !(reqID instanceof String))
			reqID = (new Date()).getTime();
		File f = new File(fileManager.getTmpDirectory(), "REQUESTBODY_" + reqID);
		OutputStream output = null;
		InputStream input = null;
		long totalLength = 0;
		try{
			output = new BufferedOutputStream(new FileOutputStream(f));
			input = new BufferedInputStream(request.getInputStream());

			byte[] buffer = new byte[2049];
			int len = input.read(buffer);
			if (len <= 0){
				output.close();
				f.delete();
				HashMap<String, Object> params = new HashMap<String, Object>(1);
				params.put(paramName, "");
				return params;
			}else if (len <= 2048 && request.getMethod() != null && request.getMethod().equalsIgnoreCase("put") && request.getContentType() != null && request.getContentType().toLowerCase().startsWith("text/plain")){
				output.close();
				f.delete();
				HashMap<String, Object> params = new HashMap<String, Object>(1);
				params.put(paramName, new String(buffer, 0, len));
				return params;
			}else{
				do{
					output.write(buffer, 0, len);
					totalLength += len;
				}while((len = input.read(buffer)) > 0);
				output.flush();
			}
		}catch(IOException ioe){
			throw new UWSException(UWSException.INTERNAL_SERVER_ERROR, ioe, "Internal error => Impossible to get the JDL from the HTTP request!");
		}finally{
			if (input != null){
				try{
					input.close();
				}catch(IOException ioe2){
				}
			}
			if (output != null){
				try{
					output.close();
				}catch(IOException ioe2){
				}
			}
		}

		// Build its description:
		UploadFile lob = new UploadFile(paramName, f.toURI().toString(), fileManager);
		lob.mimeType = request.getContentType();
		lob.length = totalLength;

		// Create the parameters map:
		HashMap<String, Object> parameters = new HashMap<String, Object>();
		parameters.put(paramName, lob);

		return parameters;
	}

}
