#include <fstream>
#include <iostream>
#include <vector>
#include <iterator>
#include <curl/curl.h>
#include "json.h"
#include "signer.h"
#include "base64.h"
#include "HWOcrClientAKSK.h"

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;
}

HWOcrClientAKSK::HWOcrClientAKSK()
	: m_strAK("")
	, m_strSK("")
	, m_strUri("")
	, m_region("")
{
}


HWOcrClientAKSK::~HWOcrClientAKSK()
{
}


//@brief:init function
void HWOcrClientAKSK::init(std::string strAK, std::string strSK, std::string strUri, std::string region)
{
	m_strAK = strAK;
	m_strSK = strSK;
	m_strUri = strUri;
	m_region = region;
}

//@brief:request function, it has two request type, this function used the encode of Base64
std::string HWOcrClientAKSK::request_ocr_service_base64(std::string strReqUri, std::string imgPath)
{
	std::string strRespData = "";
	std::ostringstream ostrFormate;

	// encapsulates the string as JSON formart
	//Json::Value req_data;
	Json::Value jsonReqImgBase64;

	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) {
		jsonReqImgBase64["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 strImgBase64 = base64.Encode(imdata.data(), imdata.size());

		jsonReqImgBase64["image"] = Json::Value(strImgBase64);
	}

	//Json serialization into a string
	std::string strReqImgBase64 = jsonReqImgBase64.toStyledString();

	std::string endpoint = "";
	endpoint = "ocr.";
	endpoint += m_region;
	endpoint += ".myhuaweicloud.com";
	std::cout << "endpoint: " << endpoint << std::endl;

	// URI:"/v2/{project_id}/ocr/id-card"
	RequestParams* request = new RequestParams("POST", endpoint, m_strUri, "", strReqImgBase64);
	request->addHeader("Content-Type", "application/json");

	// generate signature. generated Authorization header is inserted into params.headers
	Signer signer(m_strAK, m_strSK);
	signer.createSignature(request);

	// URL:https://ocr.cn-north-4.myhuaweicloud.com/v2/{project_id}/ocr/id-card
	std::string url = "https://" + request->getHost() + request->getUri();
	try
	{
		CURL *pCurl = NULL;
		CURLcode res;
		// In windows, this will init the winsock stuff
		curl_global_init(CURL_GLOBAL_ALL);

		// get the handle
		pCurl = curl_easy_init();
		if (NULL != pCurl)
		{
			// To save the return value from server
			struct MemoryStruct resp_header;
			resp_header.memory = (char*)malloc(1);
			resp_header.size = 0;
			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, 15);

			// Set the URL
			curl_easy_setopt(pCurl, CURLOPT_URL, url.c_str());

			// Set the send request content type is JSON
			struct curl_slist *plist = NULL;
			std::set<Header>::iterator it;
			for (auto header : *request->getHeaders()) {
				std::string headerEntry = header.getKey() + ": " + header.getValue();
				printf("%s\n", headerEntry.c_str());
				plist = curl_slist_append(plist, headerEntry.c_str());
			}

			curl_easy_setopt(pCurl, CURLOPT_HTTPHEADER, plist);
			curl_easy_setopt(pCurl, CURLOPT_POST, true);

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

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

			// Perform the request, return the state info, error info linking:https://support.huaweicloud.com/ugcall-apig/apig-zh-ug-180530090.html
			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();
			}
			// release malloc
			free(resp_header.memory);
			free(resp_body.memory);

			// always cleanup
			curl_easy_cleanup(pCurl);
		}
	}
	catch (std::exception &ex)
	{
		printf("curl exception %s.\n", ex.what());
	}

	curl_global_cleanup();
	return utf8_to_string(strRespData);
}
