// mUtil.cpp
// `````````

#include "mUtil.h"

#include <cstdarg>
#include <time.h>
#include <Windows.h> // Sleep()

using namespace std;

// Parse an ansi (single-char-width) ini into concentric map/string structure
ini_t ini_parse(TSTRING filepath) {
	ini_t ret;
	string sbuf;
	file_get_contents(filepath, sbuf);

	long fpo = 0, fpn = 0;
	std::string sCurSection = "";
	do {
		fpn = sbuf.find("\n", fpo+1);
		if (fpn != sbuf.npos) fpn++;
		std::string sLine = sbuf.substr(fpo, fpn-fpo);

		// Strip \r
		long rp; while ((rp = sLine.find("\r")) != sLine.npos) sLine.erase(rp, rp+1);
		
		// Process each line
		if (sLine.length() > 1 && sLine.at(0) != '#') {
			if (sLine.at(0) == '[') {
				// Section, find ]
				long ebp = sLine.find("]", 1);
				if (ebp != sLine.npos) {
					sCurSection = sLine.substr(1, ebp-1);
					ret[sCurSection];
				}

			} else {
				// Key/value, find =
				long eqp = sLine.find("=");
				if (eqp != sLine.npos) {
					ret[sCurSection][sLine.substr(0, eqp)] = sLine.substr(eqp+1);
				}
			}			
		}

		fpo = fpn;
	} while (fpo != sbuf.npos);

	return ret;
}

// Read entire file into std::string
bool file_get_contents(FILE* f, std::string& result) {
	long flen;

	if (!f) return false;
		fseek(f, 0, SEEK_END);
		flen = ftell(f);
		fseek(f, 0, SEEK_SET);

		char* cBuff = new char[flen+1];
		memset(cBuff, 0, flen+1);
		fread(cBuff, 1, flen, f);
	fclose(f);
	result.assign(cBuff);
	delete[] cBuff;
	return true;
}

// Read entire file into std::string
bool file_get_contents(std::wstring filepath, std::string& result) {	
	FILE* f = 0;	
	_wfopen_s(&f, filepath.c_str(), L"rb");
	return file_get_contents(f, result);
}

// Read entire file into std::string
bool file_get_contents(std::string filepath, std::string& result) {	
	FILE* f = 0;
	fopen_s(&f, filepath.c_str(), "rb");
	return file_get_contents(f, result);
}

// Explodes a string into a vector
void explode(vector<string> &res, string &haystack, const char* needle) {
	res.clear();
	size_t i = 0, last = 0, needle_len = strlen(needle);
	for(;;) {
		i = haystack.find(needle, last);
		if (i == haystack.npos) {
			res.push_back(haystack.substr(last));
			break;
		}
		res.push_back(haystack.substr(last, i-last));
		last = i+needle_len;
	}
}

// Printf-style TSTRING formatting
string format_arg_list(const char *fmt, va_list args) {
    if (!fmt) return "";
    int   result = -1, length = 256;
    char *buffer = 0;

	while (result == -1) {
        if (buffer) delete [] buffer;
        buffer = new char [length + 1];
        memset(buffer, 0, length + 1);
		//result = vsprintf_s(buffer, length, fmt, args);
		result = vsnprintf(buffer, length, fmt, args);
        length *= 2;
    }

    string s(buffer);
    delete [] buffer;
    return s;
}

// Printf-style TSTRING formatting
string format(const char *fmt, ...) {
    va_list args;
    va_start(args, fmt);
    string s = format_arg_list(fmt, args);
    va_end(args);
    return s;
}

// Performs a find/replace operation over str.
void replace(string& str, const char* find, const char* replace) {
	size_t pos = 0;
	int find_len = strlen(find), replace_len = strlen(replace);
	while ( (pos = str.find(find, pos)) != string::npos) {
		str.replace(pos, find_len, replace);
		pos += replace_len;
	}
}

// Returns a formatted timestamp string
string timestamp() {
	time_t t = time(0);
	struct tm *tm_struct = localtime(&t);
	return format("[%02d:%02d]", tm_struct->tm_hour, tm_struct->tm_min);
}

// OS-specific: Delays infinitesimally to lower CPU usage.
void releaseTimeslice() {
	Sleep(1);
}

// OS-specific: Returns a number of milliseconds.
long getUpTicks() {
	return GetTickCount();
}