#include "HWOcrClientToken.h"
#include <curl/curl.h>
#include <iostream>
#include "utils.h"

typedef struct {
	std::string m_token;
} GET_TOKEN_RESPONSE_HEADERS;

struct MemoryStruct {
	char* memory;
	size_t size;
};

//@brief:callback function, save the info of response
static size_t WriteMemoryCallback(void* contents, size_t size, size_t nmemb, void* userp)
{
	size_t realsize = size * nmemb;
	struct MemoryStruct* mem = (struct MemoryStruct*)userp;

	mem->memory = (char*)realloc(mem->memory, mem->size + realsize + 1);
	if (mem->memory == NULL) {
		/* out of memory! */
		printf("not enough memory (realloc returned NULL)\n");
		return 0;
	}

	memcpy(&(mem->memory[mem->size]), contents, realsize);
	mem->size += realsize;
	mem->memory[mem->size] = 0;

	return realsize;
}

size_t OnWriteGetTokenResponseBody(void *buffer, size_t size, size_t nmemb, void *callBackData) {
	return size * nmemb;
}

size_t OnWriteGetTokenResponseHeaders(void *buffer, size_t size, size_t nmemb, void *callBackData) {
	GET_TOKEN_RESPONSE_HEADERS *responseHeaders = (GET_TOKEN_RESPONSE_HEADERS *)callBackData;
	std::string header = static_cast<const char *>(buffer);
	std::string headerKey = "X-Subject-Token: ";
	if (header.find(headerKey) == 0) {
		size_t headerKeyLength = strlen(headerKey.c_str());
		responseHeaders->m_token = header.substr(headerKeyLength, strlen(header.c_str()) - headerKeyLength - 2);
	}
	return size * nmemb;
}

HWOcrClientToken::HWOcrClientToken() {
	m_token = "";
	m_username = "";
	m_password = "";
	m_region = "";
	m_domainname = "";
}

HWOcrClientToken::~HWOcrClientToken() {
}

void HWOcrClientToken::init(std::string domainname, std::string username, std::string password, std::string region) {
	m_domainname = domainname;
	m_username = username;
	m_password = password;
	m_region = region;
}


void HWOcrClientToken::get_token() {
	if (m_token != "") {
		std::cout << "m_token is not null."<< std::endl;
		return;
	}
	std::string endpoint = "";
	endpoint = "iam.";
	endpoint += m_region;
	endpoint += ".myhuaweicloud.com";
	std::cout << "endpoint: " << endpoint << std::endl;

	std::string url = "";
	url = "https://";
	url += endpoint;
	url += "/v3/auth/tokens";
	std::cout << "URL: " << url << std::endl;

	std::string requestBody = "";
	requestBody +=
		"{\"auth\": {\"identity\": {\"password\": {\"user\": {\"domain\": {\"name\": \"";
	requestBody += m_domainname;
	requestBody += "\"},\"name\": \"";
	requestBody += m_username;
	requestBody += "\",\"password\": \"";
	requestBody += m_password;
	requestBody += "\"}},\"methods\": [\"password\"]},\"scope\": {\"project\": {\"name\": \"";
	requestBody += m_region;
	requestBody += "\"}}}}";
	//std::cout << "requestBody: " << requestBody << std::endl;

	std::string responseBody = "";
	GET_TOKEN_RESPONSE_HEADERS responseHeaders = { };

	for (int i = 0; i < 3; i++) {

		CURL* pCurl = NULL;
		pCurl = curl_easy_init();
		if (NULL != pCurl) {

			// Set the out of time is 1s
			curl_easy_setopt(pCurl, CURLOPT_TIMEOUT, 15);
			curl_easy_setopt(pCurl, CURLOPT_POST, 1);
			curl_easy_setopt(pCurl, CURLOPT_URL, url.c_str());

			struct curl_slist* pHeaders = NULL;
			pHeaders = curl_slist_append(pHeaders, "Content-Type:application/json");

			// Set the send request content type is JSON
			curl_easy_setopt(pCurl, CURLOPT_HTTPHEADER, pHeaders);

			// Set the JSON data for POST
			curl_easy_setopt(pCurl, CURLOPT_POSTFIELDS, requestBody.c_str());
			curl_easy_setopt(pCurl, CURLOPT_WRITEDATA, (void*)& responseBody);
			curl_easy_setopt(pCurl, CURLOPT_WRITEFUNCTION, OnWriteGetTokenResponseBody);
			curl_easy_setopt(pCurl, CURLOPT_HEADERFUNCTION, OnWriteGetTokenResponseHeaders);
			curl_easy_setopt(pCurl, CURLOPT_HEADERDATA, &responseHeaders);

			// Perform the request, res will get the return code
			CURLcode status = curl_easy_perform(pCurl);
			// Check for errors
			if (status != CURLE_OK) {
				std::cout << " get_token curl_easy_perform() failed:"
					<< status << "," << curl_easy_strerror(status) << std::endl;
			}
			else {
				m_token = responseHeaders.m_token.c_str();
				std::cout << "get token success" << std::endl;
				break;
			}
			// always cleanup
			curl_easy_cleanup(pCurl);
		}
	}

	

}

