#pragma once

#include "twitchsdk/core/httprequest.h"

namespace TwitchInGames {
	int const defaultTimeoutInSeconds= 10;

	using ttv::HttpRequestType;

	struct HttpParam {
		HttpParam(tstring const& name, tstring const& value);
		HttpParam(tstring const& name, int value);

		tstring paramName;
		tstring paramValue;
	};

	typedef std::function<bool(unsigned statusCode, std::map<tstring, tstring> const& headers, void* userData)> HttpRequestHeadersCallback;
	typedef std::function<void(unsigned statusCode, std::vector<char> const& body, void* userData)> HttpRequestCallback;

	/**
	* Determines if the given url represents a Twitch endpoint.
	*/
	bool IsTwitchEndpoint(string_t url);

	class HttpResponse {
	public:
		~HttpResponse();
		int GetResult() const { return result; }
		std::map<tstring, tstring> const& GetHeaders() const;
		std::vector<char> const& GetResponse() const;
		int GetResultCode() const;
		static bool IsSuccessful(unsigned statusCode) { return statusCode >= 200 && statusCode < 300; }

		__declspec(property(get=GetResult)) int const Result;
		__declspec(property(get=GetHeaders)) std::map<tstring, tstring> const& Headers;
		__declspec(property(get=GetResponse)) std::vector<char> const& Response;
		__declspec(property(get=GetResultCode)) int const ResultCode;

		HttpResponse()= delete;
		HttpResponse(HttpResponse const&)= delete;
		HttpResponse(HttpResponse&&)= default;
		HttpResponse& operator=(HttpResponse const&)= delete;
		HttpResponse& operator=(HttpResponse&&)= default;

	private:
		friend class HttpRequest;

		std::map<tstring, tstring> headers;
		std::vector<char> response;
		int resultCode;
		int result;
		uintptr_t handle;

		HttpResponse(int result, uintptr_t handle);
		static int PlatformGetHttpResponseResultCode(uintptr_t handle);
		static std::tuple<int, std::map<tstring, tstring>> PlatformGetHttpResponseHeaders(uintptr_t handle);
		static std::tuple<int, std::vector<char>> PlatformGetHttpResponse(uintptr_t handle);
		static void PlatformFinishHttpRequest(uintptr_t& handle);
	};

	class HttpRequest {
	public:
		HttpRequest(tstring const& clientId= EmptyString, tstring const& token= EmptyString);
		~HttpRequest();

		HttpResponse Get(tstring const& url);
		HttpResponse Post(tstring const& url, std::vector<char> const& requestBody= EmptyBody);
		HttpResponse Put(tstring const& url, std::vector<char> const& requestBody= EmptyBody);
		tstring const& GetClientId() const { return clientId; }
		void SetClientId(tstring const& clientId_) { clientId= clientId_; }
		tstring const& GetToken() const { return token; }
		void SetToken(tstring const& token_) { token= token_; }
		int GetTimeout() const { return timeout; }
		void SetTimeout(int timeout_) { timeout= timeout_; }

		__declspec(property(get=GetClientId, put=SetClientId)) tstring const& ClientId;
		__declspec(property(get=GetToken, put=SetToken)) tstring const& Token;
		__declspec(property(get=GetTimeout, put=SetTimeout)) int Timeout;

		static std::vector<char> const EmptyBody;
		static tstring const EmptyString;
		static int const DefaultTimeout= 10; // seconds

	private:
		tstring clientId;
		tstring token;
		int timeout;

		std::tuple<int, uintptr_t> StartHttpRequest(tstring const& url, HttpRequestType requestType,
			std::vector<char> const& requestBody);
		static std::tuple<int, uintptr_t> PlatformStartHttpRequest(string_t url, HttpRequestType requestType,
			unsigned timeout, std::vector<HttpParam> const& requestHeaders, std::vector<char> const& requestBody);
	};
}
