#include <iostream>
#include <fstream>
#include <sstream>
#include <string>
#include <map>

#include <cstdio>
#include <cstdlib>
#include <cstring>

// #include <expat.h>
#include <curl/curl.h>

bool init_xml_query(std::string& xml, const char *query, const char *key);

extern "C" {
	size_t reader_fcn(void *ptr, size_t size, size_t nmemb, void *stream);
	size_t writer_fcn(void *ptr, size_t size, size_t nmemb, void *stream);
}

// typedef std::map<std::string, std::string> SearchResult;

std::string input_xml = "<?xml version='1.0' encoding='UTF-8'?>\n<SOAP-ENV:Envelope xmlns:SOAP-ENV=\"http://schemas.xmlsoap.org/soap/envelope/\" xmlns:xsi=\"http://www.w3.org/1999/XMLSchema-instance\" xmlns:xsd=\"http://www.w3.org/1999/XMLSchema\">\n<SOAP-ENV:Body>\n<ns1:doGoogleSearch xmlns:ns1=\"urn:GoogleSearch\" SOAP-ENV:encodingStyle=\"http://schemas.xmlsoap.org/soap/encoding/\">\n<key xsi:type=\"xsd:string\">SOAP_API_KEY</key>\n<q xsi:type=\"xsd:string\">GOOGLE_QUERY</q>\n<start xsi:type=\"xsd:int\">0</start>\n<maxResults xsi:type=\"xsd:int\">10</maxResults>\n<filter xsi:type=\"xsd:boolean\">true</filter>\n<restrict xsi:type=\"xsd:string\"></restrict>\n<safeSearch xsi:type=\"xsd:boolean\">false</safeSearch>\n<lr xsi:type=\"xsd:string\"></lr>\n<ie xsi:type=\"xsd:string\">latin1</ie>\n<oe xsi:type=\"xsd:string\">latin1</oe>\n</ns1:doGoogleSearch>\n</SOAP-ENV:Body>\n</SOAP-ENV:Envelope>";
std::string api_key = "1uLxMnpQFHIGgqomK6o82/ewswSPddlW", qry = "Ford XR8", host = "ford.com.au";
std::string results = "";

int main(int argc, char *argv[])
{
	std::string the_query = qry + " +site: " + host;

	if (! init_xml_query(input_xml, the_query.c_str(), api_key.c_str())) {
		std::cerr << "init_xml_query failed, bailing out" << std::endl;
		return (-2);
	}
	
	curl_global_init(CURL_GLOBAL_WIN32);
	CURL *hCurl = curl_easy_init();
	
	if (hCurl == NULL) {
		std::cerr << "Something went wrong with libcurl: curl_easy_init() == NULL" << std::endl;
		return (-1);
	}
	
	char content_length[64];
	_snprintf(content_length, 64, "Content-Length: %d", input_xml.size());

	curl_slist *http_headers = NULL;
	http_headers = curl_slist_append(http_headers, "Content-Type: text/xml; charset=utf-8");
	http_headers = curl_slist_append(http_headers, content_length);
	http_headers = curl_slist_append(http_headers, "SOAPAction: urn:GoogleSearchAction");

	curl_easy_setopt(hCurl, CURLOPT_HEADER, TRUE);
	curl_easy_setopt(hCurl, CURLOPT_POST, TRUE);
	curl_easy_setopt(hCurl, CURLOPT_READFUNCTION, reader_fcn);
	curl_easy_setopt(hCurl, CURLOPT_WRITEFUNCTION, writer_fcn);
	curl_easy_setopt(hCurl, CURLOPT_URL, "http://api.google.com/search/beta2");
	curl_easy_setopt(hCurl, CURLOPT_HTTPHEADER, http_headers);

	CURLcode rc = curl_easy_perform(hCurl);
	
	if (rc != CURLE_OK) {
		std::cerr << "CURL error: " << curl_easy_strerror(rc) << std::endl;
		// TODO: cleanup and exit gracefully
	}

	std::cout << "*** RETURN SOAP ENVELOPE:\n\n" << results << std::endl;
	
	curl_slist_free_all(http_headers);
	curl_easy_cleanup(hCurl);
	curl_global_cleanup();
}

size_t reader_fcn(void *ptr, size_t size, size_t nmemb, void *stream)
{
	if (input_xml.size() > size * nmemb) {
		std::cerr << "Attempting to send too much data: " << input_xml.size() << " bytes" << std::endl;
		return 0;
	}

	strncpy((char *)ptr, input_xml.c_str(), input_xml.size());
	return input_xml.size();
}

size_t writer_fcn(void *ptr, size_t size, size_t nmemb, void *stream)
{
	if (size * nmemb == 0)
		return 0;

	char *buff = new char[size * nmemb + 1];
	if (buff == (char *)0L) {
		std::cerr << "Failed to allocate " << size * nmemb + 1 << " bytes in writer_fcn" << std::endl;
		return 0;
	}

	strncpy(buff, (char *)ptr, size * nmemb);
	buff[size * nmemb] = 0;

	results = results + buff;
	delete[] buff;

	return size * nmemb;
}

bool init_xml_query(std::string& xml, const char *query, const char *key)
{
	std::string::size_type querypos, keypos;
	
	keypos = xml.find("SOAP_API_KEY");
	
	if (keypos == std::string::npos) {
		std::cerr << "Failed to find SOAP_API_KEY in xml file" << std::endl;
		return false;
	}
	
	xml.erase(keypos, strlen("SOAP_API_KEY"));
	xml.insert(keypos, key);

	querypos = xml.find("GOOGLE_QUERY");
	
	if (querypos == std::string::npos) {
		std::cerr << "Failed to find GOOGLE_QUERY in xml file" << std::endl;
		return false;
	}
	
	xml.erase(querypos, strlen("GOOGLE_QUERY"));
	xml.insert(querypos, query);
	
	/*
	std::cerr << "*** Dumping XML SOAP query to querytmp.xml" << std::endl;
	std::ofstream out("querytmp.xml");
	if (out.fail() || out.bad()) {
		std::cerr << "*** Oops: couldn't dump to file querytmp.xml" << std::endl;
	}
	else {
		out << xml;
	}
	out.close();
	*/

	return true;
}
