/*
 * Copyright (c) 2008, 2009, 2010, 2011
 *   Jonathan Schleifer <js@webkeks.org>
 *
 * All rights reserved.
 *
 * This file is part of ObjFW. It may be distributed under the terms of the
 * Q Public License 1.0, which can be found in the file LICENSE.QPL included in
 * the packaging of this file.
 *
 * Alternatively, it may be distributed under the terms of the GNU General
 * Public License, either version 2 or 3, which can be found in the file
 * LICENSE.GPLv2 or LICENSE.GPLv3 respectively included in the packaging of this
 * file.
 */

#import "OFObject.h"

@class OFString;
@class OFDictionary;
@class OFURL;
@class OFHTTPRequest;
@class OFHTTPRequestResult;
@class OFDataArray;

typedef enum of_http_request_type_t {
	OF_HTTP_REQUEST_TYPE_GET,
	OF_HTTP_REQUEST_TYPE_POST,
	OF_HTTP_REQUEST_TYPE_HEAD
} of_http_request_type_t;

/**
 * \brief A delegate for OFHTTPRequests.
 */
#ifndef OF_HTTP_REQUEST_M
@protocol OFHTTPRequestDelegate <OFObject>
#else
@protocol OFHTTPRequestDelegate
#endif
#ifdef OF_HAVE_OPTIONAL_PROTOCOLS
@optional
#endif
/**
 * This callback is called when the OFHTTPRequest received the headers.
 *
 * \param request The OFHTTPRequest which received the headers
 * \param headers The headers received
 * \param statusCode The status code received
 */
-     (void)request: (OFHTTPRequest*)request
  didReceiveHeaders: (OFDictionary*)headers
     withStatusCode: (int)statusCode;

/**
 * This callback is called when the OFHTTPRequest received data.
 *
 * This is useful for example if you want to update a status display.
 *
 * \param request The OFHTTPRequest which received data
 * \param data The data the OFHTTPRequest received
 * \param len The length of the data received, in bytes
 */
-  (void)request: (OFHTTPRequest*)request
  didReceiveData: (const char*)data
      withLength: (size_t)len;

/**
 * This callback is called when the OFHTTPRequest will follow a redirect.
 *
 * If you want to get the headers and data for each redirect, set the number of
 * redirects to 0 and perform a new OFHTTPRequest for each redirect. However,
 * this callback will not be called then and you have to look at the status code
 * to detect a redirect.
 *
 * This callback will only be called if the OFHTTPRequest will follow a
 * redirect. If the maximum number of redirects has been reached already, this
 * callback will not be called.
 *
 * \param request The OFHTTPRequest which will follow a redirect
 * \param url The URL to which it will follow a redirect
 * \return A boolean whether the OFHTTPRequest should follow the redirect
 */
-	 (BOOL)request: (OFHTTPRequest*)request
  willFollowRedirectTo: (OFURL*)url;
@end

/**
 * \brief A class for storing and performing HTTP requests.
 */
@interface OFHTTPRequest: OFObject
{
	OFURL *URL;
	of_http_request_type_t requestType;
	OFString *queryString;
	OFDictionary *headers;
	BOOL redirectsFromHTTPSToHTTPAllowed;
	id <OFHTTPRequestDelegate> delegate;
}

#ifdef OF_HAVE_PROPERTIES
@property (copy) OFURL *URL;
@property (assign) of_http_request_type_t requestType;
@property (copy) OFString *queryString;
@property (copy) OFDictionary *headers;
@property (assign) BOOL redirectsFromHTTPSToHTTPAllowed;
@property (retain) id <OFHTTPRequestDelegate> delegate;
#endif

/**
 * \return A new, autoreleased OFHTTPRequest
 */
+ request;

/**
 * \param url The URL for the request
 * \return A new, autoreleased OFHTTPRequest
 */
+ requestWithURL: (OFURL*)url;

/**
 * Initializes an already allocated OFHTTPRequest with the specified URL.
 *
 * \param url The URL for the request
 * \return An initialized OFHTTPRequest
 */
- initWithURL: (OFURL*)url;

/**
 * Sets the URL for the HTTP request.
 *
 * \param URL The URL for the HTTP request
 */
- (void)setURL: (OFURL*)url;

/**
 * \return The URL for the HTTP request
 */
- (OFURL*)URL;

/**
 * Sets the request type for the HTTP request.
 *
 * \param type The request type for the HTTP request
 */
- (void)setRequestType: (of_http_request_type_t)type;

/**
 * \return The request type for the HTTP request
 */
- (of_http_request_type_t)requestType;

/**
 * Sets the query string for the HTTP request.
 *
 * \param qs The query string for the HTTP request
 */
- (void)setQueryString: (OFString*)qs;

/**
 * \return The query string for the HTTP request
 */
- (OFString*)queryString;

/**
 * Sets a dictionary with headers for the HTTP request.
 *
 * \param headers A dictionary with headers for the HTTP request
 */
- (void)setHeaders: (OFDictionary*)headers;

/**
 * \return A dictionary with headers for the HTTP request.
 */
- (OFDictionary*)headers;

/**
 * Sets whether redirects from HTTPS to HTTP are allowed.
 *
 * \param allowed Whether redirects from HTTPS to HTTP are allowed
 */
- (void)setRedirectsFromHTTPSToHTTPAllowed: (BOOL)allowed;

/**
 * \return Whether redirects from HTTPS to HTTP are allowed
 */
- (BOOL)redirectsFromHTTPSToHTTPAllowed;

/**
 * Sets the delegate for the HTTP request.
 *
 * \param delegate The delegate for the HTTP request
 */
- (void)setDelegate: (id <OFHTTPRequestDelegate>)delegate;

/**
 * \return The delegate for the HTTP request.
 */
- (id <OFHTTPRequestDelegate>)delegate;

/**
 * Performs the HTTP request and returns an OFHTTPRequestResult.
 *
 * \return An OFHTTPRequestResult with the result of the HTTP request
 */
- (OFHTTPRequestResult*)perform;

/**
 * Performs the HTTP request and returns an OFHTTPRequestResult.
 *
 * \param redirects The maximum number of redirects after which no further
 *		    attempt is done to follow the redirect, but instead the
 *		    redirect is returned as an OFHTTPRequest
 * \return An OFHTTPRequestResult with the result of the HTTP request
 */
- (OFHTTPRequestResult*)performWithRedirects: (size_t)redirects;
@end

/**
 * \brief A class for storing the result of an HTTP request.
 */
@interface OFHTTPRequestResult: OFObject
{
	short statusCode;
	OFDataArray *data;
	OFDictionary *headers;
}

#ifdef OF_HAVE_PROPERTIES
@property (readonly) short statusCode;
@property (readonly, copy) OFDictionary *headers;
@property (readonly, retain) OFDataArray *data;
#endif

/// \cond internal
- initWithStatusCode: (short)status
	     headers: (OFDictionary*)headers
		data: (OFDataArray*)data;
/// \endcond

/**
 * \return The status code of the result of the HTTP request
 */
- (short)statusCode;

/**
 * \return The HTTP headers of the result of the HTTP request
 */
- (OFDictionary*)headers;

/**
 * \return The data returned for the HTTP request
 */
- (OFDataArray*)data;
@end

@interface OFObject (OFHTTPRequestDelegate) <OFHTTPRequestDelegate>
@end

extern Class of_http_request_tls_socket_class;
