#include "stdafx.h"
#include "util.h"

pjson null_json(new jsonitem);

std::string URLencode(const std::string &orgstr)
{
	std::stringstream iss(orgstr), oss;
	unsigned char c;
	while (!(iss >> c).eof()) {
		if (((c >= 'a') && (c <= 'z')) ||
			((c >= 'A') && (c <= 'Z')) ||
			((c >= '0') && (c <= '9')) ||
			(c == '-') || (c == '.') || (c == '_') || (c == '~'))
		{
			oss << (char)c;
			continue;
		}
		if (c == ' ') {
			oss << '+';
			continue;
		}
		char buf[8];
		sprintf_s(buf, "%%%02X", c);
		buf[4] = 0;
		oss << buf;
	}

	return oss.str();
}

std::string URLencode(const std::wstring &orgstr)
{
	std::stringstream oss;
	std::wstringstream iss(orgstr);
	wchar_t c;
	while (!(iss >> c).eof()) {
		unsigned char c1[2];
		int count;
		if (c > 0xff) {
			c1[1] = c & 0xff;
			c1[0] = c >> 8;
			count = 2;
		}
		else {
			c1[0] = c & 0xff;
			count = 1;
		}
		for (int i = 0; i < count; i++){
			unsigned char d = c1[i];
			if (((d >= 'a') && (d <= 'z')) ||
				((d >= 'A') && (d <= 'Z')) ||
				((d >= '0') && (d <= '9')) ||
				(d == '-') || (d == '.') || (d == '_') || (d == '~'))
			{
				oss << (char)d;
				continue;
			}
			if (d == ' ') {
				oss << '+';
				continue;
			}
			char buf[8];
			sprintf_s(buf, "%%%02X", d);
			buf[4] = 0;
			oss << buf;
		}
	}
	oss << (wchar_t)0;
	return oss.str();
}

std::string WideChartoUTF8(const std::wstring &str)
{
	std::string outstr;

	int iBufferSize = WideCharToMultiByte(CP_UTF8, 0, str.c_str(), (int)str.length(), (char *)NULL, 0, NULL, NULL);
	outstr.resize(iBufferSize);
	// wstring->UTF8
	WideCharToMultiByte(CP_UTF8, 0, str.c_str(), (int)str.length(), &outstr[0], (int)outstr.length(), NULL, NULL);
	return outstr;
}

std::wstring UTF8toWideChar(const std::string &str)
{
	std::wstring outstr;

	int iBufferSize = MultiByteToWideChar(CP_UTF8, 0, str.c_str(), (int)str.length(), (wchar_t *)NULL, 0);
	outstr.resize(iBufferSize);
	// UTF8->wstring
	MultiByteToWideChar(CP_UTF8, 0, str.c_str(), (int)str.length(), &outstr[0], (int)outstr.length());
	return outstr;
}

std::string CreateQueryURI(std::map<std::string, std::string> data, bool add)
{
	std::string qURL;
	for (auto it = data.begin(); it != data.end(); ++it){
		qURL += "&" + URLencode(it->first) + "=" + URLencode(it->second);
	}
	if ((qURL.length() > 0) && !add) {
		qURL[0] = '?';
	}

	return qURL;
}

std::string MakeCookie(std::multimap<std::string, std::string> setcookie)
{
	auto eqit = setcookie.equal_range("Set-Cookie");
	std::multimap<std::string, std::string> cookie;
	for (auto it = eqit.first; it != eqit.second; ++it){
		auto cookieset = it->second;
		do {
			auto np = cookieset.find('=');
			auto vp = cookieset.find(';');
			auto name = cookieset.substr(0, np);
			auto value = cookieset.substr(np + 1, vp - np - 1);
			cookie.insert(std::make_pair(name, value));
			if (vp == std::string::npos) break;
			cookieset = cookieset.substr(vp + 2);
		} while (true);
	}
	std::string cookiestr;
	for (auto it = cookie.begin(); it != cookie.end(); it++){
		if (it->first == "expires") continue;
		if (it->first == "Path") continue;
		if (it->first == "Domain") continue;
		if (it->first == "Max-Age") continue;
		cookiestr += "; " + it->first + "=" + it->second;
	}
	return cookiestr.substr(2);
}






