#pragma once
#include <Windows.h>
#include <vector>
#include "zlib\zlib.h"
#include <WinInet.h>


#include <sstream>
#include <string>
#include <urlmon.h>
#pragma comment(lib, "urlmon.lib")
#pragma comment(lib, "Wininet.lib") 

using namespace std;
#ifndef _IMAGE_DEBUG_DIRECTORY_RAW_
#define _IMAGE_DEBUG_DIRECTORY_RAW_
typedef struct _IMAGE_DEBUG_DIRECTORY_RAW {
	uint8_t format[4];
	uint8_t PdbSignature[16];
	uint32_t PdbDbiAge;
	uint8_t ImageName[256];
	const char* GuidString()
	{
		char* buf = new char[128];
		sprintf(buf, "%.2X%.2X%.2X%.2X%.2X%.2X%.2X%.2X%.2X%.2X%.2X%.2X%.2X%.2X%.2X%.2X",
			this->PdbSignature[3], this->PdbSignature[2],
			this->PdbSignature[1], this->PdbSignature[0],
			this->PdbSignature[5], this->PdbSignature[4],
			this->PdbSignature[7], this->PdbSignature[6],
			this->PdbSignature[8], this->PdbSignature[9],
			this->PdbSignature[10], this->PdbSignature[11],
			this->PdbSignature[12], this->PdbSignature[13],
			this->PdbSignature[14], this->PdbSignature[15]
		);
		return buf;
	}
} IMAGE_DEBUG_DIRECTORY_RAW, * PIMAGE_DEBUG_DIRECTORY_RAW;
#endif
static vector<BYTE> Compress(BYTE* buffer, int len)
{
	vector<BYTE> result = vector<BYTE>();
	uLong destLen = compressBound(len);
	unsigned char* ostream = (unsigned char*)malloc(destLen);
	int res = compress(ostream, &destLen, (const unsigned char*)buffer, len);
	if (res == Z_BUF_ERROR) {
		return result;
	}
	if (res == Z_MEM_ERROR) {
		return result;
	}
	int ldx = result.size();
	result.resize(destLen);
	memcpy(&result.operator[](ldx), ostream, destLen);

	return result;
}
static vector<BYTE> Decompress(BYTE* buffer, int len, int maxlen)
{
	uLong destLen = maxlen;
	vector<BYTE> result = vector<BYTE>();
	BYTE* o2stream = new BYTE[maxlen];
	int des = uncompress(o2stream, &destLen, buffer, len);
	result.resize(destLen);
	memcpy(result.data(), o2stream, destLen);

	return result;
}
static vector<BYTE> GDecompress(vector<BYTE> compressedBytes) {
	vector<BYTE> uncompressedBytes = vector<BYTE>();
	if (compressedBytes.size() == 0)return uncompressedBytes;
	unsigned full_length = compressedBytes.size();
	unsigned half_length = compressedBytes.size() / 2;

	unsigned uncompLength = full_length;
	char* uncomp = (char*)calloc(sizeof(char), uncompLength);

	z_stream strm;
	strm.next_in = (Bytef*)compressedBytes.data();
	strm.avail_in = compressedBytes.size();
	strm.total_out = 0;
	strm.zalloc = Z_NULL;
	strm.zfree = Z_NULL;

	bool done = false;

	if (inflateInit2(&strm, (16 + MAX_WBITS)) != Z_OK) {
		free(uncomp);
		return uncompressedBytes;
	}

	while (!done) {
		if (strm.total_out >= uncompLength) {
			char* uncomp2 = (char*)calloc(sizeof(char), uncompLength + half_length);
			memcpy(uncomp2, uncomp, uncompLength);
			uncompLength += half_length;
			free(uncomp);
			uncomp = uncomp2;
		}

		strm.next_out = (Bytef*)(uncomp + strm.total_out);
		strm.avail_out = uncompLength - strm.total_out;
		int err = inflate(&strm, Z_SYNC_FLUSH);
		if (err == Z_STREAM_END) done = true;
		else if (err != Z_OK) {
			break;
		}
	}

	if (inflateEnd(&strm) != Z_OK) {
		free(uncomp);
		return uncompressedBytes;
	}

	for (size_t i = 0; i < strm.total_out; ++i) {
		uncompressedBytes.push_back(uncomp[i]);
	}
	free(uncomp);
	return uncompressedBytes;
}
static vector<BYTE> GCompress(vector<BYTE> input)
{
	vector<BYTE> res = vector<BYTE>();
	res.resize(input.size());
	z_stream zs;
	zs.zalloc = Z_NULL;
	zs.zfree = Z_NULL;
	zs.opaque = Z_NULL;
	zs.avail_in = (uInt)input.size();
	zs.next_in = (Bytef*)input.data();
	zs.avail_out = (uInt)input.size();
	zs.next_out = (Bytef*)res.data();
	deflateInit2(&zs, Z_DEFAULT_COMPRESSION, Z_DEFLATED, 15 | 16, 8, Z_DEFAULT_STRATEGY);
	deflate(&zs, Z_FINISH);
	deflateEnd(&zs);
	res.resize(zs.total_out);
	return res;
}
static vector<BYTE> GDecompress(BYTE* compressedBytes,ULONG len) {
	vector<BYTE> uncompressedBytes = vector<BYTE>();
	if (len == 0)return uncompressedBytes;
	unsigned full_length = len;
	unsigned half_length = len / 2;

	unsigned uncompLength = full_length;
	char* uncomp = (char*)calloc(sizeof(char), uncompLength);

	z_stream strm;
	strm.next_in = compressedBytes;
	strm.avail_in = len;
	strm.total_out = 0;
	strm.zalloc = Z_NULL;
	strm.zfree = Z_NULL;

	bool done = false;

	if (inflateInit2(&strm, (16 + MAX_WBITS)) != Z_OK) {
		free(uncomp);
		return uncompressedBytes;
	}

	while (!done) {
		if (strm.total_out >= uncompLength) {
			char* uncomp2 = (char*)calloc(sizeof(char), uncompLength + half_length);
			memcpy(uncomp2, uncomp, uncompLength);
			uncompLength += half_length;
			free(uncomp);
			uncomp = uncomp2;
		}

		strm.next_out = (Bytef*)(uncomp + strm.total_out);
		strm.avail_out = uncompLength - strm.total_out;
		int err = inflate(&strm, Z_SYNC_FLUSH);
		if (err == Z_STREAM_END) done = true;
		else if (err != Z_OK) {
			break;
		}
	}

	if (inflateEnd(&strm) != Z_OK) {
		free(uncomp);
		return uncompressedBytes;
	}

	for (size_t i = 0; i < strm.total_out; ++i) {
		uncompressedBytes.push_back(uncomp[i]);
	}
	free(uncomp);
	return uncompressedBytes;
}
static vector<BYTE> GCompress(BYTE* input,ULONG len)
{
	vector<BYTE> res = vector<BYTE>();
	res.resize(len);
	z_stream zs;
	zs.zalloc = Z_NULL;
	zs.zfree = Z_NULL;
	zs.opaque = Z_NULL;
	zs.avail_in = (uInt)len;
	zs.next_in = (Bytef*)input;
	zs.avail_out = (uInt)len;
	zs.next_out = (Bytef*)res.data();
	deflateInit2(&zs, Z_DEFAULT_COMPRESSION, Z_DEFLATED, 15 | 16, 8, Z_DEFAULT_STRATEGY);
	deflate(&zs, Z_FINISH);
	deflateEnd(&zs);
	res.resize(zs.total_out);
	return res;
}
static vector<BYTE> GCompress(initializer_list<BYTE> input)
{
	return GCompress((BYTE*)input.begin(), input.size());
}
static vector<BYTE> GCompress(initializer_list<BYTE>* input)
{
	return GCompress((BYTE*)input->begin(), input->size());
}
static char* ToHex(const char* data, size_t len)
{
	const char* key = "0123456789ABCDEF";
	char* result = new char[len * 2];
	for (int i = 0; i < len; i++)
	{
		result[i * 2] = key[(BYTE)(data[i] >> 4)];
		result[(i * 2) + 1] = key[(BYTE)((BYTE)(data[i] << 4) >> 4)];
	}
	return result;
}
static char* ToHex(const BYTE* data, size_t len)
{
	const char* key = "0123456789ABCDEF";
	char* result = new char[(len * 2) + 1];
	for (int i = 0; i < len; i++)
	{
		result[i * 2] = key[(BYTE)(data[i] >> 4)];
		result[(i * 2) + 1] = key[(BYTE)((BYTE)(data[i] << 4) >> 4)];
	}
	result[len * 2] = '\0';
	return result;
}
static vector<BYTE> ParseHex(const char* hex)
{
	vector<BYTE> result = vector<BYTE>();
	int len = strlen(hex);
	BYTE* buffer = new BYTE[len];
	bool ish = true;
	BYTE val = 0;
	for (int i = 0; i < len; i++)
	{
		bool isval = false;
		BYTE v = 0;
		if (hex[i] >= '0' && hex[i] <= '9')
		{
			v = (hex[i] - '0');
			isval = true;
		}
		else if (hex[i] >= 'A' && hex[i] <= 'F')
		{
			v = 10 + (hex[i] - 'A');
			isval = true;
		}
		else if (hex[i] >= 'a' && hex[i] <= 'f')
		{
			v = 10 + (hex[i] - 'a');
			isval = true;
		}
		if (isval)
		{
			if (ish)
			{
				val = (v << 4) | val;
			}
			else
			{
				val = v | val;
				result.push_back(val);
				val = 0;
			}
			ish = !ish;
		}
	}
	return result;
}
static initializer_list<BYTE>* ReadAllBytes(const char* filePath)
{
	HANDLE pFile;
	DWORD fileSize;
	char* buffer, * tmpBuf;
	DWORD dwBytesRead, dwBytesToRead, tmpLen;

	pFile = CreateFileA(filePath,
		GENERIC_READ,
		FILE_SHARE_READ,
		NULL,
		OPEN_EXISTING,
		FILE_ATTRIBUTE_NORMAL,
		NULL);
	if (pFile == INVALID_HANDLE_VALUE)
	{
		CloseHandle(pFile);
		return nullptr;
	}
	fileSize = GetFileSize(pFile, NULL);
	buffer = (char*)malloc(fileSize);
	if (!buffer)
	{
		return nullptr;
	}
	ZeroMemory(buffer, fileSize);
	dwBytesToRead = fileSize;
	dwBytesRead = 0;
	tmpBuf = buffer;
	do {
		if (!ReadFile(pFile, tmpBuf, dwBytesToRead, &dwBytesRead, NULL) || dwBytesRead == 0)
			break;
		dwBytesToRead -= dwBytesRead;
		tmpBuf += dwBytesRead;

	} while (dwBytesToRead > 0);
	CloseHandle(pFile);
	return new initializer_list<BYTE>((BYTE*)&buffer[0], (BYTE*)&buffer[fileSize]);
}
static BOOL WriteAllBytes(const char* filePath, char* buffer, int len)
{
	HANDLE pFile;
	char* tmpBuf;
	DWORD dwBytesWrite, dwBytesToWrite;

	pFile = CreateFileA(filePath,
		GENERIC_WRITE,
		0,
		NULL,
		CREATE_ALWAYS,
		FILE_ATTRIBUTE_NORMAL,
		NULL);

	if (pFile == INVALID_HANDLE_VALUE)
	{
		CloseHandle(pFile);
		return FALSE;
	}

	dwBytesToWrite = len;
	dwBytesWrite = 0;

	tmpBuf = buffer;

	do {

		WriteFile(pFile, tmpBuf, dwBytesToWrite, &dwBytesWrite, NULL);

		dwBytesToWrite -= dwBytesWrite;
		tmpBuf += dwBytesWrite;

	} while (dwBytesToWrite > 0);

	CloseHandle(pFile);

	return TRUE;
}
static BOOL WriteAllBytes(const char* filePath, BYTE* buffer, int len)
{
	return WriteAllBytes(filePath,(char*)buffer,len);
}
static BOOL WriteAllBytes(const char* filePath, vector<BYTE> buffer)
{
	return WriteAllBytes(filePath, (char*)buffer.data(), buffer.size());
}
static BOOL WriteAllBytes(const char* filePath, initializer_list<BYTE> buffer)
{
	return WriteAllBytes(filePath, (char*)buffer.begin(), buffer.size());
}
static BOOL WriteAllBytes(const char* filePath, initializer_list<BYTE>* buffer)
{
	return WriteAllBytes(filePath, (char*)buffer->begin(), buffer->size());
}
static BOOL WriteAllBytes(const char* filePath, initializer_list<char> buffer)
{
	return WriteAllBytes(filePath, (char*)buffer.begin(), buffer.size());
}
static BOOL WriteAllText(const char* filePath, const char* text)
{
	return WriteAllBytes(filePath, (char*)text, strlen(text));
}
static vector<BYTE> HttpRequest(const char* url)
{
#define MAXSIZE 4096
	vector<BYTE> result = vector<BYTE>();
	BYTE* val = new BYTE[MAXSIZE];
	ULONG len = 1;
	HINTERNET hSession = InternetOpenA(NULL, INTERNET_OPEN_TYPE_PRECONFIG, NULL, NULL, 0);
	if (hSession != NULL)
	{
		HINTERNET hHttp = InternetOpenUrlA(hSession, url, NULL, 0, INTERNET_FLAG_DONT_CACHE, 0);

		if (hHttp != NULL)
		{
			while (len > 0)
			{
				InternetReadFile(hHttp, val, MAXSIZE - 1, &len);
				if (len > 0)
				{
					int ldx = result.size();
					result.resize(result.size() + len);
					memcpy(&result.operator[](ldx), &val[0], len);
				}
			}
			InternetCloseHandle(hHttp);
			hHttp = NULL;
		}
		InternetCloseHandle(hSession);
		hSession = NULL;
	}
	return result;
}
static string GetPathFolder(const char* path)
{
	int len = strlen(path);
	char* res = new char[len];
	strcpy(res, path);
	for (int i = len - 1; i >= 0; i--)
	{
		if (res[i] == '\\')
		{
			res[i] = '\0';
			break;
		}
	}
	return res;
}
static string GetFileNameByPath(const char* path)
{
	int len = strlen(path);
	char* res = new char[len];
	strcpy(res, path);
	for (int i = len - 1; i >= 0; i--)
	{
		if (res[i] == '\\')
		{
			return &res[i+1];
		}
	}
	return res;
}
static string WebRequest(const char* url)
{
	IStream* stream;
	HRESULT result = URLOpenBlockingStreamA(0, url, &stream, 0, 0);
	if (result != 0)
	{
		return "";
	}
	char buffer[4096];
	unsigned long bytesRead;
	stringstream ss;
	stream->Read(buffer, 4096, &bytesRead);
	while (bytesRead > 0U)
	{
		ss.write(buffer, (long long)bytesRead);
		stream->Read(buffer, 4096, &bytesRead);
	}
	stream->Release();
	return ss.str();
}