std::string HWOcrClientToken::ocr_post(std::string imgPath, std::string reqUrl, Json::Value jsonOption)
{
	get_token();

	std::string strRespData = "";
	std::ostringstream ostrFormate;

	std::string::size_type idx = std::string::npos, idx2 = std::string::npos;
	std::string http = "http://";
	std::string https = "https://";
	idx = imgPath.find(http);
	idx2 = imgPath.find(https);
	if (idx != std::string::npos || idx2 != std::string::npos) {
		jsonOption["url"] = Json::Value(imgPath);
	}
	else {
		std::ifstream file(imgPath.c_str(), std::ios::binary);
		std::vector<unsigned char> imdata;

		file >> std::noskipws;
		copy(std::istream_iterator<unsigned char>(file), std::istream_iterator<unsigned char>(), back_inserter(imdata));
		CBase64 base64;
		std::string imgBase64 = base64.Encode(imdata.data(), imdata.size());
		jsonOption["image"] = Json::Value(imgBase64);
	}

	std::string url = "";
	url = "https://ocr.";
	url += m_region;
	url += ".myhuaweicloud.com";
	url += reqUrl;
	std::cout << "POST URL: " << url << std::endl;

	try
	{
		CURL *pCurl = NULL;
		CURLcode res;
		// In windows, this will init the winsock stuff
		curl_global_init(CURL_GLOBAL_ALL);

		// get a curl handle
		pCurl = curl_easy_init();
		if (NULL != pCurl)
		{
			std::string tokenHeader = "";
			tokenHeader += "X-Auth-Token:";
			tokenHeader += m_token;

			struct MemoryStruct resp_body;
			resp_body.memory = (char*)malloc(1);
			resp_body.size = 0;

			// Set the out of time is 1s
			curl_easy_setopt(pCurl, CURLOPT_TIMEOUT, 20);

			// First set the URL that is about to receive our POST. 
			// This URL can just as well be a 
			// https:// URL if that is what should receive the data.
			curl_easy_setopt(pCurl, CURLOPT_URL, url.c_str());

			// Set the send request content type is JSON
			struct curl_slist *plist = NULL;
			plist = curl_slist_append(plist, "Content-Type:application/json");
			plist = curl_slist_append(plist, tokenHeader.c_str());
			curl_easy_setopt(pCurl, CURLOPT_HTTPHEADER, plist);

			curl_easy_setopt(pCurl, CURLOPT_POST, true);

			// Put the JSON serialization into string
			std::string post_data = jsonOption.toStyledString();

			// Set the JSON data for POST
			curl_easy_setopt(pCurl, CURLOPT_POSTFIELDS, post_data.c_str());

			// callback function, to write data
			curl_easy_setopt(pCurl, CURLOPT_WRITEFUNCTION, WriteMemoryCallback);
			curl_easy_setopt(pCurl, CURLOPT_WRITEDATA, (void*)& resp_body);

			// Perform the request, res will get the return code 
			res = curl_easy_perform(pCurl);

			long status;
			curl_easy_getinfo(pCurl, CURLINFO_HTTP_CODE, &status);
			// Check for errors
			if (res != CURLE_OK)
			{
				printf("curl_easy_perform() failed:%s\n", curl_easy_strerror(res));
				ostrFormate << "{\"status\":" << status << ",\"message\":\"ocr request failed\"}";
				strRespData = ostrFormate.str();
			}
			else {
				ostrFormate << "{\"status\":" << status << ",\"response body\":" << resp_body.memory << "}";
				strRespData = ostrFormate.str();
			}
			// always cleanup
			curl_easy_cleanup(pCurl);
		}
		curl_global_cleanup();
	}
	catch (std::exception &ex)
	{
		printf("curl exception %s.\n", ex.what());
	}
	return utf8_to_string(strRespData);
}
