/**
 * @file
 *
 */
#ifndef UI_UTIL_HTTP_HEADER_HPP
#define UI_UTIL_HTTP_HEADER_HPP

// STDC++
#include <string>
#include <vector>
#include <map>

// C++ libraries
#include <ui-utilcpp/Exception.hpp>
#include <ui-utilcpp/Text.hpp>

namespace UI {
namespace Util {
/** @brief Namespace for all HTTP related code. */
namespace Http {

/** @brief Exception for Http namespace. */
class Exception: public Util::Exception
{
public:
	Exception(std::string const & what=NoWhatGiven_, std::string const & debug=NoDebugGiven_);
};

/** @brief Helper to verify a version string. */
bool verifyVersion(std::string const & version, bool const & doThrow=true);

/** @brief Parse and represent a request's request line. @see RFC 2616, 5.1.
 *  @see RFC 2616, 5.1.
 *
 *  Example request lines: "POST / HTTP/1.1", "GET /humbug&a=auchHumbug HTTP/1.1".
 */
class RequestLine
{
public:
	/** @name HTTP 1.1 methods and string mappings.
	 *
	 * @note Be sure to always update both, Method and generateMethodMap().
	 */
	enum Method
	{
		Options_,
		Get_,
		Head_,
		Post_,
		Put_,
		Delete_,
		Trace_,
		Connect_
	};
private:
	typedef std::map<Method, std::string> MethodMap;
	static MethodMap generateMethodMap();
	static MethodMap const MethodMap_;

public:
	static Method str2Method(std::string const & method);

	/** @brief Construct from components. */
	RequestLine(Method const & method=Post_, std::string const & uri="/", std::string const & version="HTTP/1.1");
	/** @brief Construct from composition (parse). */
	RequestLine(std::string const & line);

	/** @name Set (and validate) components.
	 * @{ */
	RequestLine & setMethod(Method const & method);
	RequestLine & setMethod(std::string const & method);
	RequestLine & setURI(std::string const & uri);
	RequestLine & setVersion(std::string const & version);
	/** @} */

	/** @name Get components.
	 * @{ */
	Method      const & getMethod()    const;
	std::string const & getMethodStr() const;
	std::string const & getURI()       const;
	std::string const & getVersion()   const;
	/** @} */

	/** @brief Get composition. */
	std::string get() const;

private:
	Method      method_;
	std::string uri_;
	std::string version_;
};


/** @brief Parse and represent a response's status line.
 *  @see RFC 2616, 6.1.
 *
 *  Example status line: "HTTP/1.1 550 Internal Server Error"
 */
class StatusLine
{
public:
	typedef unsigned int Code;

	/** @brief Construct from components. */
	StatusLine(Code const & code=200, std::string const & reason="OK", std::string const & version="HTTP/1.1");

	/** @brief Construct from composition (parse). */
	StatusLine(std::string const & line);

	/** @name Set (and validate) components.
	 * @{ */
	StatusLine & setCode(Code const & code);
	StatusLine & setCode(std::string const & code);
	StatusLine & setReason(std::string const & reason);
	StatusLine & setVersion(std::string const & version);
	/** @} */

	/** @name Get components.
	 * @{ */
	Code        const & getCode()    const;
	std::string         getCodeStr() const;
	std::string const & getReason()  const;
	std::string const & getVersion() const;
	/** @} */

	/** @brief Get composition. */
	std::string get() const;

private:
	Code code_;
	std::string reason_;
	std::string version_;
};

/** @brief Header field.
 *
 *  Example header field: "ContentLength: 1234"
 */
class HeaderField: private std::pair<std::string, std::string>
{
public:
	/** @brief Construct from components. */
	HeaderField(std::string const & name, std::string const & value);

	/** @brief Construct from composition (parse). */
	HeaderField(std::string const & line);

	/** @name Get components.
	 * @{ */
	std::string const & getName()  const;
	std::string const & getValue() const;
	/** @} */

	/** @brief Get composition. */
	std::string get()  const;
};

/** @brief Array of header fields. */
class Header: public std::vector<HeaderField>
{
public:
	const_iterator find(std::string const & name) const;
	bool exists(std::string const & name) const;
	std::string const & get(std::string const & name, bool doThrow=true) const;
	template <typename I>
	I get(std::string const & name, bool doThrow=true) const
	{
		return ato<I>(get(name, doThrow));
	}
	Header & add(std::string const & key, std::string const & value);
	Header & add(std::string const & line);
	Header getMulti(std::string const & name) const;
	Header getPrefix(std::string const & prefix) const;

	/** @brief Get composition. */
	std::string get() const;
};

}}}
#endif
