#include "pch.h"
#include "twitchsdk/core/stringutilities.h"
#include "Internal.h"

using namespace TwitchInGames;

#ifdef _DEBUG
void TwitchInGames::DebugWriteLine_(string_t format, ...) {
	va_list args;
	va_start(args, format);
# ifdef _WIN32
	auto const len= _vscwprintf(format, args);
	std::wstring buffer;
	buffer.resize(len + 1);
	vswprintf_s(&buffer[0], buffer.size(), format, args);
	buffer.back()= L'\n';
	OutputDebugStringW(buffer.c_str());
# else
	auto const len= vsnprintf(nullptr, 0, format, args);
	std::string buffer;
	buffer.resize(len + 1);
	vsprintf_s(&buffer[0], buffer.size(), format, args);
	buffer.back()= '\n';
	std::cout << buffer;
# endif
	va_end(args);
}
#endif

tstring TwitchInGames::ExtractToken(tstring const& text, string_t name/*= _T("token")*/) {
	// Extract the GET request line, if any.
	auto token= text;
	auto i= token.find(_T('\r'));
	if(i != tstring::npos) {
		token.erase(i);

		// Extract the URL.
		i= token.find(_T(' '));
		if(i != tstring::npos) {
			token.erase(0, i + 1);
			i= token.find(_T(' '));
			if(i != tstring::npos) {
				token.erase(i);
			}
		}
	}

	// Extract the token.
	tstring s= name;
	s += _T('=');
	i= token.find(s);
	if(i == tstring::npos) {
		return _T("");
	}
	if(i > 0 && _tcschr(_T("#&?"), token[i - 1]) == nullptr) {
		return _T("");
	}
	token.erase(0, i + s.size());
	i= token.find(_T('&'));
	if(i != tstring::npos) {
		token.erase(i);
	}

	return token;
}

static char const base64Alphabet[]= "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";

tstring TwitchInGames::ToLowerCase(tstring const& s) {
	return ToTstring(ttv::ToLowerCase(FromTstring(s)));
}

std::string TwitchInGames::Base64Encode(std::vector<char> const& data) {
	std::string rv;
	for(std::vector<char>::size_type i= 0, n= data.size() - data.size() % 3; i < n; i += 3) {
		unsigned index= static_cast<unsigned char>(data[i]) >> 2;
		rv += base64Alphabet[index];
		index= (data[i] << 4) | (static_cast<unsigned char>(data[i + 1]) >> 4);
		rv += base64Alphabet[index & 0x3f];
		index= (data[i + 1] << 2) | (static_cast<unsigned char>(data[i + 2]) >> 6);
		rv += base64Alphabet[index & 0x3f];
		index= data[i + 2];
		rv += base64Alphabet[index & 0x3f];
	}
	switch(data.size() % 3) {
		unsigned index;
	case 1:
		index= static_cast<unsigned char>(data.back()) >> 2;
		rv += base64Alphabet[index];
		index= data.back() << 4;
		rv += base64Alphabet[index & 0x3f];
		rv += "==";
		break;
	case 2:
		index= static_cast<unsigned char>(data[data.size() - 2]) >> 2;
		rv += base64Alphabet[index];
		index= (data[data.size() - 2]) << 4 | (static_cast<unsigned char>(data.back()) >> 4);
		rv += base64Alphabet[index & 0x3f];
		index= static_cast<unsigned char>(data.back()) << 2;
		rv += base64Alphabet[index & 0x3f];
		rv += '=';
		break;
	}
	return rv;
}

std::time_t TwitchInGames::ParseTime(std::string const& s) {
	std::stringstream ss(s);
	std::tm when{};
	auto const* const format = "%Y-%m-%dT%H:%M:%SZ";
	ss >> std::get_time(&when, format);
	return std::mktime(&when);
}

#ifdef _WIN32
std::string TwitchInGames::FromTstring(tstring const& s) {
	std::wstring_convert<std::codecvt_utf8_utf16<wchar_t>> converter;
	return converter.to_bytes(s);
}
tstring TwitchInGames::ToTstring(std::string const& s) {
	std::wstring_convert<std::codecvt_utf8_utf16<wchar_t>> converter;
	return converter.from_bytes(s);
}
#endif
