#pragma once

#include "ehs/EHS.h"
#include "ehs/Vector.h"
#include "ehs/Str.h"
#include "ehs/json/Json.h"
#include "Socket.h"

namespace ehs
{
	enum class Verb
	{
		POST,
		GET,
		PUT,
		DEL
	};

	class EHS_LIB_IO Request
	{
	private:
		Verb verb;
		Str_8 rsrc;
		Vector<Str_8> queries;
		Vector<Str_8> header;
		ContentType cType;
		Str_8 body;
	
	public:
	    /// Default member initialization.
		Request();

	    /// Initializes this request with a given verb and URI resource.
	    /// @param [in] verb The type of request to make.
	    /// @param [in] rsrc The URI endpoint to make the request at.
		Request(const Verb verb, const Str_8& rsrc);

		/// Initializes this request with the raw request data.
		/// @param [in] data The C-style string of the request.
		/// @param [in] size The size of the given C-style string.
		Request(const char* data, const UInt_64 size);

        /// Initializes this request with the raw request data.
        /// @param [in] data The string of the request.
		Request(const Str_8& data);

		/// Copies members from another object of the same type.
		/// @param [in] req The object to copy from.
		Request(const Request& req) = default;

        /// Copies members from another object of the same type.
        /// @param [in] req The object to copy from.
        /// @returns The request that has been assigned to.
		Request& operator=(const Request& req);

		/// Retrieves the verb for the request.
		/// @returns The result.
		Verb GetVerb() const;

		/// Sets the content type for the body.
		/// @param [in] cType The content type to use.
		void SetContentType(const ContentType cType);

		/// Retrieves the content type for the body.
		/// @returns The result.
		ContentType GetContentType() const;

		/// Sets the URI resource.
		/// @param [in] rsrc The resource.
		void SetResource(const Str_8& rsrc);

		/// Retrieves the URI resource.
		/// @returns The result.
		Str_8 GetResource() const;

		/// Adds a query variable to the URI.
		/// @param [in] var The variable identifier.
		/// @param [in] value The value of the variable.
		void AddQuery(const Str_8& var, const Str_8& value);

		/// Retrieves a query variable from the URI.
		/// @param [in] var The variable identifier to look for.
		/// @returns The value of the query variable. Empty if it was not found.
		Str_8 GetQuery(const Str_8& var);

		/// Retrieves all the query variables from the URI in a vector object.
		/// @returns The result.
		Vector<Str_8> GetQueries() const;

		/// A helper method to automatically add the required header variables for basic authentication.
		/// @param [in] id The username or id.
		/// @param [in] secret The secret given by an API.
		void BasicAuth(const Str_8& id, const Str_8& secret);

        /// A helper method to automatically add the required header variables for bearer authentication.
        /// @param [in] token The token given by an API.
		void BearerAuth(const Str_8& token);

        /// A helper method to automatically add the required header variables for bearer authentication.
        /// @param [in] token The token given by an API.
        /// @param [in] clientId The client id given by an API.
        void BearerAuth(const Str_8& token, const Str_8& clientId);

        /// A helper method to automatically add the required header variables for bot authentication.
        /// @param [in] token The token given by an API.
		void BotAuth(const Str_8& token);

        /// Adds a header variable.
        /// @param [in] var The variable identifier.
        /// @param [in] value The value of the variable.
		void AddToHeader(const Str_8& var, const Str_8& value);

        /// Retrieves a header variable.
        /// @param [in] var The variable identifier to look for.
        /// @returns The value of the header variable. Empty if it was not found.
		Str_8 GetHeader(const Str_8& var) const;

        /// Retrieves all the header variables in a vector object.
        /// @returns The result.
		Vector<Str_8> GetHeader() const;

        /// Adds a body variable.
        /// @param [in] var The variable identifier.
        /// @param [in] value The value of the variable.
		void AddToBody(const Str_8& var, const Str_8& value);

        /// Adds a value to the body.
        /// @param [in] data The value to add.
		void AddToBody(const Str_8& data);

		/// Sets the entire body.
		/// @param [in] body The body to use.
		void SetBody(const Str_8& body);

        /// Retrieves a body variable.
        /// @param [in] var The variable identifier to look for.
        /// @returns The value of the body variable. Empty if it was not found.
		Str_8 GetVar(const Str_8& var) const;

		/// Retrieves the entire body.
		/// @returns The result.
		Str_8 GetBody() const;

		/// Retrieves the entire body as a Json.
		/// @returns The result.
		Json GetJson() const;

		/// Forms the raw result of the request to be sent.
		/// @returns The result.
		Str_8 FormResult() const;

		bool IsValid() const;
		
	private:
		static Str_8 VerbToStr(const Verb verb);
		
		static Str_8 ContentTypeToStr(const ContentType cType);

		static ContentType StrToContentType(const Str_8& value);

		void ReadData(const Str_8& data);
		
	};
}