package jp.cssj.server.rest;

import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.servlet.http.HttpServletRequest;

import org.apache.commons.fileupload.FileItemIterator;
import org.apache.commons.fileupload.FileItemStream;
import org.apache.commons.fileupload.FileUploadException;
import org.apache.commons.fileupload.servlet.ServletFileUpload;
import org.apache.commons.fileupload.util.Streams;
import org.apache.commons.io.IOUtils;

public class RestRequest {
	public static class FormField {
		public final String name, value;
		public final byte[] data;

		public FormField(String name, String value, byte[] data) {
			this.name = name;
			this.value = value;
			this.data = data;
		}
	}

	public final HttpServletRequest req;
	public final FileItemIterator iter;
	private FileItemStream item = null;
	private Map nameToValue = null;
	private List fields = new ArrayList();

	public static final byte NONE = 0;
	public static final byte FIELD = 1;
	public static final byte FILE = 2;
	private int pos = 0;

	public static final RestRequest getRestRequest(HttpServletRequest req) {
		RestRequest restReq = (RestRequest) req.getAttribute(RestRequest.class
				.getName());
		return restReq;
	}

	public RestRequest(HttpServletRequest req) throws IOException,
			FileUploadException {
		this.req = req;
		if (ServletFileUpload.isMultipartContent(req)) {
			ServletFileUpload upload = new ServletFileUpload();
			this.iter = upload.getItemIterator(req);
		} else {
			this.iter = null;
		}
		req.setAttribute(RestRequest.class.getName(), this);
		for (Enumeration i = req.getParameterNames(); i.hasMoreElements();) {
			String name = (String) i.nextElement();
			if (name.startsWith("rest.")) {
				this.fields.add(new FormField(name, req.getParameter(name),
						null));
			} else {
				String[] values = req.getParameterValues(name);
				for (int j = 0; j < values.length; ++j) {
					this.fields.add(new FormField(name, values[j], null));
				}
			}
		}
	}

	public byte nextType() throws IOException, FileUploadException {
		if (this.pos < this.fields.size()) {
			return FIELD;
		}
		FileItemStream item = this.current();
		if (item == null) {
			return NONE;
		}
		return item.isFormField() ? FIELD : FILE;
	}

	public Object getItem() throws IOException, FileUploadException {
		if (this.pos < this.fields.size()) {
			FormField field = (FormField) this.fields.get(this.pos);
			return field;
		}
		FileItemStream item = this.current();
		if (item.isFormField()) {
			String charset = req.getCharacterEncoding();
			if (charset == null) {
				charset = RestServlet.CHARSET;
			}
			FormField field;
			InputStream in = item.openStream();
			try {
				byte[] data = IOUtils.toByteArray(in);
				String value = new String(data, charset);
				field = new FormField(item.getFieldName(), value, data);
			} finally {
				in.close();
			}
			return field;
		}
		return item;
	}

	public void nextItem() throws IOException, FileUploadException {
		if (this.pos < this.fields.size()) {
			++this.pos;
			return;
		}
		this.next();
	}

	private void next() throws IOException, FileUploadException {
		if (this.iter == null) {
			return;
		}
		if (!this.iter.hasNext()) {
			this.item = null;
			return;
		}
		this.item = this.iter.next();
	}

	private FileItemStream current() throws IOException, FileUploadException {
		if (this.item == null) {
			this.next();
		}
		return this.item;
	}

	public String getParameter(String name) throws IOException,
			FileUploadException {
		String value = this.req.getParameter(name);
		if (value != null || this.item == null) {
			return value;
		}
		String charset = this.req.getCharacterEncoding();
		if (charset == null) {
			charset = RestServlet.CHARSET;
		}
		for (FileItemStream item = this.current(); item != null; item = this
				.current()) {
			if (!item.isFormField()) {
				break;
			}
			String fieldName = item.getFieldName();
			value = Streams.asString(item.openStream(), charset);
			if (this.nameToValue == null) {
				this.nameToValue = new HashMap();
			}
			this.nameToValue.put(fieldName, value);
			this.fields.add(new FormField(fieldName, value, null));
			if (fieldName.equals(name)) {
				this.item = null;
				return value;
			}
			this.next();
		}
		if (this.nameToValue != null) {
			value = (String) this.nameToValue.get(name);
		}
		return value;
	}
}
