/*
 * This file is part of Nuts Framework.
 * Copyright(C) 2009-2012 Nuts Develop Team.
 *
 * Nuts Framework is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License any later version.
 * 
 * Nuts Framework 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 General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with Nuts Framework. If not, see <http://www.gnu.org/licenses/>.
 */
package nuts.exts.fileupload;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.UnsupportedEncodingException;
import java.util.Map;

import nuts.core.io.IOUtils;

import org.apache.commons.fileupload.FileItem;
import org.apache.commons.fileupload.FileItemHeaders;
import org.apache.commons.fileupload.FileItemHeadersSupport;
import org.apache.commons.fileupload.ParameterParser;

@SuppressWarnings("serial")
public class MemoryFileItem implements FileItem, FileItemHeadersSupport {

	public static final String DEFAULT_CHARSET = "ISO-8859-1";

	private String fieldName;
	private String contentType;
	private boolean isFormField;
	private String fileName;

	private byte[] cachedContent = null;
	private ByteArrayOutputStream baos = null;

	private FileItemHeaders headers = null;

	/**
	 * Constructs a new <code>MemoryFileItem</code> instance.
	 * 
	 * @param fieldName The name of the form field.
	 * @param contentType The content type passed by the browser or
	 *            <code>null</code> if not specified.
	 * @param isFormField Whether or not this item is a plain form field, as
	 *            opposed to a file upload.
	 * @param fileName The original filename in the user's filesystem, or
	 *            <code>null</code> if not specified.
	 */
	public MemoryFileItem(String fieldName, String contentType,
			boolean isFormField, String fileName) {
		this.fieldName = fieldName;
		this.contentType = contentType;
		this.isFormField = isFormField;
		this.fileName = fileName;
	}

	/**
	 * Deletes the underlying storage for a file item, including deleting any
	 * associated temporary disk file. Although this storage will be deleted
	 * automatically when the <code>FileItem</code> instance is garbage
	 * collected, this method can be used to ensure that this is done at an
	 * earlier time, thus preserving system resources.
	 */
	public void delete() {
		cachedContent = null;
		baos = null;
	}

	/**
	 * Returns the contents of the file item as an array of bytes.
	 * 
	 * @return The contents of the file item as an array of bytes.
	 */
	public byte[] get() {
		if (cachedContent == null) {
			cachedContent = baos.toByteArray();
		}
		return cachedContent;
	}

	/**
	 * Returns the content type passed by the browser or <code>null</code> if
	 * not defined.
	 * 
	 * @return The content type passed by the browser or <code>null</code> if
	 *         not defined.
	 */
	public String getContentType() {
		return contentType;
	}

	/**
	 * Returns the name of the field in the multipart form corresponding to this
	 * file item.
	 * 
	 * @return The name of the form field.
	 */
	public String getFieldName() {
		return fieldName;
	}

	public InputStream getInputStream() throws IOException {
		if (cachedContent == null) {
			cachedContent = baos.toByteArray();
		}
		return new ByteArrayInputStream(cachedContent);
	}

	public String getName() {
		return fileName;
	}

	public OutputStream getOutputStream() throws IOException {
		if (baos == null) {
			baos = new ByteArrayOutputStream();
		}
		return baos;
	}

	public long getSize() {
		if (baos != null) {
			baos.size();
		}
		return 0;
	}

	public String getString() {
		byte[] rawdata = get();
		String charset = getCharSet();
		if (charset == null) {
			charset = DEFAULT_CHARSET;
		}
		try {
			return new String(rawdata, charset);
		}
		catch (UnsupportedEncodingException e) {
			return new String(rawdata);
		}
	}

	public String getCharSet() {
		ParameterParser parser = new ParameterParser();
		parser.setLowerCaseNames(true);
		// Parameter parser can handle null input
		Map params = parser.parse(getContentType(), ';');
		return (String)params.get("charset");
	}

	public String getString(String encoding)
			throws UnsupportedEncodingException {
		return new String(get(), encoding);
	}

	public boolean isFormField() {
		return isFormField;
	}

	public boolean isInMemory() {
		return true;
	}

	public void setFieldName(String name) {
		this.fieldName = name;
	}

	public void setFormField(boolean state) {
		this.isFormField = state;
	}

	public void write(File file) throws Exception {
		FileOutputStream fout = null;
		try {
			fout = new FileOutputStream(file);
			fout.write(get());
		}
		finally {
			if (fout != null) {
				IOUtils.closeQuietly(fout);
			}
		}
	}

	public FileItemHeaders getHeaders() {
		return headers;
	}

	public void setHeaders(FileItemHeaders headers) {
		this.headers = headers;
	}

}
