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.IOException;
import java.io.InputStream;

import uws.job.UWSJob;
import uws.job.parameters.UWSParameters;
import uws.service.file.UWSFileManager;

/**
 * <p>This class lets represent a file submitted inline in an HTTP request.</p>
 * 
 * <p>
 * 	To read this special kind of parameter, an {@link InputStream} must be open. This class lets do it
 * 	by its function {@link #open()}.
 * </p>
 * 
 * <p>
 * 	When not used any more this file should be deleted, in order to save server disk space.
 * 	This can be easily done thanks to {@link #deleteFile()}. This function actually just call the corresponding function
 * 	of the file manager, which is the only one to known how to deal with this file on the server. Indeed, even if most
 * 	of the time this file is stored on the local file system, it could also be stored on a distant server by a VOSpace.
 * 	In this case, the way to proceed is different, hence the use of the file manager.
 * </p>
 * 
 * @author Gr&eacute;gory Mantelet (ARI)
 * @version 4.1 (11/2014)
 * 
 * @see UWSParameters
 * @see MultipartParser
 */
public class UploadFile {
	/** Name of the parameter in which the file was submitted. */
	public final String paramName;

	/** File name. It is the name provided in the HTTP request. */
	public final String fileName;

	/** Location at which the content of this upload has been stored.
	 * It can be a local file path, but also any other path or ID allowing
	 * the {@link UWSFileManager} to access its content. */
	protected String location;

	/** Jobs that owns this uploaded file. */
	protected UWSJob owner = null;

	/** Indicate whether this file has been or is used by a UWSJob.
	 * In other words, it is <i>true</i> when an open, move or delete operation has been performed.
	 * An unused {@link UploadFile} instance shall be physically deleted from the file system. */
	protected boolean used = false;

	/** MIME type of the file. */
	public String mimeType = null;

	/** Length in bytes of the file.
	 * If negative, the length should be considered as unknown. */
	public long length = -1;

	/** File manager to use in order to open, move or delete this uploaded file. */
	protected final UWSFileManager fileManager;

	/**
	 * Build the description of an uploaded file.
	 * 
	 * @param paramName		Name of the HTTP request parameter in which the uploaded content was stored. <b>MUST NOT be NULL</b>
	 * @param location		Location of the file on the server. This String is then used by the given file manager in order to open,
	 *                		move or delete the uploaded file. Thus, it can be a path, an ID or any other String meaningful to the file manager.
	 * @param fileManager	File manager to use in order to open, move or delete this uploaded file from the server.
	 */
	public UploadFile(final String paramName, final String location, final UWSFileManager fileManager){
		this(paramName, null, location, fileManager);
	}

	/**
	 * Build the description of an uploaded file.
	 * 
	 * @param paramName		Name of the HTTP request parameter in which the uploaded content was stored. <b>MUST NOT be NULL</b>
	 * @param fileName		Filename as provided by the HTTP request. <i>MAY be NULL</i>
	 * @param location		Location of the file on the server. This String is then used by the given file manager in order to open,
	 *                		move or delete the uploaded file. Thus, it can be a path, an ID or any other String meaningful to the file manager.
	 * @param fileManager	File manager to use in order to open, move or delete this uploaded file from the server.
	 */
	public UploadFile(final String paramName, final String fileName, final String location, final UWSFileManager fileManager){
		if (paramName == null)
			throw new NullPointerException("Missing name of the parameter in which the uploaded file content was => can not create UploadFile!");
		else if (location == null)
			throw new NullPointerException("Missing server location of the uploaded file => can not create UploadFile!");
		else if (fileManager == null)
			throw new NullPointerException("Missing file manager => can not create the UploadFile!");

		this.paramName = paramName;
		this.fileName = (fileName == null) ? "" : fileName;
		this.location = location;
		this.fileManager = fileManager;
	}

	/**
	 * <p>Get the location (e.g. URI, file path) of this file on the server.</p>
	 * 
	 * <p><i>Important note:
	 * 	This function SHOULD be used only by the {@link UWSFileManager} when open, move and delete operations are executed.
	 * 	The {@link RequestParser} provided by the library set this location to the file URI (i.e. "file://{local-file-path}")
	 * 	since the default behavior is to store uploaded file on the system temporary directory.
	 * </i></p>
	 * 
	 * @return	Location (e.g. URI) or ID or any other meaningful String used by the file manager to access to the uploaded file.
	 */
	public String getLocation(){
		return location;
	}

	/**
	 * Get the job that uses this uploaded file.
	 * 
	 * @return	The owner of this file.
	 */
	public UWSJob getOwner(){
		return owner;
	}

	/**
	 * <p>Tell whether this uploaded file has been or will be used.
	 * That's to say, whether an open, delete or move operation has been executed (even if it failed) on this {@link UploadFile} instance.</p>
	 * 
	 * @return	<i>true</i> if the file must be preserved, <i>false</i> otherwise.
	 */
	public final boolean isUsed(){
		return used;
	}

	/**
	 * Open a stream toward this uploaded file.
	 * 
	 * @return	Stream toward this upload content.
	 * 
	 * @throws IOException	If an error occurs while opening the stream.
	 * 
	 * @see UWSFileManager#getUploadInput(UploadFile)
	 */
	public InputStream open() throws IOException{
		used = true;
		return fileManager.getUploadInput(this);
	}

	/**
	 * Delete definitely this uploaded file from the server.
	 * 
	 * @throws IOException	If the delete operation can not be performed.
	 *        
	 * @see UWSFileManager#deleteUpload(UploadFile)
	 */
	public void deleteFile() throws IOException{
		fileManager.deleteUpload(this);
		used = true;
	}

	/**
	 * <p>Move this uploaded file in a location related to the given {@link UWSJob}.
	 * It is particularly useful if at reception of an HTTP request uploaded files are stored in a temporary
	 * directory (e.g. /tmp on Unix/Linux systems).</p>
	 * 
	 * <p>
	 * 	This function calls {@link UWSFileManager#moveUpload(UploadFile, UWSJob)} to process to the physical
	 * 	moving of the file, but it then, it updates its location in this {@link UploadFile} instance.
	 * 	<b>The file manager does NOT update this location! That's why it must not be called directly, but
	 * 	through {@link #move(UWSJob)}.</b>
	 * </p>
	 * 
	 * @param destination	The job by which this uploaded file will be exclusively used.
	 * 
	 * @throws IOException	If the move operation can not be performed.
	 * 
	 * @see UWSFileManager#moveUpload(UploadFile, UWSJob)
	 */
	public void move(final UWSJob destination) throws IOException{
		if (destination == null)
			throw new NullPointerException("Missing move destination (i.e. the job in which the uploaded file must be stored)!");

		location = fileManager.moveUpload(this, destination);
		used = true;
		owner = destination;
	}

	@Override
	public String toString(){
		return (owner != null && owner.getJobList() != null && owner.getUrl() != null) ? owner.getUrl().jobParameter(owner.getJobList().getName(), owner.getJobId(), paramName).toString() : fileName;
	}
}
