/* 
 * 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.util.NoSuchElementException;
import java.util.StringTokenizer;

/**
 * This is a immutable implementation of the <code>StatusLine</code> interface.
 */
public class StatusLine
{
	/**
	 * Basic Continue message from an HTTP/1.1 server.
	 */
	public static final StatusLine HTTP11_100 = new StatusLine(HttpVersion.HTTP11, 100, "Continue");

	/**
	 * Basic OK message from an HTTP/1.1 server.
	 */
	public static final StatusLine HTTP11_200_OK = new StatusLine(HttpVersion.HTTP11, 200, "OK");

	/**
	 * Basic 204 message from an HTTP/1.1 server.
	 */
	public static final StatusLine HTTP11_204 = new StatusLine(HttpVersion.HTTP11, 204, "No Content");

	/**
	 * Basic 301 message from an HTTP/1.1 server.
	 */
	public static final StatusLine HTTP11_301 = new StatusLine(HttpVersion.HTTP11, 301, "Moved Permanently");

	/**
	 * Basic 404 message from an HTTP/1.1 server.
	 */
	public static final StatusLine HTTP11_404 = new StatusLine(HttpVersion.HTTP11, 404, "Not Found");

	private HttpVersion version;
	private int statusCode;
	private String reasonPhrase;
	/** Cached string value for this object */
	private String toString;

	/**
	 * Constructs this object using a status code, HTTP version 1.1, and blank reason.
	 * @param statusCode must be in the range 0 to 999
	 */
	public StatusLine(int statusCode)
	{
		this(HttpVersion.HTTP11, statusCode, "");
	}

	/**
	 * Constructs this object using <code>Status-Line<code> parts.
	 * @param version may not be null
	 * @param statusCode must be in the range 0 to 999
	 * @param reasonPhrase may not be null
	 */
	public StatusLine(HttpVersion version, int statusCode, String reasonPhrase)
	{
		if (version == null)
			throw new IllegalArgumentException("Null version");
		if (reasonPhrase == null)
			throw new IllegalArgumentException("Null reason");
		if (statusCode < 0 || statusCode > 999)
			throw new IllegalArgumentException("Invalid range for status code");
		this.version = version;
		this.statusCode = statusCode;
		this.reasonPhrase = reasonPhrase;
	}

	/**
	 * Constructs a <code>StatusLineImpl</code> using an unparsed request
	 * line.  This string should not end in <code>CRLF</code>.
	 * @throws HttpException if an invalid HTTP Request-Line was used
	 * in initialization
	 */
	public StatusLine(String line)
		throws HttpException
	{
		if (line == null)
			throw new IllegalArgumentException("Null status line");
		StringTokenizer st = new StringTokenizer(line, " ");
		try {
			version = HttpVersion.parseVersion(st.nextToken());
			statusCode = Integer.parseInt(st.nextToken());
			if (statusCode < 0 || statusCode > 999)
				throw new HttpException("Invalid status number: " + statusCode);
			reasonPhrase = st.nextToken("").trim();
		} catch (NoSuchElementException e) {
			throw new HttpException("Invalid status line: " + line); 
		}
	}

	/**
	 * Constructs this object using <code>Status-Line<code> parts.
	 * @param statusCode must be in the range 0 to 999
	 * @param reasonPhrase may not be null
	 */
	public StatusLine(int statusCode, String statusReason) {
		this(HttpVersion.HTTP11, statusCode, statusReason);
	}

	/**
	 * Returns either {@link StatusLine#HTTP11_200_OK} or a newly
	 * constructed <code>StatusLine</code> object.
	 */
	public static StatusLine parseStatusLine(String line)
		throws HttpException
	{
		if (line == null)
			throw new IllegalArgumentException("Null status line");
		if (line.equals(HTTP11_200_OK.toString()))
			return HTTP11_200_OK;
		return new StatusLine(line);
	}

	/**
	 * Returns the status' HTTP version.
	 */
	public HttpVersion getHttpVersion() {
		return version;
	}

	/**
	 * Returns the three-digit status code.
	 */
	public int getStatusCode() {
		return statusCode;
	}

	/**
	 * Returns the status reason phrase.
	 */
	public String getReasonPhrase() {
		return reasonPhrase;
	}

	/**
	 * Returns this <code>StatusLine</code> as:
	 * <pre>
	 * getHttpVersion() + ' ' + getStatusCode() + ' ' + getReasonPhrase()
	 * </pre>
	 * Note:  Does not include <code>CRLF</code>.
	 */
	public String toString() {
		if (toString == null)
			toString = getHttpVersion().toString() + ' ' + getStatusCode() + ' ' + getReasonPhrase();
		return toString;
	}
}
