//  This software code is made available "AS IS" without warranties of any
//  kind.  You may copy, display, modify and redistribute the software
//  code either by itself or as incorporated into your code; provided that
//  you do not remove any proprietary notices.  Your use of this software
//  code is at your own risk and you waive any claim against Amazon
//  Digital Services, Inc. or its affiliates with respect to your use of
//  this software code. (c) 2006 Amazon Digital Services, Inc. or its
//  affiliates.

package net.noderunner.amazon.s3;

import java.io.IOException;
import java.io.InputStream;

import org.apache.commons.httpclient.Header;
import org.apache.commons.httpclient.HttpMethod;
import org.xml.sax.ContentHandler;
import org.xml.sax.ErrorHandler;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
import org.xml.sax.SAXParseException;
import org.xml.sax.XMLReader;
import org.xml.sax.helpers.XMLReaderFactory;

/**
 * The parent class of all other Responses. <p/> Returns status codes that
 * should be checked.
 */
public class Response {

	private HttpMethod method;
	
	Response(HttpMethod method) {
		this.method = method;
	}

	/**
	 * Returns the HTTP response code.
	 */
	public int getResponseCode() throws IOException {
		return method.getStatusCode();
	}

	/**
	 * Returns the HTTP response message.
	 */
	public String getResponseMessage() throws IOException {
		return method.getStatusText();
	}

	/**
	 * Returns content length of the response.
	 */
	public int getContentLength() {
		return -1; // TODO
	}

	/**
	 * Throws an IllegalStateException if not 200-level OK.
	 */
	public void assertOk() {
        if (isOk())
            return;
        String msg;
		try {
			msg = method.getResponseBodyAsString();
		} catch (IOException e) {
			msg = "?";
		}
        throw new IllegalStateException("Unexpected response: " + this + " Message: " + msg);
	}
	
	/**
	 * Returns an HTTP header field from the response.
	 */
	public String getHeaderField(String field) {
		Header header = method.getResponseHeader(field);
		if (header == null)
			return null;
		return header.getValue();
	}

	/**
	 * Returns all HTTP headers.
	 * Prefer caching the return value.
	 */
	public Headers getHeaders() {
		return new Headers(method.getResponseHeaders());
	}

	/**
	 * Returns true if processing was 200-level OK.
	 */
	public boolean isOk() {
		try {
			int code = getResponseCode();
			return code == 200 || code == 204;
		} catch (IOException e) {
			return false;
		}
	}

	/**
	 * Returns true if processing returned 404.
	 */
	public boolean isNotFound() {
		try {
			return getResponseCode() == 404;
		} catch (IOException e) {
			return false;
		}
	}

	/**
	 * Returns a string representation.
	 */
	@Override
	public String toString() {
		try {
			return "Response code=" + getResponseCode() + " msg='"
					+ getResponseMessage() + "'";
		} catch (IOException e) {
			throw new RuntimeException(e);
		}
	}

	/**
	 * Parses the response.
	 */
	protected void parse(ContentHandler handler) throws IOException {
		try {
			XMLReader xr = createXMLReader();
			xr.setContentHandler(handler);
			ErrorHandler eh = new ErrorHandler() {

				public void error(SAXParseException e) throws SAXException {
					throw e;
				}

				public void fatalError(SAXParseException e) throws SAXException {
					throw e;
				}

				public void warning(SAXParseException e) throws SAXException {
					throw e;
				}

			};
			xr.setErrorHandler(eh);
			InputStream is = method.getResponseBodyAsStream();
			xr.parse(new InputSource(is));
			is.close();
		} catch (SAXException e) {
			throw new RuntimeException("Unexpected error parsing ListBucket xml", e);
		}
	}
	
	static XMLReader createXMLReader() {
		try {
			return XMLReaderFactory.createXMLReader();
		} catch (SAXException e) {
			// oops, lets try doing this (needed in 1.4)
			System.setProperty("org.xml.sax.driver",
					"org.apache.crimson.parser.XMLReaderImpl");
		}
		try {
			// try once more
			return XMLReaderFactory.createXMLReader();
		} catch (SAXException e) {
			throw new RuntimeException(
					"Couldn't initialize a sax driver for the XMLReader");
		}
	}

}
