/* 
 * E-XML Library:  For XML, XML-RPC, HTTP, and related.
 * Copyright (C) 2002-2008  Elias Ross
 * 
 * genman@noderunner.net
 * http://noderunner.net/~genman
 * 
 * 1025 NE 73RD ST
 * SEATTLE WA 98115
 * USA
 *
 * This library 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 2.1 of the License, or (at your option) any later version.
 *
 * This library 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.
 * 
 * $Id$
 */

package net.noderunner.http.servlet;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.UnsupportedEncodingException;
import java.net.InetSocketAddress;
import java.net.ServerSocket;
import java.net.URI;
import java.net.URISyntaxException;
import java.nio.charset.Charset;
import java.security.Principal;
import java.text.ParseException;
import java.util.Collections;
import java.util.Date;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Locale;
import java.util.Map;

import javax.servlet.RequestDispatcher;
import javax.servlet.ServletInputStream;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;

import net.noderunner.http.ContentType;
import net.noderunner.http.HttpUtil;
import net.noderunner.http.MessageHeader;
import net.noderunner.http.MessageHeaders;
import net.noderunner.http.ServerRequest;

/**
 * Simple HTTP servlet request.
 * @author Elias Ross
 */
public class HttpServletRequestImpl implements HttpServletRequest {
	
	private ServerRequest request;
	private BasicHttpSession session;
	private String encoding;
	private InputStream inputStream;
	
	private String serverName;
	private int serverPort;
	private String remoteHost;
	private int remotePort;
	private String remoteAddr;
	private Map<String, String[]> parameters = new HashMap<String, String[]>();
	private Map<String, Object> attributes = new HashMap<String, Object>();
	private String localAddr;
	private String localName;
	private int localPort;
	private boolean initParams = false;
	
	/**
	 * Constructs a new HttpServletRequestImpl based on a server request.
	 */
	public HttpServletRequestImpl(ServerRequest request) throws IOException {
		this.request = request;
		this.encoding = getCharacterEncoding0();
		try {
			URI uri = new URI(request.getRequestLine().getRequestURI());
			String rq = uri.getRawQuery();
			if (rq != null) {
        		parameters.putAll(HttpUtil.urlDecode(uri.getRawQuery()));
			}
		} catch (URISyntaxException e) {
			throw new RuntimeException(e);
		}
    	inputStream = HttpUtil.wrapInputStream(request.getInputStream(), request.getHeaders());
	}
	
	void serverSocket(ServerSocket ss) {
		serverName = ss.getLocalSocketAddress().toString();
		localName = ss.getLocalSocketAddress().toString();
		localAddr = ss.getLocalSocketAddress().toString();
		serverPort = ss.getLocalPort();
		localPort = ss.getLocalPort();
	}
	
	void remoteAddress(InetSocketAddress address) {
		remoteHost = address.getHostName();
		remoteAddr = address.getAddress().getHostAddress();
		remotePort = address.getPort();
	}
	
	public String getAuthType() {
		return null;
	}
	
	private String getURI() {
		String uri = request.getRequestLine().getRequestURI();
		return uri;
	}

	public String getContextPath() {
		return getURI();
	}

	public Cookie[] getCookies() {
		return new Cookie[0];
	}

	public long getDateHeader(String fieldName) {
		try {
			Date date = new HttpDateFormat().parse(getHeader(fieldName));
			return date.getTime();
		} catch (ParseException e) {
			throw new RuntimeException(e);
		}
	}

	public String getHeader(String fieldName) {
		return request.getHeaders().getFieldContent(fieldName);
	}

	public Enumeration<String> getHeaderNames() {
		return Collections.enumeration(request.getHeaders().getNames());
	}

	public Enumeration<Object> getHeaders(String arg0) {
		throw new UnsupportedOperationException();
	}

	public int getIntHeader(String fieldName) {
		return Integer.parseInt(getHeader(fieldName));
	}

	public String getMethod() {
		return request.getRequestLine().getMethod().name();
	}

	public String getPathInfo() {
		return getURI();
	}

	public String getPathTranslated() {
		return getURI();
	}

	public String getQueryString() {
		return getURI();
	}

	public String getRemoteUser() {
		return null;
	}

	public String getRequestURI() {
		return getURI();
	}

