/* 
 * 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;

import java.io.BufferedInputStream;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.Writer;
import java.net.Socket;

/**
 * A very basic HTTP server implementation.
 *
 * @see BasicHttpClient
 */
public class BasicHttpServer
	implements HttpServer
{
	enum State {
    	INITIAL,
    	READ_REQUEST,
    	WROTE_RESPONSE,
    	WROTE_CONTINUE,
    	// GOT_OUTPUT = INITIAL;
    	CLOSED
	}

	private OutputStream os;
	private Writer writer;
	private InputStream is;
	private Socket socket;
	private State state;

	/**
	 * Constructs a BasicHttpServer that communicates over
	 * a socket.  The input stream is wrapped in a buffered
	 * input stream.
	 */
	public BasicHttpServer(Socket socket)
		throws IOException
	{
		this(socket.getOutputStream(), 
			new BufferedInputStream(socket.getInputStream()));
		this.socket = socket;
	}

	/**
	 * Constructs a BasicHttpServer that communicates over
	 * an input and output stream.
	 */
	public BasicHttpServer(OutputStream os, InputStream is) {
		if (os == null)
			throw new IllegalArgumentException("Null OutputStream");
		if (is == null)
			throw new IllegalArgumentException("Null InputStream");
		this.os = os;
		this.writer = new BufferedWriter(new OutputStreamWriter(os));
		this.is = is;
		this.state = State.INITIAL;
	}

	public ServerRequest readRequest()
		throws IOException
	{
		if (state != State.INITIAL && state != State.WROTE_RESPONSE)
			throw new IllegalHttpStateException("Invalid HTTP state");
		state = State.READ_REQUEST;
		return new ServerRequest(is);
	}

	public void writeResponse(ServerResponse response)
		throws IOException
	{
		if (state != State.READ_REQUEST && state != State.WROTE_CONTINUE)
			throw new IllegalHttpStateException("Invalid HTTP state");
		if (response == null)
			throw new IllegalArgumentException("Null ServerResponse");
		state = response.isContinue() ? State.WROTE_CONTINUE : State.WROTE_RESPONSE;
		writer.write(response.getStatusLine().toString());
		writer.write(HttpUtil.CRLF);
		response.getHeaders().write(writer);
		writer.write(HttpUtil.CRLF);
		writer.flush();
		if (response.getDataPoster() != null) {
			response.getDataPoster().sendData(getOutputStream());
		}
	}

	public OutputStream getOutputStream()
	{
		if (state != State.WROTE_RESPONSE)
			throw new IllegalHttpStateException("Invalid HTTP state " + state);
		state = State.INITIAL;
		return os;
	}

	/**
	 * Closes the underlying input and output streams.
	 */
	public void close()
		throws IOException
	{
		state = State.CLOSED;
		if (socket != null)
			socket.close();
		if (os != null)
			os.close();
		if (is != null)
			is.close();
		os = null;
		is = null;
	}

	/**
	 * Returns debug information.
	 */
	public String toString() {
		return "BasicHttpServer state=" + state + " socket=" + socket;
	}

}