pjson jsonitem::Create(std::string str)
{
	pjson item;
	auto sp = str.find_first_not_of(" \t\r\n");
	if (sp == std::string::npos) {
		str = "";
		item.reset(new jsonitem);
	}
	if (sp != 0) str = str.substr(sp);
	if (str.empty() || str == "null") {
		item.reset(new jsonitem);
	}
	if (str == "true" || str == "false") {
		item.reset(new jsonitem);
	}
	if (str[0] == '"') {
		item.reset((jsonitem *)new json_string);
	}
	switch (str[0]){
	case '0':
	case '1':
	case '2':
	case '3':
	case '4':
	case '5':
	case '6':
	case '7':
	case '8':
	case '9':
	case '-':
		item.reset((jsonitem *)new json_number);
	}
	if (str[0] == '[') {
		item.reset((jsonitem *)new json_array);
	}
	if (str[0] == '{') {
		item.reset((jsonitem *)new json_object);
	}
	if (!item){
		item.reset(new jsonitem);
	}
	item->parse(str);
	return item;
}

void jsonitem::parse(std::string str)
{
	json = str;
	if (str.empty() || str == "null") {
		type = jsonitem::null;
	}
	if (str == "true" || str == "false") {
		type = jsonitem::boolean;
	}
	return;
}

void json_string::parse(std::string str)
{
	json = str;
	type = jsonitem::string;
	std::wstringstream out(L"");
	if (str.find('\\') == std::string::npos){
		auto sp = str.find('"');
		auto ep = str.find('"', sp + 1);
		std::string UTF8str = str.substr(sp + 1, ep - sp - 1);
		value = UTF8toWideChar(UTF8str);
		return;
	}
	auto sp = str.find('"');
	char *p;
	for (p = &str[sp+1]; *p != '"'; p++){
		if (*p != '\\'){
			out << *p;
			continue;
		}
		p++;
		char c = 0;
		if (p == 0)	return ;
		switch (*p) {
		case '"':
		case '/':
		case '\\':
			c = *p;
			break;
		case 'b': c = '\b'; break;
		case 'f': c = '\f'; break;
		case 'n': c = '\n'; break;
		case 'r': c = '\r'; break;
		case 't': c = '\t'; break;
		}
		if (c != 0){
			out << (char)c;
			continue;
		}
		if (*(p++) != 'u')	return ;
		unsigned ec = 0;
		for (int i = 0; (i < 4) && (*p != 0); i++, p++){
			ec <<= 4;
			switch (*p){
			case '0':
			case '1':
			case '2':
			case '3':
			case '4':
			case '5':
			case '6':
			case '7':
			case '8':
			case '9':
				ec |= (*p - '0');
				break;
			case 'a':
			case 'b':
			case 'c':
			case 'd':
			case 'e':
			case 'f':
				ec |= (*p - 'a' + 10);
				break;
			case 'A':
			case 'B':
			case 'C':
			case 'D':
			case 'E':
			case 'F':
				ec |= (*p - 'A' + 10);
				break;
			default:
				return ;
			}
		}
		p--;
		out << (wchar_t)ec;
	}
	//out << std::ends;
	value = out.str();
	return ;
}

void json_number::parse(std::string str)
{
	json = str;
	type = jsonitem::number;
	std::stringstream out(str);
	out >> value;
	return ;
}

inline char *skip_ws(char *p, const char *end)
{
	for (; p < end; p++){
		switch (*p){
		case ' ':
		case '\t':
		case '\r':
		case '\a':
			continue;
		default:
			return p;
		}
	}
	return p;
}

inline char *skip_string(char *p, const char *end)
{
	if (*p != '"') return p;
	for (p++; (*p != '"') && (p < end); p++){
		if (*p == '\\'){
			if ((*(p + 1) == '"') || (*(p + 1) == '\\')){
				p++;
			}
		}
	}
	return p;
}

inline char *skip_array(char *p, const char *end);

