#include "api.h"
#include "../xml/obj_xml.h"
#include "api_auth.h"
#include "../nu/AutoChar.h"
#include "../jnetlib/api_httpget.h"
#include "ifc_authcallback.h"
#include <api/service/waservicefactory.h>
#include <strsafe.h>


static const GUID internetConfigGroupGUID =
{
	0xc0a565dc, 0xcfe, 0x405a, { 0xa2, 0x7c, 0x46, 0x8b, 0xc, 0x8a, 0x3a, 0x5c }
};


#define USER_AGENT_SIZE (10 /*User-Agent*/ + 2 /*: */ + 6 /*Winamp*/ + 1 /*/*/ + 1 /*5*/ + 3/*.55*/ + 1 /*Null*/)
static void SetUserAgent(api_httpreceiver *http)
{
	char user_agent[USER_AGENT_SIZE] = {0};
	StringCchPrintfA(user_agent, USER_AGENT_SIZE, "User-Agent: Winamp/%S", WASABI_API_APP->main_getVersionNumString());
	http->addheader(user_agent);
}


#define HTTP_BUFFER_SIZE 8192

static int FeedXMLHTTP(api_httpreceiver *http, obj_xml *parser, bool *noData)
{
	char downloadedData[HTTP_BUFFER_SIZE] = {0};
	int xmlResult = API_XML_SUCCESS; 
	int downloadSize = http->get_bytes(downloadedData, HTTP_BUFFER_SIZE);
	if (downloadSize)
	{
		xmlResult = parser->xmlreader_feed((void *)downloadedData, downloadSize);
		*noData=false;
	}
	else	
		*noData = true;

	return xmlResult;
}


static int RunXMLDownload(api_httpreceiver *http, obj_xml *parser, ifc_authcallback *callback)
{
	int ret;
	bool noData;
	do
	{
		if (callback && callback->OnIdle())
		{
			return AUTH_ABORT;
		}
		else if (!callback)
		{
			Sleep(50);
		}

		ret = http->run();
		if (FeedXMLHTTP(http, parser, &noData) != API_XML_SUCCESS)
			return AUTH_ERROR_PARSING_XML;
	}
	while (ret == HTTPRECEIVER_RUN_OK);

	// finish off the data
	do
	{
		if (FeedXMLHTTP(http, parser, &noData) != API_XML_SUCCESS)
			return AUTH_ERROR_PARSING_XML;
	} while (!noData);

	parser->xmlreader_feed(0, 0);
	if (ret != HTTPRECEIVER_RUN_ERROR)
		return AUTH_SUCCESS;
	else
		return AUTH_CONNECTIONRESET;
}


int PostXML(const char *url, const char *post_data, obj_xml *parser, ifc_authcallback *callback)
{
	if (!parser)
		return AUTH_NOPARSER;

	api_httpreceiver *http = 0;
	waServiceFactory *sf = WASABI_API_SVC->service_getServiceByGuid(httpreceiverGUID);
	if (sf)
		http = (api_httpreceiver *)sf->getInterface();

	if (!http)
		return AUTH_NOHTTP;

	int use_proxy = 1;
	bool proxy80 = AGAVE_API_CONFIG->GetBool(internetConfigGroupGUID, L"proxy80", false);
	if (proxy80 && strstr(url, ":") && (!strstr(url, ":80/") && strstr(url, ":80") != (url + strlen(url) - 3)))
		use_proxy = 0;

	const wchar_t *proxy = use_proxy?AGAVE_API_CONFIG->GetString(internetConfigGroupGUID, L"proxy", 0):0;
	size_t clen = strlen(post_data);

	http->open(API_DNS_AUTODNS, HTTP_BUFFER_SIZE, (proxy && proxy[0]) ? (const char *)AutoChar(proxy) : NULL);
	SetUserAgent(http);

	char clen_header[1024] = {0};
	StringCbPrintfA(clen_header, sizeof(clen_header), "Content-Length: %u", clen);
	http->addheader(clen_header);
	http->addheader("Content-Type: application/x-www-form-urlencoded; charset=UTF-8");
	if (callback && callback->OnConnecting())
	{
		sf->releaseInterface(http);
		return AUTH_ABORT;
	}
	http->connect(url, 0, "POST");

	// POST the data
	api_connection *connection = http->GetConnection();
	if (connection)
	{
		if (callback && callback->OnIdle())
		{
			sf->releaseInterface(http);
			return AUTH_ABORT;
		}
		else if (!callback)
		{
			Sleep(50);
		}

		if (http->run() == -1)
			goto connection_failed;

		if (callback && callback->OnSending())
		{
			sf->releaseInterface(http);
			return AUTH_ABORT;
		}

		const char *dataIndex = post_data;
		while (clen)
		{
			if (callback && callback->OnIdle())
			{
				sf->releaseInterface(http);
				return AUTH_ABORT;
			}
			else if (!callback)
			{
				Sleep(50);
			}

			if (http->run() == -1)
				goto connection_failed;

			size_t lengthToSend = min(clen, connection->GetSendBytesAvailable());
			if (lengthToSend)
			{
				connection->send(dataIndex, (int)lengthToSend);
				dataIndex+=lengthToSend;
				clen-=lengthToSend;
			}			
		}
	}

	// retrieve reply
	if (callback && callback->OnReceiving())
	{
		sf->releaseInterface(http);
		return AUTH_ABORT;
	}

	int ret;
	do
	{
		if (callback && callback->OnIdle())
		{
			sf->releaseInterface(http);
			return AUTH_ABORT;
		}
		else if (!callback)
		{
			Sleep(50);
		}

		ret = http->run();
		if (ret == -1) // connection failed
			break;

		// ---- check our reply code ----
		int status = http->get_status();
		switch (status)
		{
		case HTTPRECEIVER_STATUS_CONNECTING:
		case HTTPRECEIVER_STATUS_READING_HEADERS:
			break;

		case HTTPRECEIVER_STATUS_READING_CONTENT:
			{
				int downloadError;
				downloadError = RunXMLDownload(http, parser, callback);
				sf->releaseInterface(http);
				return downloadError;
			}
			break;
		case HTTPRECEIVER_STATUS_ERROR:
		default:
			sf->releaseInterface(http);
			return AUTH_404;
		}
	}
	while (ret == HTTPRECEIVER_RUN_OK);


connection_failed:
	sf->releaseInterface(http);
	return AUTH_404;
}