	public StringBuffer getRequestURL() {
		return new StringBuffer(getURI());
	}

	public String getRequestedSessionId() {
		return null;
	}

	public String getServletPath() {
		return getURI();
	}

	public HttpSession getSession() {
		if (session == null)
			this.session = new BasicHttpSession();
		return session;
	}

	public HttpSession getSession(boolean create) {
		if (!create)
    		return session;
		return getSession();
	}

	public Principal getUserPrincipal() {
		return null;
	}

	public boolean isRequestedSessionIdFromCookie() {
		return false;
	}

	public boolean isRequestedSessionIdFromURL() {
		return false;
	}

	public boolean isRequestedSessionIdFromUrl() {
		return false;
	}

	public boolean isRequestedSessionIdValid() {
		return false;
	}

	public boolean isUserInRole(String arg0) {
		return false;
	}

	public Object getAttribute(String key) {
		return attributes.get(key);
	}

	public Enumeration<String> getAttributeNames() {
		return Collections.enumeration(attributes.keySet());
	}

	private String getCharacterEncoding0() {
		String ct = getHeader("content-type");
		if (ct == null)
    		return null;
		String enc = ContentType.parse(ct).getParameterValue("encoding");
		return enc;
	}
	
	public String getCharacterEncoding() {
		return encoding;
	}

	public int getContentLength() {
		String cl = getHeader("content-length");
		if (cl == null)
			return -1;
		return Integer.parseInt(cl);
	}

	public String getContentType() {
		return getHeader("content-type");
	}

	public ServletInputStream getInputStream() throws IOException {
		return new ServletInputStream() {

			@Override
			public int read(byte[] b, int off, int len) throws IOException {
				return inputStream.read(b, off, len);
			}

			@Override
			public int read() throws IOException {
				return inputStream.read();
			}
			
		};
	}

	public String getLocalAddr() {
		return localAddr;
	}

	public String getLocalName() {
		return localName;
	}

	public int getLocalPort() {
		return localPort;
	}

	public Locale getLocale() {
		return Locale.getDefault();
	}

	public Enumeration<Locale> getLocales() {
		return Collections.enumeration(Collections.singleton(getLocale()));
	}

	public String getParameter(String key) {
		initParams();
		String[] sa = parameters.get(key);
		if (sa == null)
			return null;
		return sa[0];
	}

	public Map<String, String[]> getParameterMap() {
		initParams();
		return parameters;
	}

	public Enumeration<String> getParameterNames() {
		initParams();
		return Collections.enumeration(parameters.keySet());
	}

	private void initParams() {
		if (initParams)
			return;
		MessageHeaders headers = request.getHeaders();
		// TODO decide of headers should be read or not
		if (headers.contains(MessageHeader.MH_URL_ENCODED)) {
			try {
				parameters.putAll(HttpUtil.urlDecode(inputStream));
			} catch (IOException e) {
				throw new RuntimeException(e);
			}
		}
		initParams = true;
	}

	public String[] getParameterValues(String key) {
		return parameters.get(key);
	}

	public String getProtocol() {
		return "http";
	}

	public BufferedReader getReader() throws IOException {
		InputStreamReader isr = new InputStreamReader(inputStream, encoding);
		return new BufferedReader(isr);
	}

	public String getRealPath(String arg0) {
		return getURI();
	}

	public String getRemoteAddr() {
		return remoteAddr;
	}

	public String getRemoteHost() {
		return remoteHost;
	}

	public int getRemotePort() {
		return remotePort;
	}

	public RequestDispatcher getRequestDispatcher(String arg0) {
		throw new UnsupportedOperationException();
	}

	public String getScheme() {
		return "http";
	}

	public String getServerName() {
		return serverName;
	}

	public int getServerPort() {
		return serverPort;
	}

	public boolean isSecure() {
		return false;
	}

	public void removeAttribute(String key) {
		attributes.remove(key);
	}

	public void setAttribute(String key, Object value) {
		attributes.put(key, value);
	}

	public void setCharacterEncoding(String encoding)
			throws UnsupportedEncodingException {
		Charset.forName(encoding);
		this.encoding = encoding;
	}

	public String toString() {
		return super.toString() + " p=" + parameters;
	}
}