inline char *skip_object(char *p, const char *end)
{
	if (*p != '{') return p;
	while (true) {
		// "name"
		p++;
		p = skip_ws(p, end);
		if (*p != '"') return p;
		p = skip_string(p, end);
		p++;
		// :
		p = skip_ws(p, end);
		if (*p != ':') return p;
		p++;
		// value
		p = skip_ws(p, end);
		switch (*p){
		case '"':
			p = skip_string(p, end);
			p++;
			break;
		case '{':
			p = skip_object(p, end);
			p++;
			break;
		case '[':
			p = skip_array(p, end);
			p++;
			break;
		default:
			for (; (*p != ',') && (*p != '}') && (p < end); p++);
		}
		p = skip_ws(p, end);
		if (*p == ',') continue;
		if (*p == '}') break;
		return p;
	}
	return p;
}

inline char *skip_array(char *p, const char *end)
{
	if (*p != '[') return p;
	while (true) {
		p = skip_ws(++p, end);
		switch (*p){
		case '"':
			p = skip_string(p, end);
			p++;
			break;
		case '{':
			p = skip_object(p, end);
			p++;
			break;
		case '[':
			p = skip_array(p, end);
			p++;
			break;
		default:
			for (; (*p != ',') && (*p != ']') && (p < end); p++);
		}
		p = skip_ws(p, end);
		if (*p == ',') continue;
		if (*p == ']') break;
		return p;
	}
	return p;
}

void json_object::parse(std::string str)
{
	type = jsonitem::structure;
	if (str.empty() || str[0] != '{') return;
	char *p = &str[1];
	char *end = &str[str.length()];
	while (p < end) {
		// member
		//  name
		p = skip_ws(p, end);
		if (*(p++) != '"') return;
		char *np;
		for (np = p; (*np != '"') && (np < end); np++);
		std::string name = str.substr(p - &str[0], np - p);
		p = np + 1;
		// :
		p = skip_ws(p, end);
		if (*(p++) != ':') return;
		// value
		p = skip_ws(p + 1, end);
		switch (*p){
		case '"':
			np = skip_string(p, end);
			break;
		case '{':
			np = skip_object(p, end);
			break;
		case '[':
			np = skip_array(p, end);
			break;
		default:
			for (np = p; (*np != ',') && (*np != '}') && (np < end); np++);
			np--;
		}
		pjson value(jsonitem::Create(str.substr(p - &str[0], np - p + 1)));
		p = np + 1;
		p = skip_ws(p, end);
		members[name] = value;
		if (*p == '}') break;
		if (*p == ',') {
			p++;
			continue;
		}
		return;
	}
}


void json_array::parse(std::string str)
{
	type = jsonitem::structure;
	if (str.empty() || str[0] != '[') return;
	char *p = &str[1];
	char *end = &str[str.length()];
	if (*p == ']') return;
	while (p < end) {
		// value
		p = skip_ws(p, end);
		char *np;
		switch (*p){
		case '"':
			np = skip_string(p, end);
			break;
		case '{':
			np = skip_object(p, end);
			break;
		case '[':
			np = skip_array(p, end);
			break;
		default:
			for (np = p; (*np != ',') && (*np != '}') && (np < end); np++);
			np--;
		}
		pjson value(jsonitem::Create(str.substr(p - &str[0], np - p + 1)));
		p = np + 1;
		p = skip_ws(p, end);
		array.push_back(value);
		if (*p == ']') break;
		if (*p == ',') {
			p++;
			continue;
		}
		return;
	}

}


std::vector<std::wstring> GetLocalDirFiles(std::wstring path)
{
	std::vector<std::wstring> result;
	WIN32_FIND_DATAW fData;
	HANDLE h = FindFirstFileExW(
		((path.find(L":\\") == 1) ? (L"\\\\?\\" + path + L"*") : (path + L"*")).c_str(),
		FindExInfoBasic,
		&fData,
		FindExSearchNameMatch,
		NULL,
		0);
	if (h != INVALID_HANDLE_VALUE){
		do {
			std::wstring filename(fData.cFileName);
			if ((filename == L".") || (filename == L".."))
				continue;
			result.push_back(filename);
		} while (FindNextFileW(h,&fData));
		FindClose(h);
	}
	return result;
}
